Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d764b581d | |||
| 57a37fcc20 | |||
| 7f6ba5534f | |||
| b24bfae585 | |||
| d3e123b0a6 | |||
| bf1acf7b21 | |||
| fdaf4d5e27 | |||
| 1c47abc462 | |||
| 5222703444 | |||
| 4b38b44c91 | |||
| aa28e48e17 | |||
| f31586db41 | |||
| 8337b9b2d4 | |||
| 85a53ea9cd | |||
| 2d87ef8f23 | |||
| 3df8b637f3 | |||
| 28d3126fe5 | |||
| ad21f8feaf | |||
| 7f2df141cd | |||
| 4e735e5363 | |||
| a9db6073c9 | |||
| e48e27aa2b | |||
| 832b383cbc | |||
| c4bff290e5 | |||
| 55b00148dd | |||
| 23550b5e27 | |||
| 97d2a08c5a | |||
| 7f6efb2822 | |||
| b50867001b | |||
| 8b9ce885d3 | |||
| 56dae6fa1b | |||
| 611dcbc035 | |||
| 2c9066e012 | |||
| 8ecc9357ef | |||
| 71dca7c4c2 | |||
| ce77c25b06 | |||
| a1188721e1 | |||
| 1917ff86c4 | |||
| c60e1960c6 | |||
| 268e71eb8b | |||
| 03e6fb3df5 | |||
| 4fc9cf6289 | |||
| 8dee8f1b9e | |||
| 1eef631ab5 | |||
| 2ef92c329b | |||
| 1467e15bca | |||
| 389349edc3 | |||
| 722e97e8e6 | |||
| d3933a4181 | |||
| 8d02b07af4 | |||
| 24af9e2a6e | |||
| 4879e49c93 | |||
| aec25f1829 | |||
| f87e8288fb | |||
| 9b1beb8e09 | |||
| 681e6246e3 | |||
| 13e2ea73b0 | |||
| f5295ea448 | |||
| bb01b8bf89 | |||
| 711aba7727 | |||
| 88322c1af8 | |||
| 18f055eea5 | |||
| 24a7f28f1e | |||
| 146f9c1611 | |||
| e55829a1cd | |||
| 571e323f7d | |||
| bf11cf3095 | |||
| 5a1e148da6 | |||
| eec095a751 | |||
| 7b2c7cbab8 | |||
| b830f5b68e | |||
| 7e7a0693e5 | |||
| 02929f82f3 | |||
| b9d3625e92 | |||
| 863a4232a6 | |||
| 4a39ad475b | |||
| 696d65dcba | |||
| 947cb4d145 | |||
| 77fc41f499 | |||
| d98a12a6f2 | |||
| ef2c6e39be | |||
| c219a87ad7 | |||
| fad4dc07d7 | |||
| 373f054d5e | |||
| beb00e44de | |||
| 6a4403a118 | |||
| 77cdc37c65 | |||
| ab95ba65c0 | |||
| 86416bcbee | |||
| d04c38c489 | |||
| bdc5a1cde1 | |||
| eccd618d76 | |||
| 7f11af6b5e | |||
| 437ba49a52 | |||
| 9a576fa0fa | |||
| b5c317d672 | |||
| 05741ebf0b | |||
| 9e2c215779 | |||
| 8a1f600c29 | |||
| 8519b8a60f | |||
| 4bc3031497 | |||
| ab5c7698bb | |||
| 89690502d1 | |||
| f47e218006 | |||
| 8dc4c75ade | |||
| e91811095b | |||
| afb298b7ff | |||
| 571afd6558 | |||
| df6e731506 | |||
| af9c2d71e2 | |||
| dc158e7e40 | |||
| 0b7fff303f | |||
| 96d62cc0fc | |||
| 5cb7d0e046 | |||
| a60bbc12e8 | |||
| ca23d5f68f | |||
| a398773b0c | |||
| 1ef741563d | |||
| 2d44a681eb | |||
| 543af651d0 | |||
| e48666aeaf | |||
| 87f80379ea | |||
| 9cee054920 | |||
| e69f35507e | |||
| 379b8d9583 | |||
| bf79770706 | |||
| 7b0a865c97 | |||
| ce13cfd30a | |||
| c51fbcb7de | |||
| 796f7ab414 | |||
| 94d34beed4 | |||
| f476060de6 | |||
| 6a953bb0cb | |||
| 3092fd31bb | |||
| dae4a2e736 | |||
| 2632250a42 | |||
| fdbd92ff99 | |||
| 3b27dd37a2 | |||
| 76c6493c2b | |||
| da03497f6b | |||
| a1fd2239c9 | |||
| a39baef657 | |||
| 56508a1d5f | |||
| d7d8708a9b | |||
| 2ffbfb0ad0 | |||
| 946d9ae90b |
+1
-1
@@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.2'
|
||||
- '4.4'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
+252
@@ -1,3 +1,218 @@
|
||||
<a name="1.4.10"></a>
|
||||
# 1.4.10 benignant-oscillation (2016-03-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **core:** only call `console.log` when `window.console` exists
|
||||
([beb00e44](https://github.com/angular/angular.js/commit/beb00e44de947981dbe35d5cf7a116e10ea8dc67),
|
||||
[#14006](https://github.com/angular/angular.js/issues/14006), [#14007](https://github.com/angular/angular.js/issues/14007), [#14047](https://github.com/angular/angular.js/issues/14047))
|
||||
- **$animateCss:** cancel fallback timeout when animation ends normally
|
||||
([a60bbc12](https://github.com/angular/angular.js/commit/a60bbc12e8c5170e70d95f1b2c3e309b3b95cb84),
|
||||
[#13787](https://github.com/angular/angular.js/issues/13787))
|
||||
- **$compile:**
|
||||
- allow directives to have decorators
|
||||
([77cdc37c](https://github.com/angular/angular.js/commit/77cdc37c65491b551fcf01a18ab848a693c293d7))
|
||||
- properly denormalize templates when only one of the start/end symbols is different
|
||||
([2d44a681](https://github.com/angular/angular.js/commit/2d44a681eb912a81a8bc8e16a278c45dae91fa24),
|
||||
[#13848](https://github.com/angular/angular.js/issues/13848))
|
||||
- handle boolean attributes in `@` bindings
|
||||
([2ffbfb0a](https://github.com/angular/angular.js/commit/2ffbfb0ad0647d103ff339ee4b772b62d4823bf3),
|
||||
[#13767](https://github.com/angular/angular.js/issues/13767), [#13769](https://github.com/angular/angular.js/issues/13769))
|
||||
- **$parse:**
|
||||
- prevent assignment on constructor properties
|
||||
([f47e2180](https://github.com/angular/angular.js/commit/f47e218006029f39b4785d820b430de3a0eebcb0),
|
||||
[#13417](https://github.com/angular/angular.js/issues/13417))
|
||||
- preserve expensive checks when runnning `$eval` inside an expression
|
||||
([96d62cc0](https://github.com/angular/angular.js/commit/96d62cc0fc77248d7e3ec4aa458bac0d3e072629))
|
||||
- copy `inputs` for expressions with expensive checks
|
||||
([0b7fff30](https://github.com/angular/angular.js/commit/0b7fff303f46202bbae1ff3ca9d0e5fa76e0fc9a))
|
||||
- **$rootScope:** set no context when calling helper functions for `$watch`
|
||||
([ab5c7698](https://github.com/angular/angular.js/commit/ab5c7698bb106669ca31b5f79a95afa54d65c5f1))
|
||||
- **$route:** allow preventing a route reload
|
||||
([4bc30314](https://github.com/angular/angular.js/commit/4bc3031497447ad527356f12bd0ceee1d7d09db5),
|
||||
[#9824](https://github.com/angular/angular.js/issues/9824), [#13894](https://github.com/angular/angular.js/issues/13894))
|
||||
- **$routeProvider:** properly handle optional eager path named groups
|
||||
([6a4403a1](https://github.com/angular/angular.js/commit/6a4403a11845173d6a96232f77d73aa544b182af),
|
||||
[#14011](https://github.com/angular/angular.js/issues/14011))
|
||||
- **copy:** add support for copying `Blob` objects
|
||||
([863a4232](https://github.com/angular/angular.js/commit/863a4232a6faa92428df45cd54d5a519be2434de),
|
||||
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
|
||||
- **dateFilter:** follow the CLDR on pattern escape sequences
|
||||
([f476060d](https://github.com/angular/angular.js/commit/f476060de6cc016380c0343490a184543f853652),
|
||||
[#12839](https://github.com/angular/angular.js/issues/12839))
|
||||
- **dateFilter, input:** fix Date parsing in IE/Edge when timezone offset contains `:`
|
||||
([571afd65](https://github.com/angular/angular.js/commit/571afd6558786d7b99e2aebd307b4a94c9f2bb87),
|
||||
[#13880](https://github.com/angular/angular.js/issues/13880), [#13887](https://github.com/angular/angular.js/issues/13887))
|
||||
- **input:** re-validate when partially editing date-family inputs
|
||||
([02929f82](https://github.com/angular/angular.js/commit/02929f82f30449301ff18fea84a6396a017683b1),
|
||||
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
|
||||
- **select:** handle corner case of adding options via a custom directive
|
||||
([df6e7315](https://github.com/angular/angular.js/commit/df6e731506831a3dc7f44c9a90abe17515450b3e),
|
||||
[#13874](https://github.com/angular/angular.js/issues/13874), [#13878](https://github.com/angular/angular.js/issues/13878))
|
||||
- **ngOptions:** always set the 'selected' attribute for selected options
|
||||
([f87e8288](https://github.com/angular/angular.js/commit/f87e8288fb69526fd240a66a046f5de52ed204de),
|
||||
[#14115](https://github.com/angular/angular.js/issues/14115))
|
||||
- **ngAnimate:** properly cancel previously running class-based animations
|
||||
([3b27dd37](https://github.com/angular/angular.js/commit/3b27dd37a2cc8a52992784ece6b371023dadf792),
|
||||
[#10156](https://github.com/angular/angular.js/issues/10156), [#13822](https://github.com/angular/angular.js/issues/13822))
|
||||
- **ngAnimateChildren:** make it compatible with `ngIf`
|
||||
([dc158e7e](https://github.com/angular/angular.js/commit/dc158e7e40624ef94c66560386522ef7e991a9ce),
|
||||
[#13865](https://github.com/angular/angular.js/issues/13865), [#13876](https://github.com/angular/angular.js/issues/13876))
|
||||
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
|
||||
([947cb4d1](https://github.com/angular/angular.js/commit/947cb4d1451afa4f5090a693df5b1968dd0df70c),
|
||||
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$locale:** Include original locale ID in $locale
|
||||
([e69f3550](https://github.com/angular/angular.js/commit/e69f35507e10c994708ce4f1efba7573951d1acd),
|
||||
[#13390](https://github.com/angular/angular.js/issues/13390))
|
||||
- **ngAnimate:** provide ng-[event]-prepare class for structural animations
|
||||
([796f7ab4](https://github.com/angular/angular.js/commit/796f7ab41487e124b5b0c02dbf0a03bd581bf073))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** avoid needless overhead when wrapping text nodes
|
||||
([946d9ae9](https://github.com/angular/angular.js/commit/946d9ae90bb31fe911ebbe1b80cd4c8af5a665c6))
|
||||
- **ngRepeat:** avoid duplicate jqLite wrappers
|
||||
([d04c38c4](https://github.com/angular/angular.js/commit/d04c38c48968db777c3ea6a177ce2ff0116df7b4))
|
||||
- **ngAnimate:**
|
||||
- avoid jqLite/jQuery for upward DOM traversal
|
||||
([ab95ba65](https://github.com/angular/angular.js/commit/ab95ba65c08b38cace83de6717b7681079182b45))
|
||||
- avoid `$.fn.data` overhead with jQuery
|
||||
([86416bcb](https://github.com/angular/angular.js/commit/86416bcbee2192fa31c017163c5d856763182ade))
|
||||
|
||||
|
||||
<a name="1.4.9"></a>
|
||||
# 1.4.9 implicit-superannuation (2016-01-21)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Animation**
|
||||
- ensure that animate promises resolve when the document is hidden
|
||||
([9a60408c](https://github.com/angular/angular.js/commit/9a60408c804a62a9517857bdb9a42182ab6769e3))
|
||||
- do not trigger animations if the document is hidden
|
||||
([09f6061a](https://github.com/angular/angular.js/commit/09f6061a8ee41cae4268e8d44d727d3bf52e22a9),
|
||||
[#12842](https://github.com/angular/angular.js/issues/12842), [#13776](https://github.com/angular/angular.js/issues/13776))
|
||||
- only copy over the animation options once
|
||||
([2fc954d3](https://github.com/angular/angular.js/commit/2fc954d33a3a4c5d4f355be1e15a381664e02f1b),
|
||||
[#13722](https://github.com/angular/angular.js/issues/13722), [#13578](https://github.com/angular/angular.js/issues/13578))
|
||||
- allow event listeners on document in IE
|
||||
([5ba4419e](https://github.com/angular/angular.js/commit/5ba4419e265ff34c6c23bf3533a3332c99c5f014),
|
||||
[#13548](https://github.com/angular/angular.js/issues/13548), [#13696](https://github.com/angular/angular.js/issues/13696))
|
||||
- allow removing classes that are added by a running animation
|
||||
([6c4581fc](https://github.com/angular/angular.js/commit/6c4581fcb692b17295a41b8918c6038333e7bc3d),
|
||||
[#13339](https://github.com/angular/angular.js/issues/13339), [#13380](https://github.com/angular/angular.js/issues/13380), [#13414](https://github.com/angular/angular.js/issues/13414), [#13472](https://github.com/angular/angular.js/issues/13472), [#13678](https://github.com/angular/angular.js/issues/13678))
|
||||
- do not use `event.timeStamp` anymore for time tracking
|
||||
([620a20d1](https://github.com/angular/angular.js/commit/620a20d1b3376d95f85004ffa494e36bb19a2e4d),
|
||||
[#13494](https://github.com/angular/angular.js/issues/13494), [#13495](https://github.com/angular/angular.js/issues/13495))
|
||||
- ignore children without animation data when closing them
|
||||
([be01cebf](https://github.com/angular/angular.js/commit/be01cebfae9ca2383105e535820442b39a96b240),
|
||||
[#11992](https://github.com/angular/angular.js/issues/11992), [#13424](https://github.com/angular/angular.js/issues/13424))
|
||||
- do not alter the provided options data
|
||||
([7a81e6fe](https://github.com/angular/angular.js/commit/7a81e6fe2db084172e34d509f0baad2b33a8722c),
|
||||
[#13040](https://github.com/angular/angular.js/issues/13040), [#13175](https://github.com/angular/angular.js/issues/13175))
|
||||
- correctly handle `$animate.pin()` host elements
|
||||
([a985adfd](https://github.com/angular/angular.js/commit/a985adfdabd871f3f3f3ee59f371da50cd9611d9),
|
||||
[#13783](https://github.com/angular/angular.js/issues/13783))
|
||||
- allow animations when pinned element is parent element
|
||||
([4cb8ac61](https://github.com/angular/angular.js/commit/4cb8ac61c7574ab4039852c358dd5946268b69fb),
|
||||
[#13466](https://github.com/angular/angular.js/issues/13466))
|
||||
- allow enabled children to animate on disabled parents
|
||||
([6d85f24e](https://github.com/angular/angular.js/commit/6d85f24e2081d2a69c80697d90ebd45f228d9682),
|
||||
[#13179](https://github.com/angular/angular.js/issues/13179), [#13695](https://github.com/angular/angular.js/issues/13695))
|
||||
- correctly access `minErr`
|
||||
([0c1b54f0](https://github.com/angular/angular.js/commit/0c1b54f04cf5bd7c1fe42ac49b4fbfdf35c60979))
|
||||
- ensure animate runner is the same with and without animations
|
||||
([937942f5](https://github.com/angular/angular.js/commit/937942f5ada6de1bdacdf0ba465f6f118c270119),
|
||||
[#13205](https://github.com/angular/angular.js/issues/13205), [#13347](https://github.com/angular/angular.js/issues/13347))
|
||||
- remove animation end event listeners on close
|
||||
([d9157849](https://github.com/angular/angular.js/commit/d9157849df224a3a8d2e0bf03099d137f51499f6),
|
||||
[#13672](https://github.com/angular/angular.js/issues/13672))
|
||||
- consider options.delay value for closing timeout
|
||||
([592bf516](https://github.com/angular/angular.js/commit/592bf516e50b9729e446d9aa01f4d9ebdd72d187),
|
||||
[#13355](https://github.com/angular/angular.js/issues/13355), [#13363](https://github.com/angular/angular.js/issues/13363))
|
||||
- **$controller:** allow identifiers containing `$`
|
||||
([2563ff7b](https://github.com/angular/angular.js/commit/2563ff7ba92d84af978e7e4131253190d4d00c20),
|
||||
[#13736](https://github.com/angular/angular.js/issues/13736))
|
||||
- **$http:** throw if url passed is not a string
|
||||
([c5bf9dae](https://github.com/angular/angular.js/commit/c5bf9daef6dfdb3e4a2942c21155a9f67d92e237),
|
||||
[#12925](https://github.com/angular/angular.js/issues/12925), [#13444](https://github.com/angular/angular.js/issues/13444))
|
||||
- **$parse:** handle interceptors with `undefined` expressions
|
||||
([7bb2414b](https://github.com/angular/angular.js/commit/7bb2414bf6461aa45a983fd322ae875f81814cc4))
|
||||
- **$resource:** don't allow using promises as `timeout` and log a warning
|
||||
([47486524](https://github.com/angular/angular.js/commit/474865242c89ba3e8143f0cd52f8c292979ea730))
|
||||
- **formatNumber:** cope with large and small number corner cases
|
||||
([9c49eb13](https://github.com/angular/angular.js/commit/9c49eb131a6100d58c965d01fb08bcd319032229),
|
||||
[#13394](https://github.com/angular/angular.js/issues/13394), [#8674](https://github.com/angular/angular.js/issues/8674), [#12709](https://github.com/angular/angular.js/issues/12709), [#8705](https://github.com/angular/angular.js/issues/8705), [#12707](https://github.com/angular/angular.js/issues/12707), [#10246](https://github.com/angular/angular.js/issues/10246), [#10252](https://github.com/angular/angular.js/issues/10252))
|
||||
- **input:**
|
||||
- fix URL validation being too strict
|
||||
([6610ae81](https://github.com/angular/angular.js/commit/6610ae816f78ee8fc1080b93a55bf19e4ce48d3e),
|
||||
[#13528](https://github.com/angular/angular.js/issues/13528), [#13544](https://github.com/angular/angular.js/issues/13544))
|
||||
- add missing chars to URL validation regex
|
||||
([2995b54a](https://github.com/angular/angular.js/commit/2995b54afdb9a3a2a81b0076a6ac0a9001041163),
|
||||
[#13379](https://github.com/angular/angular.js/issues/13379), [#13460](https://github.com/angular/angular.js/issues/13460))
|
||||
- **isArrayLike:** recognize empty instances of an Array subclass
|
||||
([323f9ab7](https://github.com/angular/angular.js/commit/323f9ab73696f223c245ddefd62a769fe102615e),
|
||||
[#13560](https://github.com/angular/angular.js/issues/13560), [#13708](https://github.com/angular/angular.js/issues/13708))
|
||||
- **ngInclude:** do not compile template if original scope is destroyed
|
||||
([9590bcf0](https://github.com/angular/angular.js/commit/9590bcf0620cd507a7795c55f9a6f4a48bfedbc1))
|
||||
- **ngOptions:**
|
||||
- don't skip `optgroup` elements with `value === ''`
|
||||
([85e392f3](https://github.com/angular/angular.js/commit/85e392f3543ef5285c7e90e843af0ab522cb0531),
|
||||
[#13487](https://github.com/angular/angular.js/issues/13487), [#13489](https://github.com/angular/angular.js/issues/13489))
|
||||
- don't `$dirty` multiple select after compilation
|
||||
([f163c905](https://github.com/angular/angular.js/commit/f163c90555774426ccb14752d089fc707cb4029c),
|
||||
[#13211](https://github.com/angular/angular.js/issues/13211), [#13326](https://github.com/angular/angular.js/issues/13326))
|
||||
- **select:** re-define `ngModelCtrl.$render` in the `select` directive's postLink function
|
||||
([529b2507](https://github.com/angular/angular.js/commit/529b2507bdb4fcc22dfa0f7ab462c79fc78d1413),
|
||||
[#13583](https://github.com/angular/angular.js/issues/13583), [#13583](https://github.com/angular/angular.js/issues/13583), [#13663](https://github.com/angular/angular.js/issues/13663))
|
||||
|
||||
## Minor Features
|
||||
|
||||
- **ngLocale:** add support for standalone months
|
||||
([54c4041e](https://github.com/angular/angular.js/commit/54c4041ebc0cc4df70cf6996f43a6aaaf56d46bd),
|
||||
[#3744](https://github.com/angular/angular.js/issues/3744), [#10247](https://github.com/angular/angular.js/issues/10247), [#12642](https://github.com/angular/angular.js/issues/12642), [#12844](https://github.com/angular/angular.js/issues/12844))
|
||||
- **ngMock:** add support for `$animate.closeAndFlush()`
|
||||
([512c0811](https://github.com/angular/angular.js/commit/512c08118786a419fabbd063fa17d224aba125cf))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **ngAnimate:** speed up `areAnimationsAllowed` check
|
||||
([2d3303dd](https://github.com/angular/angular.js/commit/2d3303ddda6330c4f45b381b6b17346f6cfe2d97))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
While we do not deem the following to be a real breaking change we are highlighting it here in the
|
||||
changelog to ensure that it does not surprise anyone.
|
||||
|
||||
- **$resource:** due to [47486524](https://github.com/angular/angular.js/commit/474865242c89ba3e8143f0cd52f8c292979ea730),
|
||||
|
||||
**Possible breaking change** for users who updated their code to provide a `timeout`
|
||||
promise for a `$resource` request in version 1.4.8.
|
||||
|
||||
Up to v1.4.7 (included), using a promise as a timeout in `$resource`, would silently
|
||||
fail (i.e. have no effect).
|
||||
|
||||
In v1.4.8, using a promise as timeout would have the (buggy) behaviour described
|
||||
in https://github.com/angular/angular.js/pull/12657#issuecomment-152108887
|
||||
(i.e. it will work as expected for the first time you resolve the promise and will
|
||||
cancel all subsequent requests after that - one has to re-create the resource
|
||||
class. This is feature was not documented.)
|
||||
|
||||
With this change, using a promise as timeout in 1.4.9 onwsards is not allowed.
|
||||
It will log a warning and ignore the timeout value.
|
||||
|
||||
If you need support for cancellable `$resource` actions, you should upgrade to
|
||||
version 1.5 or higher.
|
||||
|
||||
|
||||
<a name="1.4.8"></a>
|
||||
# 1.4.8 ice-manipulation (2015-11-19)
|
||||
|
||||
@@ -604,6 +819,43 @@ describe('$q.when', function() {
|
||||
});
|
||||
```
|
||||
|
||||
- **form:** Due to [94533e57](https://github.com/angular/angular.js/commit/94533e570673e6b2eb92073955541fa289aabe02),
|
||||
the `name` attribute of `form` elements can now only contain characters that can be evaluated as part
|
||||
of an Angular expression. This is because Angular uses the value of `name` as an assignable expression
|
||||
to set the form on the `$scope`. For example, `name="myForm"` assigns the form to `$scope.myForm` and
|
||||
`name="myObj.myForm"` assigns it to `$scope.myObj.myForm`.
|
||||
|
||||
Previously, it was possible to also use names such `name="my:name"`, because Angular used a special setter
|
||||
function for the form name. Now the general, more robust `$parse` setter is used.
|
||||
|
||||
The easiest way to migrate your code is therefore to remove all special characters from the `name` attribute.
|
||||
|
||||
If you need to keep the special characters, you can use the following directive, which will replace
|
||||
the `name` with a value that can be evaluated as an expression in the compile function, and then
|
||||
re-set the original name in the postLink function. This ensures that (1), the form is published on
|
||||
the scope, and (2), the form has the original name, which might be important if you are doing server-side
|
||||
form submission.
|
||||
|
||||
```js
|
||||
angular.module('myApp').directive('form', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
priority: 1000,
|
||||
compile: function(element, attrs) {
|
||||
var unsupportedCharacter = ':'; // change accordingly
|
||||
var originalName = attrs.name;
|
||||
if (attrs.name && attrs.name.indexOf(unsupportedCharacter) > 0) {
|
||||
attrs.$set('name', 'this["' + originalName + '"]');
|
||||
}
|
||||
|
||||
return postLinkFunction(scope, element) {
|
||||
// Don't trigger $observers
|
||||
element.setAttribute('name', originalName);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
<a name="1.4.3"></a>
|
||||
# 1.4.3 foam-acceleration (2015-07-15)
|
||||
|
||||
@@ -18,7 +18,7 @@ piece of cake. Best of all?? It makes development fun!
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
|
||||
@@ -43,3 +43,26 @@ To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
What to Use AngularJS for and When to Use it
|
||||
---------
|
||||
AngularJS is the next generation framework where each component is designed to work with every other component in an interconnected way like a well-oiled machine. AngularJS is JavaScript MVC made easy and done right. (Well it is not really MVC, read on, to understand what this means.)
|
||||
|
||||
#### MVC, no, MV* done the right way!
|
||||
MVC, short for Model-View-Controller, is a design pattern, i.e. how the code should be organized and how the different parts of an application separated for proper readability and debugging. Model is the data and the database. View is the user interface and what the user sees. Controller is the main link between Model and View. These are the three pillars of major programming frameworks present on the market today. On the other hand AngularJS works on MV*, short for Model-View-_Whatever_. The _Whatever_ is AngularJS's way of telling that you may create any kind of linking between the Model and the View here.
|
||||
|
||||
Unlike other frameworks in any programming language, where MVC, the three separate components, each one has to be written and then connected by the programmer, AngularJS helps the programmer by asking him/her to just create these and everything else will be taken care of by AngularJS.
|
||||
|
||||
#### Interconnection with HTML at the root level
|
||||
AngularJS uses HTML to define the user's interface. AngularJS also enables the programmer to write new HTML tags (AngularJS Directives) and increase the readability and understandability of the HTML code. Directives are AngularJS’s way of bringing additional functionality to HTML. Directives achieve this by enabling us to invent our own HTML elements. This also helps in making the code DRY (Don't Repeat Yourself), which means once created, a new directive can be used anywhere within the application.
|
||||
|
||||
#### Data Handling made simple
|
||||
Data and Data Models in AngularJS are plain JavaScript objects and one can add and change properties directly on it and loop over objects and arrays at will.
|
||||
|
||||
#### Two-way Data Binding
|
||||
One of AngularJS's strongest features. Two-way Data Binding means that if something changes in the Model, the change gets reflected in the View instantaneously, and the same happens the other way around. This is also referred to as Reactive Programming, i.e. suppose `a = b + c` is being programmed and after this, if the value of `b` and/or `c` is changed then the value of `a` will be automatically updated to reflect the change. AngularJS uses its "scopes" as a glue between the Model and View and makes these updates in one available for the other.
|
||||
|
||||
#### Less Written Code and Easily Maintable Code
|
||||
Everything in AngularJS is created to enable the programmer ends up writing less code that is easily maintainable and readable by any other new person on the team. Believe it or not, one can write a complete working two-way data binded application in less than 10 lines of code. Try and see for yourself!
|
||||
|
||||
#### Testing Ready
|
||||
AngularJS has Dependency Injection, i.e. it takes care of providing all the necessary dependencies to its controllers whenever required. This helps in making the AngularJS code ready for unit testing by making use of mock dependencies created and injected. This makes AngularJS more modular and easily testable thus in turn helping a team create more robust applications.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
|
||||
-442
@@ -1,442 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
|
||||
directive.runnableExample = ['$templateCache', '$document', function($templateCache, $document) {
|
||||
var exampleClassNameSelector = '.runnable-example-file';
|
||||
var doc = $document[0];
|
||||
var tpl =
|
||||
'<nav class="runnable-example-tabs" ng-if="tabs">' +
|
||||
' <a ng-class="{active:$index==activeTabIndex}"' +
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
|
||||
return {
|
||||
restrict: 'C',
|
||||
scope : true,
|
||||
controller : ['$scope', function($scope) {
|
||||
$scope.setTab = function(index) {
|
||||
var tab = $scope.tabs[index];
|
||||
$scope.activeTabIndex = index;
|
||||
$scope.$broadcast('tabChange', index, tab);
|
||||
};
|
||||
}],
|
||||
compile : function(element) {
|
||||
element.html(tpl + element.html());
|
||||
return function(scope, element) {
|
||||
var node = element[0];
|
||||
var examples = node.querySelectorAll(exampleClassNameSelector);
|
||||
var tabs = [], now = Date.now();
|
||||
angular.forEach(examples, function(child, index) {
|
||||
tabs.push(child.getAttribute('name'));
|
||||
});
|
||||
|
||||
if(tabs.length > 0) {
|
||||
scope.tabs = tabs;
|
||||
scope.$on('tabChange', function(e, index, title) {
|
||||
angular.forEach(examples, function(child) {
|
||||
child.style.display = 'none';
|
||||
});
|
||||
var selected = examples[index];
|
||||
selected.style.display = 'block';
|
||||
});
|
||||
scope.setTab(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
directive.dropdownToggle =
|
||||
['$document', '$location', '$window',
|
||||
function ($document, $location, $window) {
|
||||
var openElement = null, close;
|
||||
return {
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs) {
|
||||
scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.parent().on('click', function(event) {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var iWasOpen = false;
|
||||
|
||||
if (openElement) {
|
||||
iWasOpen = openElement === element;
|
||||
close();
|
||||
}
|
||||
|
||||
if (!iWasOpen){
|
||||
element.parent().addClass('open');
|
||||
openElement = element;
|
||||
|
||||
close = function (event) {
|
||||
event && event.preventDefault();
|
||||
event && event.stopPropagation();
|
||||
$document.off('click', close);
|
||||
element.parent().removeClass('open');
|
||||
close = null;
|
||||
openElement = null;
|
||||
}
|
||||
|
||||
$document.on('click', close);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
directive.syntax = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
function makeLink(type, text, link, icon) {
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
'<span class="' + icon + '"></span> ' + text +
|
||||
'</a>';
|
||||
};
|
||||
|
||||
var html = '';
|
||||
var types = {
|
||||
'github' : {
|
||||
text : 'View on Github',
|
||||
key : 'syntaxGithub',
|
||||
icon : 'icon-github'
|
||||
},
|
||||
'plunkr' : {
|
||||
text : 'View on Plunkr',
|
||||
key : 'syntaxPlunkr',
|
||||
icon : 'icon-arrow-down'
|
||||
},
|
||||
'jsfiddle' : {
|
||||
text : 'View on JSFiddle',
|
||||
key : 'syntaxFiddle',
|
||||
icon : 'icon-cloud'
|
||||
}
|
||||
};
|
||||
for(var type in types) {
|
||||
var data = types[type];
|
||||
var link = attrs[data.key];
|
||||
if(link) {
|
||||
html += makeLink(type, data.text, link, data.icon);
|
||||
}
|
||||
};
|
||||
|
||||
var nav = document.createElement('nav');
|
||||
nav.className = 'syntax-links';
|
||||
nav.innerHTML = html;
|
||||
|
||||
var node = element[0];
|
||||
var par = node.parentNode;
|
||||
par.insertBefore(nav, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directive.tabbable = function() {
|
||||
return {
|
||||
restrict: 'C',
|
||||
compile: function(element) {
|
||||
var navTabs = angular.element('<ul class="nav nav-tabs"></ul>'),
|
||||
tabContent = angular.element('<div class="tab-content"></div>');
|
||||
|
||||
tabContent.append(element.contents());
|
||||
element.append(navTabs).append(tabContent);
|
||||
},
|
||||
controller: ['$scope', '$element', function($scope, $element) {
|
||||
var navTabs = $element.contents().eq(0),
|
||||
ngModel = $element.controller('ngModel') || {},
|
||||
tabs = [],
|
||||
selectedTab;
|
||||
|
||||
ngModel.$render = function() {
|
||||
var $viewValue = this.$viewValue;
|
||||
|
||||
if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) {
|
||||
if(selectedTab) {
|
||||
selectedTab.paneElement.removeClass('active');
|
||||
selectedTab.tabElement.removeClass('active');
|
||||
selectedTab = null;
|
||||
}
|
||||
if($viewValue) {
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++) {
|
||||
if ($viewValue == tabs[i].value) {
|
||||
selectedTab = tabs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedTab) {
|
||||
selectedTab.paneElement.addClass('active');
|
||||
selectedTab.tabElement.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.addPane = function(element, attr) {
|
||||
var li = angular.element('<li><a href></a></li>'),
|
||||
a = li.find('a'),
|
||||
tab = {
|
||||
paneElement: element,
|
||||
paneAttrs: attr,
|
||||
tabElement: li
|
||||
};
|
||||
|
||||
tabs.push(tab);
|
||||
|
||||
attr.$observe('value', update)();
|
||||
attr.$observe('title', function(){ update(); a.text(tab.title); })();
|
||||
|
||||
function update() {
|
||||
tab.title = attr.title;
|
||||
tab.value = attr.value || attr.title;
|
||||
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
}
|
||||
ngModel.$render();
|
||||
}
|
||||
|
||||
navTabs.append(li);
|
||||
li.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (ngModel.$setViewValue) {
|
||||
$scope.$apply(function() {
|
||||
ngModel.$setViewValue(tab.value);
|
||||
ngModel.$render();
|
||||
});
|
||||
} else {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
ngModel.$render();
|
||||
}
|
||||
});
|
||||
|
||||
return function() {
|
||||
tab.tabElement.remove();
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++ ) {
|
||||
if (tab == tabs[i]) {
|
||||
tabs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
directive.table = function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
if (!attrs['class']) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var popoverElement = function() {
|
||||
var object = {
|
||||
init : function() {
|
||||
this.element = angular.element(
|
||||
'<div class="popover popover-incode top">' +
|
||||
'<div class="arrow"></div>' +
|
||||
'<div class="popover-inner">' +
|
||||
'<div class="popover-title"><code></code></div>' +
|
||||
'<div class="popover-content"></div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
this.node = this.element[0];
|
||||
this.element.css({
|
||||
'display':'block',
|
||||
'position':'absolute'
|
||||
});
|
||||
angular.element(document.body).append(this.element);
|
||||
|
||||
var inner = this.element.children()[1];
|
||||
this.titleElement = angular.element(inner.childNodes[0].firstChild);
|
||||
this.contentElement = angular.element(inner.childNodes[1]);
|
||||
|
||||
//stop the click on the tooltip
|
||||
this.element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
var self = this;
|
||||
angular.element(document.body).on('click',function(event) {
|
||||
if(self.visible()) self.hide();
|
||||
});
|
||||
},
|
||||
|
||||
show : function(x,y) {
|
||||
this.element.addClass('visible');
|
||||
this.position(x || 0, y || 0);
|
||||
},
|
||||
|
||||
hide : function() {
|
||||
this.element.removeClass('visible');
|
||||
this.position(-9999,-9999);
|
||||
},
|
||||
|
||||
visible : function() {
|
||||
return this.position().y >= 0;
|
||||
},
|
||||
|
||||
isSituatedAt : function(element) {
|
||||
return this.besideElement ? element[0] == this.besideElement[0] : false;
|
||||
},
|
||||
|
||||
title : function(value) {
|
||||
return this.titleElement.html(value);
|
||||
},
|
||||
|
||||
content : function(value) {
|
||||
if(value && value.length > 0) {
|
||||
value = marked(value);
|
||||
}
|
||||
return this.contentElement.html(value);
|
||||
},
|
||||
|
||||
positionArrow : function(position) {
|
||||
this.node.className = 'popover ' + position;
|
||||
},
|
||||
|
||||
positionAway : function() {
|
||||
this.besideElement = null;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
positionBeside : function(element) {
|
||||
this.besideElement = element;
|
||||
|
||||
var elm = element[0];
|
||||
var x = elm.offsetLeft;
|
||||
var y = elm.offsetTop;
|
||||
x -= 30;
|
||||
y -= this.node.offsetHeight + 10;
|
||||
this.show(x,y);
|
||||
},
|
||||
|
||||
position : function(x,y) {
|
||||
if(x != null && y != null) {
|
||||
this.element.css('left',x + 'px');
|
||||
this.element.css('top', y + 'px');
|
||||
}
|
||||
else {
|
||||
return {
|
||||
x : this.node.offsetLeft,
|
||||
y : this.node.offsetTop
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
object.init();
|
||||
object.hide();
|
||||
|
||||
return object;
|
||||
};
|
||||
|
||||
directive.popover = ['popoverElement', function(popover) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
priority : 500,
|
||||
link: function(scope, element, attrs) {
|
||||
element.on('click',function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if(popover.isSituatedAt(element) && popover.visible()) {
|
||||
popover.title('');
|
||||
popover.content('');
|
||||
popover.positionAway();
|
||||
}
|
||||
else {
|
||||
popover.title(attrs.title);
|
||||
popover.content(attrs.content);
|
||||
popover.positionBeside(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
directive.tabPane = function() {
|
||||
return {
|
||||
require: '^tabbable',
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs, tabsCtrl) {
|
||||
element.on('$remove', tabsCtrl.addPane(element, attrs));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
directive.foldout = ['$http', '$animate','$window', function($http, $animate, $window) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
priority : 500,
|
||||
link: function(scope, element, attrs) {
|
||||
var container, loading, url = attrs.url;
|
||||
if(/\/build\//.test($window.location.href)) {
|
||||
url = '/build/docs' + url;
|
||||
}
|
||||
element.on('click',function() {
|
||||
scope.$apply(function() {
|
||||
if(!container) {
|
||||
if(loading) return;
|
||||
|
||||
loading = true;
|
||||
var par = element.parent();
|
||||
container = angular.element('<div class="foldout">loading...</div>');
|
||||
$animate.enter(container, null, par);
|
||||
|
||||
$http.get(url, { cache : true }).success(function(html) {
|
||||
loading = false;
|
||||
|
||||
html = '<div class="foldout-inner">' +
|
||||
'<div calss="foldout-arrow"></div>' +
|
||||
html +
|
||||
'</div>';
|
||||
container.html(html);
|
||||
|
||||
//avoid showing the element if the user has already closed it
|
||||
if(container.css('display') == 'block') {
|
||||
container.css('display','none');
|
||||
$animate.addClass(container, 'ng-hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
container.hasClass('ng-hide') ? $animate.removeClass(container, 'ng-hide') : $animate.addClass(container, 'ng-hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
angular.module('bootstrap', [])
|
||||
.directive(directive)
|
||||
.factory('popoverElement', popoverElement)
|
||||
.run(function() {
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
tables: true
|
||||
});
|
||||
});
|
||||
+3
-1
@@ -54,7 +54,9 @@ angular.module('ui.bootstrap.dropdown', [])
|
||||
}
|
||||
};
|
||||
|
||||
var closeDropdown = function() {
|
||||
var closeDropdown = function(evt) {
|
||||
if (evt && evt.which === 3) return;
|
||||
|
||||
openScope.$apply(function() {
|
||||
openScope.isOpen = false;
|
||||
});
|
||||
|
||||
@@ -13,7 +13,6 @@ angular.module('docsApp', [
|
||||
'search',
|
||||
'tutorials',
|
||||
'versions',
|
||||
'bootstrap',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
@@ -34,4 +34,15 @@ angular.module('directives', [])
|
||||
return function(scope, element) {
|
||||
$anchorScroll.yOffset = element;
|
||||
};
|
||||
}]);
|
||||
}])
|
||||
|
||||
.directive('table', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
if (!attrs['class']) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
+10
-3
@@ -13,10 +13,10 @@ angular.module('errors', ['ngSanitize'])
|
||||
};
|
||||
|
||||
return function (text, target) {
|
||||
var targetHtml = target ? ' target="' + target + '"' : '';
|
||||
|
||||
if (!text) return text;
|
||||
|
||||
var targetHtml = target ? ' target="' + target + '"' : '';
|
||||
|
||||
return $sanitize(text.replace(LINKY_URL_REGEXP, function (url) {
|
||||
if (STACK_TRACE_REGEXP.test(url)) {
|
||||
return url;
|
||||
@@ -34,6 +34,10 @@ angular.module('errors', ['ngSanitize'])
|
||||
|
||||
|
||||
.directive('errorDisplay', ['$location', 'errorLinkFilter', function ($location, errorLinkFilter) {
|
||||
var encodeAngleBrackets = function (text) {
|
||||
return text.replace(/</g, '<').replace(/>/g, '>');
|
||||
};
|
||||
|
||||
var interpolate = function (formatString) {
|
||||
var formatArgs = arguments;
|
||||
return formatString.replace(/\{\d+\}/g, function (match) {
|
||||
@@ -51,12 +55,15 @@ angular.module('errors', ['ngSanitize'])
|
||||
link: function (scope, element, attrs) {
|
||||
var search = $location.search(),
|
||||
formatArgs = [attrs.errorDisplay],
|
||||
formattedText,
|
||||
i;
|
||||
|
||||
for (i = 0; angular.isDefined(search['p'+i]); i++) {
|
||||
formatArgs.push(search['p'+i]);
|
||||
}
|
||||
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
|
||||
|
||||
formattedText = encodeAngleBrackets(interpolate.apply(null, formatArgs));
|
||||
element.html(errorLinkFilter(formattedText, '_blank'));
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
angular.module('examples', [])
|
||||
|
||||
.directive('runnableExample', ['$templateCache', '$document', function($templateCache, $document) {
|
||||
var exampleClassNameSelector = '.runnable-example-file';
|
||||
var doc = $document[0];
|
||||
var tpl =
|
||||
'<nav class="runnable-example-tabs" ng-if="tabs">' +
|
||||
' <a ng-class="{active:$index==activeTabIndex}"' +
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
|
||||
return {
|
||||
restrict: 'C',
|
||||
scope : true,
|
||||
controller : ['$scope', function($scope) {
|
||||
$scope.setTab = function(index) {
|
||||
var tab = $scope.tabs[index];
|
||||
$scope.activeTabIndex = index;
|
||||
$scope.$broadcast('tabChange', index, tab);
|
||||
};
|
||||
}],
|
||||
compile : function(element) {
|
||||
element.html(tpl + element.html());
|
||||
return function(scope, element) {
|
||||
var node = element[0];
|
||||
var examples = node.querySelectorAll(exampleClassNameSelector);
|
||||
var tabs = [], now = Date.now();
|
||||
angular.forEach(examples, function(child, index) {
|
||||
tabs.push(child.getAttribute('name'));
|
||||
});
|
||||
|
||||
if(tabs.length > 0) {
|
||||
scope.tabs = tabs;
|
||||
scope.$on('tabChange', function(e, index, title) {
|
||||
angular.forEach(examples, function(child) {
|
||||
child.style.display = 'none';
|
||||
});
|
||||
var selected = examples[index];
|
||||
selected.style.display = 'block';
|
||||
});
|
||||
scope.setTab(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('formPostData', ['$document', function($document) {
|
||||
return function(url, newWindow, fields) {
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
// AngularJS
|
||||
"angular": false,
|
||||
|
||||
// ngMocks
|
||||
"module": false,
|
||||
"inject": true,
|
||||
|
||||
// Jasmine
|
||||
"jasmine": false,
|
||||
"describe": false,
|
||||
"ddescribe": false,
|
||||
"xdescribe": false,
|
||||
"it": false,
|
||||
"iit": false,
|
||||
"xit": false,
|
||||
"beforeEach": false,
|
||||
"afterEach": false,
|
||||
"spyOn": false,
|
||||
"expect": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
'use strict';
|
||||
|
||||
describe('errors', function() {
|
||||
// Mock `ngSanitize` module
|
||||
angular.
|
||||
module('ngSanitize', []).
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').andCallFake(angular.identity));
|
||||
|
||||
beforeEach(module('errors'));
|
||||
|
||||
|
||||
describe('errorDisplay', function() {
|
||||
var $sanitize;
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(inject(function(_$sanitize_, _errorLinkFilter_) {
|
||||
$sanitize = _$sanitize_;
|
||||
errorLinkFilter = _errorLinkFilter_;
|
||||
}));
|
||||
|
||||
|
||||
it('should return empty input unchanged', function() {
|
||||
var inputs = [undefined, null, false, 0, ''];
|
||||
var remaining = inputs.length;
|
||||
|
||||
inputs.forEach(function(falsyValue) {
|
||||
expect(errorLinkFilter(falsyValue)).toBe(falsyValue);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should recognize URLs and convert them to `<a>`', function() {
|
||||
var urls = [
|
||||
['ftp://foo/bar?baz#qux'],
|
||||
['http://foo/bar?baz#qux'],
|
||||
['https://foo/bar?baz#qux'],
|
||||
['mailto:foo_bar@baz.qux', null, 'foo_bar@baz.qux'],
|
||||
['foo_bar@baz.qux', 'mailto:foo_bar@baz.qux', 'foo_bar@baz.qux']
|
||||
];
|
||||
var remaining = urls.length;
|
||||
|
||||
urls.forEach(function(values) {
|
||||
var actualUrl = values[0];
|
||||
var expectedUrl = values[1] || actualUrl;
|
||||
var expectedText = values[2] || expectedUrl;
|
||||
var anchor = '<a href="' + expectedUrl + '">' + expectedText + '</a>';
|
||||
|
||||
var input = 'start ' + actualUrl + ' end';
|
||||
var output = 'start ' + anchor + ' end';
|
||||
|
||||
expect(errorLinkFilter(input)).toBe(output);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should not recognize stack-traces as URLs', function() {
|
||||
var urls = [
|
||||
'ftp://foo/bar?baz#qux:4:2',
|
||||
'http://foo/bar?baz#qux:4:2',
|
||||
'https://foo/bar?baz#qux:4:2',
|
||||
'mailto:foo_bar@baz.qux:4:2',
|
||||
'foo_bar@baz.qux:4:2'
|
||||
];
|
||||
var remaining = urls.length;
|
||||
|
||||
urls.forEach(function(url) {
|
||||
var input = 'start ' + url + ' end';
|
||||
|
||||
expect(errorLinkFilter(input)).toBe(input);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should should set `[target]` if specified', function() {
|
||||
var url = 'https://foo/bar?baz#qux';
|
||||
var target = '_blank';
|
||||
var outputWithoutTarget = '<a href="' + url + '">' + url + '</a>';
|
||||
var outputWithTarget = '<a target="' + target + '" href="' + url + '">' + url + '</a>';
|
||||
|
||||
expect(errorLinkFilter(url)).toBe(outputWithoutTarget);
|
||||
expect(errorLinkFilter(url, target)).toBe(outputWithTarget);
|
||||
});
|
||||
|
||||
|
||||
it('should truncate the contents of the generated `<a>` to 60 characters', function() {
|
||||
var looongUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo';
|
||||
var truncatedUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooo...';
|
||||
var output = '<a href="' + looongUrl + '">' + truncatedUrl + '</a>';
|
||||
|
||||
expect(looongUrl.length).toBeGreaterThan(60);
|
||||
expect(truncatedUrl.length).toBe(60);
|
||||
expect(errorLinkFilter(looongUrl)).toBe(output);
|
||||
});
|
||||
|
||||
|
||||
it('should pass the final string through `$sanitize`', function() {
|
||||
$sanitize.reset();
|
||||
|
||||
var input = 'start https://foo/bar?baz#qux end';
|
||||
var output = errorLinkFilter(input);
|
||||
|
||||
expect($sanitize.callCount).toBe(1);
|
||||
expect($sanitize).toHaveBeenCalledWith(output);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('errorDisplay', function() {
|
||||
var $compile;
|
||||
var $location;
|
||||
var $rootScope;
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.decorator('errorLinkFilter', function() {
|
||||
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
|
||||
errorLinkFilter.andCallFake(angular.identity);
|
||||
|
||||
return errorLinkFilter;
|
||||
});
|
||||
}));
|
||||
beforeEach(inject(function(_$compile_, _$location_, _$rootScope_) {
|
||||
$compile = _$compile_;
|
||||
$location = _$location_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
|
||||
it('should set the element\s HTML', function() {
|
||||
var elem = $compile('<span error-display="bar">foo</span>')($rootScope);
|
||||
expect(elem.html()).toBe('bar');
|
||||
});
|
||||
|
||||
|
||||
it('should interpolate the contents against `$location.search()`', function() {
|
||||
spyOn($location, 'search').andReturn({p0: 'foo', p1: 'bar'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
|
||||
expect(elem.html()).toBe('foo = foo, bar = bar');
|
||||
});
|
||||
|
||||
|
||||
it('should pass the interpolated text through `errorLinkFilter`', function() {
|
||||
$location.search = jasmine.createSpy('search').andReturn({p0: 'foo'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
|
||||
expect(errorLinkFilter.callCount).toBe(1);
|
||||
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
|
||||
});
|
||||
|
||||
|
||||
it('should encode `<` and `>`', function() {
|
||||
var elem = $compile('<span error-display="<xyz>"></span>')($rootScope);
|
||||
expect(elem.text()).toBe('<xyz>');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,6 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'../angular-touch.js',
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -18,7 +18,6 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -21,7 +21,6 @@ module.exports = function productionDeployment(getVersion) {
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -12,9 +12,11 @@ myModule.directive('directiveName', function factory() {
|
||||
scope: {
|
||||
'attrName': '@', // OK
|
||||
'attrName2': '=localName', // OK
|
||||
'attrName3': 'name', // ERROR: missing mode @&=
|
||||
'attrName4': ' = name', // ERROR: extra spaces
|
||||
'attrName5': 'name=', // ERROR: must be prefixed with @&=
|
||||
'attrName3': '&?localName', // OK
|
||||
'attrName4': ' = name', // OK
|
||||
'attrName5': 'name', // ERROR: missing mode @&=
|
||||
'attrName6': 'name=', // ERROR: must be prefixed with @&=
|
||||
'attrName7': '=name?', // ERROR: ? must come directly after the mode
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
This error occurs when a module fails to load due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
||||
A common reason why the module fails to load is that you've forgotten to
|
||||
include the file with the defined module or that the file couldn't be loaded.
|
||||
|
||||
### Using `ngRoute`
|
||||
|
||||
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
|
||||
@@ -24,4 +27,4 @@ angular.module('ng').filter('tel', function (){});
|
||||
|
||||
Instead create your own module and add it as a dependency to your application's top-level module.
|
||||
See [#9692](https://github.com/angular/angular.js/issues/9692) and
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
|
||||
@@ -81,3 +81,6 @@ angular.module('myModule', [])
|
||||
// a scope object cannot be injected into a service.
|
||||
}]);
|
||||
```
|
||||
|
||||
If you encounter this error only with minified code, consider using `ngStrictDi` (see
|
||||
{@link ng.directive:ngApp ngApp}) to provoke the error with the non-minified source.
|
||||
|
||||
@@ -161,7 +161,7 @@ In this second scenario, we are already inside a `$digest` when the ngFocus dire
|
||||
call to `$apply()`, causing this error to be thrown.
|
||||
|
||||
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in an
|
||||
`$apply` block:
|
||||
|
||||
```
|
||||
@@ -200,7 +200,7 @@ the top of the call stack.
|
||||
Once you have identified this call you work your way up the stack to see what the problem is.
|
||||
|
||||
* If the second call was made in your application code then you should look at why this code has been
|
||||
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
called from within an `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
sync/async scenario described earlier.
|
||||
|
||||
* If the second call was made inside an Angular directive then it is likely that it matches the second
|
||||
|
||||
@@ -330,8 +330,8 @@ reload to the original link.
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url
|
||||
base in the head of your main html file (`<base href="/my-base">`) unless `html5Mode.requireBase` is
|
||||
set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
|
||||
base in the head of your main html file (`<base href="/my-base/index.html">`) unless `html5Mode.requireBase`
|
||||
is set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
|
||||
that, relative urls will always be resolved to this base url, even if the initial url of the
|
||||
document was different.
|
||||
|
||||
@@ -339,6 +339,7 @@ There is one exception: Links that only contain a hash fragment (e.g. `<a href="
|
||||
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
|
||||
to anchors on the same page without needing to know on which page the user currently is.
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
@@ -346,6 +347,20 @@ to entry point of your application (e.g. index.html). Requiring a `<base>` tag i
|
||||
this case, as it allows Angular to differentiate between the part of the url that is the application
|
||||
base and the path that should be handled by the application.
|
||||
|
||||
### Base href constraints
|
||||
|
||||
The `$location` service is not able to function properly if the current URL is outside the URL given
|
||||
as the base href. This can have subtle confusing consequences...
|
||||
|
||||
Consider a base href set as follows: `<base href="/base/">` (i.e. the application exists in the "folder"
|
||||
called `/base`). The URL `/base` is actually outside the application (it refers to the `base` file found
|
||||
in the root `/` folder).
|
||||
|
||||
If you wish to be able to navigate to the application via a URL such as `/base` then you should ensure that
|
||||
you server is setup to redirect such requests to `/base/`.
|
||||
|
||||
See https://github.com/angular/angular.js/issues/14018 for more information.
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
|
||||
|
||||
@@ -41,7 +41,7 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
<h2 id="ngmodel">ngModel</h2>
|
||||
|
||||
Much of ngAria's heavy lifting happens in the {@link ngModel ngModel}
|
||||
Much of ngAria's heavy lifting happens in the {@link ng.directive:ngModel ngModel}
|
||||
directive. For elements using ngModel, special attention is paid by ngAria if that element also
|
||||
has a role or type of `checkbox`, `radio`, `range` or `textbox`.
|
||||
|
||||
@@ -134,14 +134,14 @@ attributes (if they have not been explicitly specified by the developer):
|
||||
|
||||
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
|
||||
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
it with your keyboard and at least one mobile and desktop screen reader.
|
||||
|
||||
<h2 id="ngdisabled">ngDisabled</h2>
|
||||
|
||||
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
|
||||
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
|
||||
using ngAria with [ngDisabled](https://docs.angularjs.org/api/ng/directive/ngDisabled) will also
|
||||
using ngAria with {@link ng.directive:ngDisabled ngDisabled} will also
|
||||
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
|
||||
custom controls to be more accessible.
|
||||
|
||||
@@ -162,7 +162,7 @@ Becomes:
|
||||
|
||||
<h2 id="ngshow">ngShow</h2>
|
||||
|
||||
>The [ngShow](https://docs.angularjs.org/api/ng/directive/ngShow) directive shows or hides the
|
||||
>The {@link ng.directive:ngShow ngShow} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngShow` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -199,7 +199,7 @@ Becomes:
|
||||
|
||||
<h2 id="nghide">ngHide</h2>
|
||||
|
||||
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
|
||||
>The {@link ng.directive:ngHide ngHide} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngHide` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -208,7 +208,7 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
|
||||
`display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.
|
||||
|
||||
<h2><span id="ngclick">ngClick</span> and <span id="ngdblclick">ngDblclick</span></h2>
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
|
||||
a node blacklist:
|
||||
|
||||
* Button
|
||||
@@ -218,14 +218,14 @@ a node blacklist:
|
||||
* Select
|
||||
* Details/Summary
|
||||
|
||||
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
|
||||
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
|
||||
dynamically bind a keypress event by default as long as the element isn't in the node blacklist.
|
||||
You can turn this functionality on or off with the `bindKeypress` configuration option.
|
||||
You can turn this functionality on or off with the `bindKeypress` configuration option.
|
||||
|
||||
ngAria will also add the `button` role to communicate to users of assistive technologies. This can
|
||||
be disabled with the `bindRoleForClick` configuration option.
|
||||
|
||||
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
|
||||
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
|
||||
such as `div` or `taco-button` to enable keyboard access.
|
||||
|
||||
<h3>Example</h3>
|
||||
|
||||
@@ -274,6 +274,37 @@ myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
}]);
|
||||
```
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
|
||||
where the animated element is briefly visible.
|
||||
|
||||
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
|
||||
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
|
||||
animations (`enter`, `move`, and `leave`).
|
||||
|
||||
Here's an example where you might see flickering:
|
||||
|
||||
```html
|
||||
<div ng-class="{red: myProp}">
|
||||
<div ng-class="{blue: myProp}">
|
||||
<div class="message" ng-if="myProp"></div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
|
||||
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
|
||||
|
||||
```css
|
||||
.message.ng-enter-prepare {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Other animation styles ... */
|
||||
```
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
@ngdoc overview
|
||||
@name Decorators
|
||||
@sortOrder 345
|
||||
@description
|
||||
|
||||
# Decorators in AngularJS
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**NOTE:** This guide is targeted towards developers who are already familiar with AngularJS basics.
|
||||
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
|
||||
</div>
|
||||
|
||||
## What are decorators?
|
||||
|
||||
Decorators are a design pattern that is used to separate modification or *decoration* of a class without modifying the
|
||||
original source code. In Angular, decorators are functions that allow a service, directive or filter to be modified
|
||||
prior to its usage.
|
||||
|
||||
## How to use decorators
|
||||
|
||||
There are two ways to register decorators
|
||||
|
||||
- `$provide.decorator`, and
|
||||
- `module.decorator`
|
||||
|
||||
Each provide access to a `$delegate`, which is the instantiated service/directive/filter, prior to being passed to the
|
||||
service that required it.
|
||||
|
||||
### $provide.decorator
|
||||
|
||||
The {@link api/auto/service/$provide#decorator decorator function} allows access to a $delegate of the service once it
|
||||
has been instantiated. For example:
|
||||
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('$log', [
|
||||
'$delegate',
|
||||
function $logDecorator($delegate) {
|
||||
|
||||
var originalWarn = $delegate.warn;
|
||||
$delegate.warn = function decoratedWarn(msg) {
|
||||
msg = 'Decorated Warn: ' + msg;
|
||||
originalWarn.apply($delegate, arguments);
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
After the `$log` service has been instantiated the decorator is fired. The decorator function has a `$delegate` object
|
||||
injected to provide access to the service that matches the selector in the decorator. This `$delegate` will be the
|
||||
service you are decorating. The return value of the function *provided to the decorator* will take place of the service,
|
||||
directive, or filter being decorated.
|
||||
|
||||
<hr>
|
||||
|
||||
The `$delegate` may be either modified or completely replaced. Given a service `myService` with a method `someFn`, the
|
||||
following could all be viable solutions:
|
||||
|
||||
|
||||
#### Completely Replace the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
var myDecoratedService = {
|
||||
// new service object to replace myService
|
||||
};
|
||||
return myDecoratedService;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
#### Patch the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
var someFn = $delegate.someFn;
|
||||
|
||||
function aNewFn() {
|
||||
// new service function
|
||||
someFn.apply($delegate, arguments);
|
||||
}
|
||||
|
||||
$delegate.someFn = aNewFn;
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
#### Augment the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
function helperFn() {
|
||||
// an additional fn to add to the service
|
||||
}
|
||||
|
||||
$delegate.aHelpfulAddition = helperFn;
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
<div class="alert alert-info">
|
||||
Note that whatever is returned by the decorator function will replace that which is being decorated. For example, a
|
||||
missing return statement will wipe out the entire object being decorated.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
Decorators have different rules for different services. This is because services are registered in different ways.
|
||||
Services are selected by name, however filters and directives are selected by appending `"Filter"` or `"Directive"` to
|
||||
the end of the name. The `$delegate` provided is dictated by the type of service.
|
||||
|
||||
| Service Type | Selector | $delegate |
|
||||
|--------------|-------------------------------|-----------------------------------------------------------------------|
|
||||
| Service | `serviceName` | The `object` or `function` returned by the service |
|
||||
| Directive | `directiveName + 'Directive'` | An `Array.<DirectiveObject>`<sub>{@link guide/decorators#drtvArray 1}</sub> |
|
||||
| Filter | `filterName + 'Filter'` | The `function` returned by the filter |
|
||||
|
||||
<small id="drtvArray">1. Multiple directives may be registered to the same selector/name</small>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**NOTE:** Developers should take care in how and why they are modifying the `$delegate` for the service. Not only
|
||||
should expectations for the consumer be kept, but some functionality (such as directive registration) does not take
|
||||
place after decoration, but during creation/registration of the original service. This means, for example, that
|
||||
an action such as pushing a directive object to a directive `$delegate` will likely result in unexpected behavior.
|
||||
|
||||
Furthermore, great care should be taken when decorating core services, directives, or filters as this may unexpectedly
|
||||
or adversely affect the functionality of the framework.
|
||||
</div>
|
||||
|
||||
### module.decorator
|
||||
|
||||
This {@link api/ng/type/angular.Module#decorator function} is the same as the `$provide.decorator` function except it is
|
||||
exposed through the module API. This allows you to separate your decorator patterns from your module config blocks. The
|
||||
main caveat here is that you will need to take note the order in which you create your decorators.
|
||||
|
||||
Unlike in the module config block (which allows configuration of services prior to their creation), the service must be
|
||||
registered prior to the decorator (see {@link guide/providers#provider-recipe Provider Recipe}). For example, the
|
||||
following would not work because you are attempting to decorate outside of the configuration phase and the service
|
||||
hasn't been created yet:
|
||||
|
||||
```js
|
||||
// will cause an error since 'someService' hasn't been registered
|
||||
angular.module('myApp').decorator('someService', ...);
|
||||
|
||||
angular.module('myApp').factory('someService', ...);
|
||||
```
|
||||
|
||||
## Example Applications
|
||||
|
||||
The following sections provide examples each of a service decorator, a directive decorator, and a filter decorator.
|
||||
|
||||
### Service Decorator Example
|
||||
|
||||
This example shows how we can replace the $log service with our own to display log messages.
|
||||
|
||||
<example module="myServiceDecorator" name="service-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('myServiceDecorator', []).
|
||||
|
||||
controller('Ctrl', [
|
||||
'$scope',
|
||||
'$log',
|
||||
'$timeout',
|
||||
function($scope, $log, $timeout) {
|
||||
var types = ['error', 'warn', 'log', 'info' ,'debug'], i;
|
||||
|
||||
for (i = 0; i < types.length; i++) {
|
||||
$log[types[i]](types[i] + ': message ' + (i + 1));
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
$log.info('info: message logged in timeout');
|
||||
});
|
||||
}
|
||||
]).
|
||||
|
||||
directive('myLog', [
|
||||
'$log',
|
||||
function($log) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: '<ul id="myLog"><li ng-repeat="l in myLog" class="{{l.type}}">{{l.message}}</li></ul>',
|
||||
scope: {},
|
||||
compile: function() {
|
||||
return function(scope) {
|
||||
scope.myLog = $log.stack;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
]).
|
||||
|
||||
config([
|
||||
'$provide',
|
||||
function($provide) {
|
||||
|
||||
$provide.decorator('$log', [
|
||||
'$delegate',
|
||||
function logDecorator($delegate) {
|
||||
|
||||
var myLog = {
|
||||
warn: function(msg) {
|
||||
log(msg, 'warn');
|
||||
},
|
||||
error: function(msg) {
|
||||
log(msg, 'error');
|
||||
},
|
||||
info: function(msg) {
|
||||
log(msg, 'info');
|
||||
},
|
||||
debug: function(msg) {
|
||||
log(msg, 'debug');
|
||||
},
|
||||
log: function(msg) {
|
||||
log(msg, 'log');
|
||||
},
|
||||
stack: []
|
||||
};
|
||||
|
||||
function log(msg, type) {
|
||||
myLog.stack.push({ type: type, message: msg.toString() });
|
||||
if (console && console[type]) console[type](msg);
|
||||
}
|
||||
|
||||
return myLog;
|
||||
|
||||
}
|
||||
]);
|
||||
|
||||
}
|
||||
]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<h1>Logs</h1>
|
||||
<my-log></my-log>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="style.css">
|
||||
li.warn { color: yellow; }
|
||||
li.error { color: red; }
|
||||
li.info { color: blue }
|
||||
li.log { color: black }
|
||||
li.debug { color: green }
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should display log messages in dom', function() {
|
||||
element.all(by.repeater('l in myLog')).count().then(function(count) {
|
||||
expect(count).toEqual(6);
|
||||
});
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
### Directive Decorator Example
|
||||
|
||||
Failed interpolated expressions in `ng-href` attributes can easily go unnoticed. We can decorate `ngHref` to warn us of
|
||||
those conditions.
|
||||
|
||||
<example module="urlDecorator" name="directive-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('urlDecorator', []).
|
||||
|
||||
controller('Ctrl', ['$scope', function ($scope) {
|
||||
$scope.id = 3;
|
||||
$scope.warnCount = 0; // for testing
|
||||
}]).
|
||||
|
||||
config(['$provide', function($provide) {
|
||||
|
||||
// matchExpressions looks for interpolation markup in the directive attribute, extracts the expressions
|
||||
// from that markup (if they exist) and returns an array of those expressions
|
||||
function matchExpressions(str) {
|
||||
var exps = str.match(/{{([^}]+)}}/g);
|
||||
|
||||
// if there isn't any, get out of here
|
||||
if (exps === null) return;
|
||||
|
||||
exps = exps.map(function(exp) {
|
||||
var prop = exp.match(/[^{}]+/);
|
||||
return prop === null ? null : prop[0];
|
||||
});
|
||||
|
||||
return exps;
|
||||
}
|
||||
|
||||
// remember: directives must be selected by appending 'Directive' to the directive selector
|
||||
$provide.decorator('ngHrefDirective', [
|
||||
'$delegate',
|
||||
'$log',
|
||||
'$parse',
|
||||
function($delegate, $log, $parse) {
|
||||
|
||||
// store the original link fn
|
||||
var originalLinkFn = $delegate[0].link;
|
||||
|
||||
// replace the compile fn
|
||||
$delegate[0].compile = function(tElem, tAttr) {
|
||||
|
||||
// store the original exp in the directive attribute for our warning message
|
||||
var originalExp = tAttr.ngHref;
|
||||
|
||||
// get the interpolated expressions
|
||||
var exps = matchExpressions(originalExp);
|
||||
|
||||
// create and store the getters using $parse
|
||||
var getters = exps.map(function(el) {
|
||||
if (el) return $parse(el);
|
||||
});
|
||||
|
||||
return function newLinkFn(scope, elem, attr) {
|
||||
// fire the originalLinkFn
|
||||
originalLinkFn.apply($delegate[0], arguments);
|
||||
|
||||
// observe the directive attr and check the expressions
|
||||
attr.$observe('ngHref', function(val) {
|
||||
|
||||
// if we have getters and getters is an array...
|
||||
if (getters && angular.isArray(getters)) {
|
||||
|
||||
// loop through the getters and process them
|
||||
angular.forEach(getters, function(g, idx) {
|
||||
|
||||
// if val is truthy, then the warning won't log
|
||||
var val = angular.isFunction(g) ? g(scope) : true;
|
||||
if (!val) {
|
||||
$log.warn('NgHref Warning: "' + exps[idx] + '" in the expression "' + originalExp +
|
||||
'" is falsy!');
|
||||
|
||||
scope.warnCount++; // for testing
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// get rid of the old link function since we return a link function in compile
|
||||
delete $delegate[0].link;
|
||||
|
||||
// return the $delegate
|
||||
return $delegate;
|
||||
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<a ng-href="/products/{{ id }}/view" id="id3">View Product {{ id }}</a>
|
||||
- <strong>id == 3</strong>, so no warning<br>
|
||||
<a ng-href="/products/{{ id + 5 }}/view" id="id8">View Product {{ id + 5 }}</a>
|
||||
- <strong>id + 5 == 8</strong>, so no warning<br>
|
||||
<a ng-href="/products/{{ someOtherId }}/view" id="someOtherId">View Product {{ someOtherId }}</a>
|
||||
- <strong style="background-color: #ffff00;">someOtherId == undefined</strong>, so warn<br>
|
||||
<a ng-href="/products/{{ someOtherId + 5 }}/view" id="someOtherId5">View Product {{ someOtherId + 5 }}</a>
|
||||
- <strong>someOtherId + 5 == 5</strong>, so no warning<br>
|
||||
<div>Warn Count: {{ warnCount }}</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should warn when an expression in the interpolated value is falsy', function() {
|
||||
var id3 = element(by.id('id3'));
|
||||
var id8 = element(by.id('id8'));
|
||||
var someOther = element(by.id('someOtherId'));
|
||||
var someOther5 = element(by.id('someOtherId5'));
|
||||
|
||||
expect(id3.getText()).toEqual('View Product 3');
|
||||
expect(id3.getAttribute('href')).toContain('/products/3/view');
|
||||
|
||||
expect(id8.getText()).toEqual('View Product 8');
|
||||
expect(id8.getAttribute('href')).toContain('/products/8/view');
|
||||
|
||||
expect(someOther.getText()).toEqual('View Product');
|
||||
expect(someOther.getAttribute('href')).toContain('/products//view');
|
||||
|
||||
expect(someOther5.getText()).toEqual('View Product 5');
|
||||
expect(someOther5.getAttribute('href')).toContain('/products/5/view');
|
||||
|
||||
expect(element(by.binding('warnCount')).getText()).toEqual('Warn Count: 1');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
### Filter Decorator Example
|
||||
|
||||
Let's say we have created an app that uses the default format for many of our `Date` filters. Suddenly requirements have
|
||||
changed (that never happens) and we need all of our default dates to be `'shortDate'` instead of `'mediumDate'`.
|
||||
|
||||
<example module="filterDecorator" name="filter-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('filterDecorator', []).
|
||||
|
||||
controller('Ctrl', ['$scope', function ($scope) {
|
||||
$scope.genesis = new Date(2010, 0, 5);
|
||||
$scope.ngConf = new Date(2016, 4, 4);
|
||||
}]).
|
||||
|
||||
config(['$provide', function($provide) {
|
||||
|
||||
$provide.decorator('dateFilter', [
|
||||
'$delegate',
|
||||
function dateDecorator($delegate) {
|
||||
|
||||
// store the original filter
|
||||
var originalFilter = $delegate;
|
||||
|
||||
// return our filter
|
||||
return shortDateDefault;
|
||||
|
||||
// shortDateDefault sets the format to shortDate if it is falsy
|
||||
function shortDateDefault(date, format, timezone) {
|
||||
if (!format) format = 'shortDate';
|
||||
|
||||
// return the result of the original filter
|
||||
return originalFilter(date, format, timezone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div id="genesis">Initial Commit default to short date: {{ genesis | date }}</div>
|
||||
<div>ng-conf 2016 default short date: {{ ngConf | date }}</div>
|
||||
<div id="ngConf">ng-conf 2016 with full date format: {{ ngConf | date:'fullDate' }}</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should default date filter to short date format', function() {
|
||||
expect(element(by.id('genesis')).getText())
|
||||
.toMatch(/Initial Commit default to short date: \d{1,2}\/\d{1,2}\/\d{2}/);
|
||||
});
|
||||
|
||||
it('should still allow dates to be formatted', function() {
|
||||
expect(element(by.id('ngConf')).getText())
|
||||
.toMatch(/ng-conf 2016 with full date format\: [A-Za-z]+, [A-Za-z]+ \d{1,2}, \d{4}/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -99,8 +99,13 @@ For example, the following forms are all equivalent and match the {@link ngBind}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should show off bindings', function() {
|
||||
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
var containerElm = element(by.css('div[ng-controller="Controller"]'));
|
||||
var nameBindings = containerElm.all(by.binding('name'));
|
||||
|
||||
expect(nameBindings.count()).toBe(5);
|
||||
nameBindings.each(function(elem) {
|
||||
expect(elem.getText()).toEqual('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
});
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -298,6 +303,7 @@ The `restrict` option is typically set to:
|
||||
* `'A'` - only matches attribute name
|
||||
* `'E'` - only matches element name
|
||||
* `'C'` - only matches class name
|
||||
* `'M'` - only matches comment
|
||||
|
||||
These restrictions can all be combined as needed:
|
||||
|
||||
@@ -401,7 +407,7 @@ This is clearly not a great solution.
|
||||
|
||||
What we want to be able to do is separate the scope inside a directive from the scope
|
||||
outside, and then map the outer scope to a directive's inner scope. We can do this by creating what
|
||||
we call an **isolate scope**. To do this, we can use a directive's `scope` option:
|
||||
we call an **isolate scope**. To do this, we can use a {@link $compile#-scope- directive's `scope`} option:
|
||||
|
||||
<example module="docsIsolateScopeDirective">
|
||||
<file name="script.js">
|
||||
@@ -641,6 +647,7 @@ To do this, we need to use the `transclude` option.
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {},
|
||||
templateUrl: 'my-dialog.html'
|
||||
};
|
||||
});
|
||||
@@ -651,8 +658,7 @@ To do this, we need to use the `transclude` option.
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-dialog.html">
|
||||
<div class="alert" ng-transclude>
|
||||
</div>
|
||||
<div class="alert" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -674,7 +680,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
transclude: true,
|
||||
scope: {},
|
||||
templateUrl: 'my-dialog.html',
|
||||
link: function (scope, element) {
|
||||
link: function (scope) {
|
||||
scope.name = 'Jeff';
|
||||
}
|
||||
};
|
||||
@@ -686,8 +692,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-dialog.html">
|
||||
<div class="alert" ng-transclude>
|
||||
</div>
|
||||
<div class="alert" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -698,7 +703,7 @@ The `transclude` option changes the way scopes are nested. It makes it so that t
|
||||
transcluded directive have whatever scope is outside the directive, rather than whatever scope is on
|
||||
the inside. In doing so, it gives the contents access to the outside scope.
|
||||
|
||||
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff';` would
|
||||
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff'` would
|
||||
reference the outside scope and we would see `Jeff` in the output.
|
||||
|
||||
This behavior makes sense for a directive that wraps some content, because otherwise you'd have to
|
||||
@@ -771,9 +776,9 @@ function.
|
||||
|
||||
Often it's desirable to pass data from the isolate scope via an expression to the
|
||||
parent scope, this can be done by passing a map of local variable names and values into the expression
|
||||
wrapper fn. For example, the hideDialog function takes a message to display when the dialog is hidden.
|
||||
This is specified in the directive by calling `close({message: 'closing for now'})`. Then the local
|
||||
variable `message` will be available within the `on-close` expression.
|
||||
wrapper function. For example, the `hideDialog` function takes a message to display when the dialog
|
||||
is hidden. This is specified in the directive by calling `close({message: 'closing for now'})`.
|
||||
Then the local variable `message` will be available within the `on-close` expression.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
@@ -832,7 +837,7 @@ element?
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<span my-draggable>Drag ME</span>
|
||||
<span my-draggable>Drag Me</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -877,7 +882,7 @@ to which tab is active.
|
||||
})
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: '^myTabs',
|
||||
require: '^^myTabs',
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
@@ -893,11 +898,9 @@ to which tab is active.
|
||||
<file name="index.html">
|
||||
<my-tabs>
|
||||
<my-pane title="Hello">
|
||||
<h4>Hello</h4>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
</my-pane>
|
||||
<my-pane title="World">
|
||||
<h4>World</h4>
|
||||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||||
</my-pane>
|
||||
@@ -914,22 +917,25 @@ to which tab is active.
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-pane.html">
|
||||
<div class="tab-pane" ng-show="selected" ng-transclude>
|
||||
<div class="tab-pane" ng-show="selected">
|
||||
<h4>{{title}}</h4>
|
||||
<div ng-transclude></div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
The `myPane` directive has a `require` option with value `^myTabs`. When a directive uses this
|
||||
option, `$compile` will throw an error unless the specified controller is found. The `^` prefix
|
||||
means that this directive searches for the controller on its parents (without the `^` prefix, the
|
||||
directive would look for the controller on just its own element).
|
||||
The `myPane` directive has a `require` option with value `^^myTabs`. When a directive uses this
|
||||
option, `$compile` will throw an error unless the specified controller is found. The `^^` prefix
|
||||
means that this directive searches for the controller on its parents. (A `^` prefix would make the
|
||||
directive look for the controller on its own element or its parents; without any prefix, the
|
||||
directive would look on its own element only.)
|
||||
|
||||
So where does this `myTabs` controller come from? Directives can specify controllers using
|
||||
the unsurprisingly named `controller` option. As you can see, the `myTabs` directive uses this
|
||||
option. Just like `ngController`, this option attaches a controller to the template of the directive.
|
||||
|
||||
If it is necessary to reference the controller or any functions bound to the controller's scope in
|
||||
the template, you can use the option `controllerAs` to specify the name of the controller as an alias.
|
||||
If it is necessary to reference the controller or any functions bound to the controller from the
|
||||
template, you can use the option `controllerAs` to specify the name of the controller as an alias.
|
||||
The directive needs to define a scope for this configuration to be used. This is particularly useful
|
||||
in the case when the directive is used as a component.
|
||||
|
||||
@@ -944,7 +950,7 @@ The corresponding parameter being sent to the `link` function will also be an ar
|
||||
angular.module('docsTabsExample', [])
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: ['^myTabs', '^ngModel'],
|
||||
require: ['^^myTabs', 'ngModel'],
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
@@ -980,4 +986,3 @@ available in the {@link guide/compiler compiler guide}.
|
||||
|
||||
The {@link ng.$compile `$compile` API} page has a comprehensive list of directive options for
|
||||
reference.
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@sortOrder 280
|
||||
@description
|
||||
|
||||
# Filters
|
||||
|
||||
A filter formats the value of an expression for display to the user. They can be used in view templates,
|
||||
controllers or services and it is easy to define your own filter.
|
||||
|
||||
@@ -32,10 +34,13 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
|
||||
|
||||
## Using filters in controllers, services, and directives
|
||||
|
||||
You can also use filters in controllers, services, and directives. For this, inject a dependency
|
||||
with the name `<filterName>Filter` to your controller/service/directive. E.g. using the dependency
|
||||
`numberFilter` will inject the number filter. The injected argument is a function that takes the
|
||||
value to format as first argument and filter parameters starting with the second argument.
|
||||
You can also use filters in controllers, services, and directives.
|
||||
|
||||
<div class="alert alert-info">
|
||||
For this, inject a dependency with the name `<filterName>Filter` into your controller/service/directive.
|
||||
E.g. a filter called `number` is injected by using the dependency `numberFilter`. The injected argument
|
||||
is a function that takes the value to format as first argument, and filter parameters starting with the second argument.
|
||||
</div>
|
||||
|
||||
The example below uses the filter called {@link ng.filter:filter `filter`}.
|
||||
This filter reduces arrays into sub arrays based on
|
||||
@@ -108,6 +113,7 @@ text upper-case.
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
Reverse, filtered in controller: {{filteredGreeting}}<br>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
@@ -127,8 +133,9 @@ text upper-case.
|
||||
return out;
|
||||
};
|
||||
})
|
||||
.controller('MyController', ['$scope', function($scope) {
|
||||
.controller('MyController', ['$scope', 'reverseFilter', function($scope, reverseFilter) {
|
||||
$scope.greeting = 'hello';
|
||||
$scope.filteredGreeting = reverseFilter($scope.greeting);
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -440,8 +440,7 @@ Note that you can alternatively use `ng-pattern` to further restrict the validat
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i;
|
||||
|
||||
return {
|
||||
require: 'ngModel',
|
||||
restrict: '',
|
||||
require: '?ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// only apply the validator if ngModel is present and Angular has added the email validator
|
||||
if (ctrl && ctrl.$validators.email) {
|
||||
|
||||
@@ -91,7 +91,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
### Server-Specific
|
||||
|
||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
@@ -101,7 +101,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
|
||||
## Learning Resources
|
||||
|
||||
###Books
|
||||
### Books
|
||||
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
|
||||
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
@@ -111,10 +111,12 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
|
||||
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
|
||||
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
|
||||
|
||||
###Videos:
|
||||
### Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
* [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/)
|
||||
|
||||
### Courses
|
||||
* **Free online:**
|
||||
@@ -122,6 +124,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
# Interpolation and data-binding
|
||||
|
||||
Interpolation markup with embedded @link {guide/expressions expressions} is used by Angular to
|
||||
Interpolation markup with embedded {@link guide/expression expressions} is used by Angular to
|
||||
provide data-binding to text nodes and attribute values.
|
||||
|
||||
An example of interpolation is shown below:
|
||||
@@ -43,8 +43,8 @@ interpret the attribute as present, meaning that the button would always be disa
|
||||
```
|
||||
|
||||
For this reason, Angular provides special `ng`-prefixed directives for the following boolean attributes:
|
||||
{@link ngDisabled `disabled`}, [@link ngRequired `required`}, [@link ngSelected `selected`},
|
||||
{@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and [@link ngOpen `open`}.
|
||||
{@link ngDisabled `disabled`}, {@link ngRequired `required`}, {@link ngSelected `selected`},
|
||||
{@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and {@link ngOpen `open`}.
|
||||
|
||||
These directives take an expression inside the attribute, and set the corresponding boolean attribute
|
||||
to true when the expression evaluates to truthy.
|
||||
@@ -98,16 +98,20 @@ For example, to bind to `viewBox`, we can write:
|
||||
</svg>
|
||||
```
|
||||
|
||||
The following attributes are also known to cause problems when used with normal bindings:
|
||||
Other attributes may also not work as expected when they contain interpolation markup, and
|
||||
can be used with `ngAttr` instead. The following is a list of known problematic attributes:
|
||||
|
||||
- **size** in `<select>` elements (see [Github issue 1619](https://github.com/angular/angular.js/issues/1619))
|
||||
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [Github issue 5025](https://github.com/angular/angular.js/issues/5025))
|
||||
- **size** in `<select>` elements (see [issue 1619](https://github.com/angular/angular.js/issues/1619))
|
||||
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [issue 5025](https://github.com/angular/angular.js/issues/5025))
|
||||
- **type** in `<button>` in Internet Explorer 11 (see [issue 14117](https://github.com/angular/angular.js/issues/5025))
|
||||
|
||||
|
||||
### Embedding interpolation markup inside expressions
|
||||
|
||||
Angular directives take either expressions or interpolation markup with embedded expressions. So the
|
||||
following example which embeds interpolation inside an expression is a bad practice:
|
||||
<div class="alert alert-danger">
|
||||
**Note:** Angular directive attributes take either expressions *or* interpolation markup with embedded expressions.
|
||||
It is considered **bad practice** to embed interpolation markup inside an expression:
|
||||
</div>
|
||||
|
||||
```html
|
||||
<div ng-show="form{{$index}}.$invalid"></div>
|
||||
@@ -120,8 +124,8 @@ You should instead delegate the computation of complex expressions to the scope,
|
||||
```
|
||||
|
||||
```js
|
||||
function getForm() {
|
||||
return $scope['form' + $index];
|
||||
function getForm(index) {
|
||||
return $scope['form' + index];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -244,6 +244,46 @@ ngModelCtrl.$formatters.push(function(value) {
|
||||
});
|
||||
```
|
||||
|
||||
### form
|
||||
|
||||
Due to [94533e57](https://github.com/angular/angular.js/commit/94533e570673e6b2eb92073955541fa289aabe02),
|
||||
the `name` attribute of `form` elements can now only contain characters that can be evaluated as part
|
||||
of an Angular expression. This is because Angular uses the value of `name` as an assignable expression
|
||||
to set the form on the `$scope`. For example, `name="myForm"` assigns the form to `$scope.myForm` and
|
||||
`name="myObj.myForm"` assigns it to `$scope.myObj.myForm`.
|
||||
|
||||
Previously, it was possible to also use names such `name="my:name"`, because Angular used a special setter
|
||||
function for the form name. Now the general, more robust `$parse` setter is used.
|
||||
|
||||
The easiest way to migrate your code is therefore to remove all special characters from the `name` attribute.
|
||||
|
||||
If you need to keep the special characters, you can use the following directive, which will replace
|
||||
the `name` with a value that can be evaluated as an expression in the compile function, and then
|
||||
re-set the original name in the postLink function. This ensures that (1), the form is published on
|
||||
the scope, and (2), the form has the original name, which might be important if you are doing server-side
|
||||
form submission.
|
||||
|
||||
```js
|
||||
angular.module('myApp').directive('form', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
priority: 1000,
|
||||
compile: function(element, attrs) {
|
||||
var unsupportedCharacter = ':'; // change accordingly
|
||||
var originalName = attrs.name;
|
||||
if (attrs.name && attrs.name.indexOf(unsupportedCharacter) > 0) {
|
||||
attrs.$set('name', 'this["' + originalName + '"]');
|
||||
}
|
||||
|
||||
return postLinkFunction(scope, element) {
|
||||
// Don't trigger $observers
|
||||
element.setAttribute('name', originalName);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
## Templating (`ngRepeat`, `$compile`)
|
||||
|
||||
### ngRepeat
|
||||
|
||||
@@ -75,9 +75,8 @@ that you break your application to multiple modules like this:
|
||||
* And an application level module which depends on the above modules and contains any
|
||||
initialization code.
|
||||
|
||||
We've also
|
||||
[written a document](http://angularjs.blogspot.com/2014/02/an-angularjs-style-guide-and-best.html)
|
||||
on how we organize large apps at Google.
|
||||
You can find a community
|
||||
[style guide](https://github.com/johnpapa/angular-styleguide) to help yourself when application grows.
|
||||
|
||||
The above is a suggestion. Tailor it to your needs.
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
||||
|
||||
2. **Watcher registration**
|
||||
|
||||
During template linking directives register {@link
|
||||
During template linking, directives register {@link
|
||||
ng.$rootScope.Scope#$watch watches} on the scope. These watches will be
|
||||
used to propagate model values to the DOM.
|
||||
|
||||
|
||||
@@ -138,9 +138,13 @@ batchModule.factory('batchLog', ['$interval', '$log', function($interval, $log)
|
||||
*/
|
||||
batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope',
|
||||
function($route, batchLog, $rootScope) {
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
return {
|
||||
startMonitoring: function() {
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
```
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@sortOrder 260
|
||||
@description
|
||||
|
||||
# Templates
|
||||
|
||||
In Angular, templates are written with HTML that contains Angular-specific elements and attributes.
|
||||
Angular combines the template with information from the model and controller to render the dynamic
|
||||
view that a user sees in the browser.
|
||||
|
||||
@@ -46,7 +46,7 @@ Download the version you want and have fun.
|
||||
Each directory under <http://code.angularjs.org/> includes the following set of files:
|
||||
|
||||
* __`angular.js`__ — This file is non-obfuscated, non-minified, and human-readable by
|
||||
opening it it any editor or browser. In order to get better error messages during development, you
|
||||
opening it in any editor or browser. In order to get better error messages during development, you
|
||||
should always use this non-minified angular script.
|
||||
|
||||
* __`angular.min.js`__ — This is a minified and obfuscated version of
|
||||
|
||||
@@ -43,7 +43,7 @@ __`app/index.html`:__
|
||||
...
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}"></a>
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
|
||||
@@ -53,6 +53,18 @@ preconfigured npm to run bower install for us:
|
||||
npm install
|
||||
```
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
|
||||
problem with the `bower install` due to a conflict between the versions of angular.js that need to
|
||||
be installed. If you get this then simply delete your `app/bower_components` folder before running
|
||||
`npm install`.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
preconfigured `npm install` to run bower for us.
|
||||
</div>
|
||||
|
||||
|
||||
## Multiple Views, Routing and Layout Template
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ The name of the starting class is the name of the event that is fired (like `ent
|
||||
The active class name is the same as the starting class's but with an `-active` suffix.
|
||||
This two-class CSS naming convention allows the developer to craft an animation, beginning to end.
|
||||
|
||||
In our example above, elements are expanded from a height of **0** to **120 pixels** when they're added to the
|
||||
In our example above, elements are expanded from a height of **0** to **120 pixels** when they're added to the
|
||||
list and are collapsed back down to **0 pixels** before being removed from the list.
|
||||
There's also a nice fade-in and fade-out effect that occurs at the same time. All of this is handled
|
||||
by the CSS transition declarations at the top of the example code above.
|
||||
@@ -357,10 +357,10 @@ For more on CSS animations, see the
|
||||
## Animating `ngClass` with JavaScript
|
||||
|
||||
Let's add another animation to our application. Switching to our `phone-detail.html` page,
|
||||
we see that we have a nice thumbnail swapper. By clicking on the thumbnails listed on the page,
|
||||
we see that we have a nice thumbnail swapper. By hovering over the thumbnails listed on the page,
|
||||
the profile phone image changes. But how can we change this around to add animations?
|
||||
|
||||
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the
|
||||
Let's think about it first. Basically, when you hover over a thumbnail image, you're changing the
|
||||
state of the profile image to reflect the newly selected thumbnail image.
|
||||
The best way to specify state changes within HTML is to use classes.
|
||||
Much like before, how we used a CSS class to specify an animation, this time the animation will
|
||||
@@ -369,7 +369,7 @@ occur whenever the CSS class itself changes.
|
||||
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
|
||||
to the matching profile image and the animation plays.
|
||||
|
||||
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
|
||||
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
|
||||
have changed the way we display our large image:
|
||||
|
||||
__`app/partials/phone-detail.html`.__
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 76 KiB |
+1
-1
@@ -5,7 +5,7 @@ set -e
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
./run-tests.sh
|
||||
npm run test-i18n
|
||||
|
||||
node src/closureSlurper.js
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
|
||||
../node_modules/.bin/jasmine-node "$PARENT_DIR"/spec/
|
||||
@@ -4,6 +4,7 @@ findLocaleId = closureI18nExtractor.findLocaleId;
|
||||
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
|
||||
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
|
||||
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
|
||||
outputLocale = closureI18nExtractor.outputLocale;
|
||||
|
||||
|
||||
function newTestLocaleInfo() {
|
||||
@@ -72,7 +73,7 @@ describe("findLocaleId", function() {
|
||||
it("should throw an error otherwise", function() {
|
||||
expect(function() {
|
||||
findLocaleId("str", "otherwise")
|
||||
}).toThrow("unknown type in findLocaleId: otherwise");
|
||||
}).toThrowError("unknown type in findLocaleId: otherwise");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,7 +132,10 @@ describe("extractCurrencySymbols", function() {
|
||||
].join('\n');
|
||||
|
||||
var localeInfo = {};
|
||||
expect(extractCurrencySymbols(CONTENT)).toEqual({
|
||||
var currencySymbols = extractCurrencySymbols(CONTENT);
|
||||
expect(currencySymbols.GBP).toEqual([2, '£', 'GB£']);
|
||||
expect(currencySymbols.AOA).toEqual([2, 'Kz', 'Kz']);
|
||||
expect(currencySymbols).toEqual({
|
||||
'GBP':[2, '£', 'GB£'],
|
||||
'AOA':[2, 'Kz', 'Kz']
|
||||
});
|
||||
@@ -142,71 +146,71 @@ describe("extractCurrencySymbols", function() {
|
||||
describe("extractDateTimeSymbols", function() {
|
||||
it("should extract date time data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
].join('\n');
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
|
||||
'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
FIRSTDAYOFWEEK: 6,
|
||||
WEEKENDRANGE: [5, 6],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
ERAS: ['av. J.-C.', 'ap. J.-C.'],
|
||||
ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
|
||||
'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
FIRSTDAYOFWEEK: 6,
|
||||
WEEKENDRANGE: [5, 6],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
ERAS: ['av. J.-C.', 'ap. J.-C.'],
|
||||
ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
})
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pluralExtractor", function() {
|
||||
@@ -272,3 +276,10 @@ describe("serializeContent", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("outputLocale", function() {
|
||||
it("should render the correct locale ids", function() {
|
||||
var output = outputLocale(newTestLocaleInfo(), 'fr_CA');
|
||||
expect(output).toContain('"id": "fr-ca"');
|
||||
expect(output).toContain('"localeID": "fr_CA"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,10 +50,10 @@ function extractNumberSymbols(content, localeInfo, currencySymbols) {
|
||||
function extractCurrencySymbols(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
// var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
// currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
|
||||
return currencySymbols;
|
||||
return Object.assign({}, goog.i18n.currency.CurrencyInfoTier2, goog.i18n.currency.CurrencyInfo);
|
||||
}
|
||||
|
||||
function extractDateTimeSymbols(content, localeInfo) {
|
||||
@@ -79,7 +79,7 @@ function pluralExtractor(content, localeInfo) {
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
try {
|
||||
eval(contentText);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.log("Error in eval(contentText): " + e.stack);
|
||||
}
|
||||
if (!goog.i18n.pluralRules.select) {
|
||||
@@ -133,7 +133,7 @@ function canonicalizeForJsonStringify(unused_key, object) {
|
||||
|
||||
function serializeContent(localeObj) {
|
||||
return JSON.stringify(localeObj, canonicalizeForJsonStringify, ' ')
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(/"@@|@@"/g, '');
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ function outputLocale(localeInfo, localeID) {
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
localeObj.localeID = localeID;
|
||||
localeObj.id = correctedLocaleId(localeID);
|
||||
|
||||
var getDecimals = [
|
||||
@@ -201,10 +202,11 @@ function outputLocale(localeInfo, localeID) {
|
||||
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
|
||||
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
|
||||
pluralCat: localeObj.pluralCat,
|
||||
id: localeObj.id
|
||||
id: localeObj.id,
|
||||
localeID: localeID
|
||||
};
|
||||
|
||||
var content = serializeContent(localeInfo[localeID]);
|
||||
var content = serializeContent(localeObj);
|
||||
if (content.indexOf('getVF(') < 0) {
|
||||
getVF = '';
|
||||
}
|
||||
|
||||
@@ -2142,7 +2142,7 @@ queue}</string>
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr
|
||||
|
||||
\f0\fs22 \cf2 $scope\
|
||||
name='Wold'}</string>
|
||||
name='World'}</string>
|
||||
<key>VerticalPad</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
|
||||
+90
-95
@@ -8186,61 +8186,45 @@
|
||||
}
|
||||
},
|
||||
"jasmine-node": {
|
||||
"version": "1.14.5",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"coffee-script": {
|
||||
"version": "1.9.1"
|
||||
},
|
||||
"jasmine-growl-reporter": {
|
||||
"version": "0.0.3",
|
||||
"dependencies": {
|
||||
"growl": {
|
||||
"version": "1.7.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requirejs": {
|
||||
"version": "2.1.17"
|
||||
"version": "1.7.1"
|
||||
},
|
||||
"walkdir": {
|
||||
"version": "0.0.7"
|
||||
"version": "0.0.11"
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.8.3"
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"gaze": {
|
||||
"version": "0.3.4",
|
||||
"version": "0.5.2",
|
||||
"dependencies": {
|
||||
"minimatch": {
|
||||
"version": "0.2.14",
|
||||
"globule": {
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
"lodash": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fileset": {
|
||||
"version": "0.1.5",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "3.2.11",
|
||||
"version": "3.1.21",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
"graceful-fs": {
|
||||
"version": "1.2.3"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
"inherits": {
|
||||
"version": "1.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.2.14",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.7.3"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8250,6 +8234,17 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.5"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"jasmine-growl-reporter": {
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"growl": {
|
||||
"version": "1.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8625,21 +8620,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"ansi": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.1.0"
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "0.1.5"
|
||||
},
|
||||
@@ -8667,12 +8662,12 @@
|
||||
"commander": {
|
||||
"version": "2.9.0"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.10.1"
|
||||
},
|
||||
@@ -8685,42 +8680,42 @@
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1"
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc3"
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc3"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.8"
|
||||
},
|
||||
"gauge": {
|
||||
"version": "1.2.2"
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.2"
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"graceful-readlink": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
@@ -8730,11 +8725,14 @@
|
||||
"has-ansi": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.2"
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.2"
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3"
|
||||
@@ -8742,44 +8740,47 @@
|
||||
"http-signature": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4"
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.12.3"
|
||||
},
|
||||
"is-property": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.12.3"
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.2"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2"
|
||||
},
|
||||
"jsonpointer": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2"
|
||||
"lodash.pad": {
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"lodash.padright": {
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"lodash._basetostring": {
|
||||
"version": "3.0.1"
|
||||
@@ -8787,24 +8788,18 @@
|
||||
"lodash._createpadding": {
|
||||
"version": "3.6.1"
|
||||
},
|
||||
"lodash.pad": {
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"lodash.padleft": {
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"lodash.padright": {
|
||||
"version": "3.1.1"
|
||||
"mime-db": {
|
||||
"version": "1.19.0"
|
||||
},
|
||||
"lodash.repeat": {
|
||||
"version": "3.0.1"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.19.0"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.7"
|
||||
},
|
||||
"lodash.padleft": {
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
@@ -8814,15 +8809,15 @@
|
||||
"node-uuid": {
|
||||
"version": "1.4.7"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.0"
|
||||
},
|
||||
"once": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
@@ -8838,24 +8833,24 @@
|
||||
"request": {
|
||||
"version": "2.67.0"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.1.0"
|
||||
},
|
||||
"sntp": {
|
||||
"version": "1.0.9"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.1.0"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31"
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
@@ -8871,12 +8866,12 @@
|
||||
"tweetnacl": {
|
||||
"version": "0.13.2"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.3"
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.6"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.3"
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.1"
|
||||
},
|
||||
|
||||
Generated
+524
-531
File diff suppressed because it is too large
Load Diff
+5
-4
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "^1.4.0-beta.0",
|
||||
"branchVersion": "1.4.x",
|
||||
"branchPattern": "1.4.*",
|
||||
"distTag": "latest",
|
||||
"distTag": "previous_1_4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -16,7 +16,8 @@
|
||||
"scripts": {
|
||||
"preinstall": "node scripts/npm/check-node-modules.js --purge",
|
||||
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
|
||||
"commit": "git-cz"
|
||||
"commit": "git-cz",
|
||||
"test-i18n": "jasmine-node i18n/spec"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
@@ -51,7 +52,7 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-node": "~1.14.5",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
"jshint-stylish": "~1.0.0",
|
||||
"karma": "^0.13.19",
|
||||
|
||||
@@ -9,7 +9,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
|
||||
capabilitiesForBrowserStack({
|
||||
browserName: 'chrome',
|
||||
platform: 'MAC',
|
||||
version: '34'
|
||||
version: '49'
|
||||
}),
|
||||
capabilitiesForBrowserStack({
|
||||
browserName: 'firefox',
|
||||
@@ -18,7 +18,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
|
||||
capabilitiesForBrowserStack({
|
||||
browserName: 'safari',
|
||||
platform: 'MAC',
|
||||
version: '7'
|
||||
version: '9'
|
||||
})
|
||||
];
|
||||
} else {
|
||||
@@ -28,8 +28,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
|
||||
config.multiCapabilities = [
|
||||
capabilitiesForSauceLabs({
|
||||
browserName: 'chrome',
|
||||
platform: 'OS X 10.9',
|
||||
version: '34'
|
||||
platform: 'OS X 10.11',
|
||||
version: '48'
|
||||
}),
|
||||
capabilitiesForSauceLabs({
|
||||
browserName: 'firefox',
|
||||
@@ -37,8 +37,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
|
||||
}),
|
||||
capabilitiesForSauceLabs({
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
platform: 'OS X 10.11',
|
||||
version: '9'
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
|
||||
source scripts/jenkins/set-node-version.sh
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
scripts/jenkins/set-node-version.sh
|
||||
|
||||
# This is the default set of browsers to use on the CI server unless overridden via env variable
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
|
||||
@@ -35,7 +35,7 @@ function init {
|
||||
}
|
||||
|
||||
function build {
|
||||
./set-node-version.sh
|
||||
source ./set-node-version.sh
|
||||
cd ../..
|
||||
|
||||
npm install -g grunt-cli
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
source ~/.nvm/nvm.sh
|
||||
|
||||
# Use node.js at 4.2.x
|
||||
nvm install 4.2
|
||||
nvm install 4.4
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
"jqLiteInheritedData": false,
|
||||
"jqLiteBuildFragment": false,
|
||||
"jqLiteParseHTML": false,
|
||||
"jqLiteWrapNode": false,
|
||||
"getBooleanAttrName": false,
|
||||
"getAliasedAttrName": false,
|
||||
"createEventHandler": false,
|
||||
|
||||
+29
-13
@@ -236,7 +236,7 @@ function isArrayLike(obj) {
|
||||
*
|
||||
* Unlike ES262's
|
||||
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
|
||||
* Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
|
||||
* providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
|
||||
* return the value provided.
|
||||
*
|
||||
```js
|
||||
@@ -313,7 +313,7 @@ function forEachSorted(obj, iterator, context) {
|
||||
* @returns {function(*, string)}
|
||||
*/
|
||||
function reverseParams(iteratorFn) {
|
||||
return function(value, key) { iteratorFn(key, value); };
|
||||
return function(value, key) {iteratorFn(key, value);};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,12 +466,22 @@ noop.$inject = [];
|
||||
* functional style.
|
||||
*
|
||||
```js
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
|
||||
// E.g.
|
||||
function getResult(fn, input) {
|
||||
return (fn || angular.identity)(input);
|
||||
};
|
||||
|
||||
getResult(function(n) { return n * 2; }, 21); // returns 42
|
||||
getResult(null, 21); // returns 21
|
||||
getResult(undefined, 21); // returns 21
|
||||
```
|
||||
* @param {*} value to be returned.
|
||||
* @returns {*} the value passed in.
|
||||
*
|
||||
* @param {*} value to be returned.
|
||||
* @returns {*} the value passed in.
|
||||
*/
|
||||
function identity($) {return $;}
|
||||
identity.$inject = [];
|
||||
@@ -721,7 +731,7 @@ function isElement(node) {
|
||||
* @returns {object} in the form of {key1:true, key2:true, ...}
|
||||
*/
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(","), i;
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) {
|
||||
obj[items[i]] = true;
|
||||
}
|
||||
@@ -894,6 +904,8 @@ function copy(source, destination) {
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
destination.lastIndex = source.lastIndex;
|
||||
} else if (isBlob(source)) {
|
||||
destination = new source.constructor([source], {type: source.type});
|
||||
} else if (isFunction(source.cloneNode)) {
|
||||
destination = source.cloneNode(true);
|
||||
} else {
|
||||
@@ -1178,7 +1190,7 @@ function toJsonReplacer(key, value) {
|
||||
* @returns {string|undefined} JSON-ified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
if (typeof obj === 'undefined') return undefined;
|
||||
if (isUndefined(obj)) return undefined;
|
||||
if (!isNumber(pretty)) {
|
||||
pretty = pretty ? 2 : null;
|
||||
}
|
||||
@@ -1205,7 +1217,10 @@ function fromJson(json) {
|
||||
}
|
||||
|
||||
|
||||
var ALL_COLONS = /:/g;
|
||||
function timezoneToOffset(timezone, fallback) {
|
||||
// IE/Edge do not "understand" colon (`:`) in timezone
|
||||
timezone = timezone.replace(ALL_COLONS, '');
|
||||
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
|
||||
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
|
||||
}
|
||||
@@ -1220,8 +1235,9 @@ function addDateMinutes(date, minutes) {
|
||||
|
||||
function convertTimezoneToLocal(date, timezone, reverse) {
|
||||
reverse = reverse ? -1 : 1;
|
||||
var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
|
||||
return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
|
||||
var dateTimezoneOffset = date.getTimezoneOffset();
|
||||
var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
||||
return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
|
||||
}
|
||||
|
||||
|
||||
@@ -1240,7 +1256,7 @@ function startingTag(element) {
|
||||
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
|
||||
elemHtml.
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
|
||||
} catch (e) {
|
||||
return lowercase(elemHtml);
|
||||
}
|
||||
@@ -1588,7 +1604,7 @@ function bootstrap(element, modules, config) {
|
||||
//Encode angle brackets to prevent input from being sanitized to empty string #8683
|
||||
throw ngMinErr(
|
||||
'btstrpd',
|
||||
"App Already Bootstrapped with this Element '{0}'",
|
||||
"App already bootstrapped with this element '{0}'",
|
||||
tag.replace(/</,'<').replace(/>/,'>'));
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
if (window.angular.bootstrap) {
|
||||
//AngularJS is already loaded, so we can return here...
|
||||
console.log('WARNING: Tried to load angular more than once.');
|
||||
if (window.console) {
|
||||
console.log('WARNING: Tried to load angular more than once.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+22
-8
@@ -487,8 +487,20 @@ function annotate(fn, strictDi, name) {
|
||||
*
|
||||
* Register a **service constructor**, which will be invoked with `new` to create the service
|
||||
* instance.
|
||||
* This is short for registering a service where its provider's `$get` property is the service
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
* This is short for registering a service where its provider's `$get` property is a factory
|
||||
* function that returns an instance instantiated by the injector from the service constructor
|
||||
* function.
|
||||
*
|
||||
* Internally it looks a bit like this:
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* $get: function() {
|
||||
* return $injector.instantiate(constructor);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* You should use {@link auto.$provide#service $provide.service(class)} if you define your service
|
||||
* as a type/class.
|
||||
@@ -528,14 +540,13 @@ function annotate(fn, strictDi, name) {
|
||||
* @description
|
||||
*
|
||||
* Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
|
||||
* number, an array, an object or a function. This is short for registering a service where its
|
||||
* number, an array, an object or a function. This is short for registering a service where its
|
||||
* provider's `$get` property is a factory function that takes no arguments and returns the **value
|
||||
* service**.
|
||||
* service**. That also means it is not possible to inject other services into a value service.
|
||||
*
|
||||
* Value services are similar to constant services, except that they cannot be injected into a
|
||||
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
|
||||
* an Angular
|
||||
* {@link auto.$provide#decorator decorator}.
|
||||
* an Angular {@link auto.$provide#decorator decorator}.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {*} value The value.
|
||||
@@ -560,8 +571,11 @@ function annotate(fn, strictDi, name) {
|
||||
* @name $provide#constant
|
||||
* @description
|
||||
*
|
||||
* Register a **constant service**, such as a string, a number, an array, an object or a function,
|
||||
* with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
|
||||
* Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
|
||||
* a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
|
||||
* possible to inject other services into a constant.
|
||||
*
|
||||
* But unlike {@link auto.$provide#value value}, a constant can be
|
||||
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
|
||||
* be overridden by an Angular {@link auto.$provide#decorator decorator}.
|
||||
*
|
||||
|
||||
+11
-6
@@ -248,6 +248,16 @@ function jqLiteParseHTML(html, context) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function jqLiteWrapNode(node, wrapper) {
|
||||
var parent = node.parentNode;
|
||||
|
||||
if (parent) {
|
||||
parent.replaceChild(wrapper, node);
|
||||
}
|
||||
|
||||
wrapper.appendChild(node);
|
||||
}
|
||||
|
||||
|
||||
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
||||
var jqLiteContains = Node.prototype.contains || function(arg) {
|
||||
@@ -939,12 +949,7 @@ forEach({
|
||||
},
|
||||
|
||||
wrap: function(element, wrapNode) {
|
||||
wrapNode = jqLite(wrapNode).eq(0).clone()[0];
|
||||
var parent = element.parentNode;
|
||||
if (parent) {
|
||||
parent.replaceChild(wrapNode, element);
|
||||
}
|
||||
wrapNode.appendChild(element);
|
||||
jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
|
||||
},
|
||||
|
||||
remove: jqLiteRemove,
|
||||
|
||||
+3
-3
@@ -197,9 +197,9 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc method
|
||||
* @name angular.Module#decorator
|
||||
* @module ng
|
||||
* @param {string} The name of the service to decorate.
|
||||
* @param {Function} This function will be invoked when the service needs to be
|
||||
* instantiated and should return the decorated service instance.
|
||||
* @param {string} name The name of the service to decorate.
|
||||
* @param {Function} decorFn This function will be invoked when the service needs to be
|
||||
* instantiated and should return the decorated service instance.
|
||||
* @description
|
||||
* See {@link auto.$provide#decorator $provide.decorator()}.
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ function $AnchorScrollProvider() {
|
||||
* When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
|
||||
* current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
|
||||
* in the
|
||||
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
|
||||
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
|
||||
*
|
||||
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
|
||||
* match any anchor whenever it changes. This can be disabled by calling
|
||||
|
||||
+74
-54
@@ -166,35 +166,38 @@
|
||||
* is bound to the parent scope, via matching attributes on the directive's element:
|
||||
*
|
||||
* * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
|
||||
* always a string since DOM attributes are strings. If no `attr` name is specified then the
|
||||
* attribute name is assumed to be the same as the local name.
|
||||
* Given `<widget my-attr="hello {{name}}">` and widget definition
|
||||
* of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
|
||||
* the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
|
||||
* `localName` property on the widget scope. The `name` is read from the parent scope (not
|
||||
* component scope).
|
||||
* always a string since DOM attributes are strings. If no `attr` name is specified then the
|
||||
* attribute name is assumed to be the same as the local name. Given `<my-component
|
||||
* my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
|
||||
* the directive's scope property `localName` will reflect the interpolated value of `hello
|
||||
* {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
|
||||
* scope. The `name` is read from the parent scope (not the directive's scope).
|
||||
*
|
||||
* * `=` or `=attr` - set up bi-directional binding between a local scope property and the
|
||||
* parent scope property of name defined via the value of the `attr` attribute. If no `attr`
|
||||
* name is specified then the attribute name is assumed to be the same as the local name.
|
||||
* Given `<widget my-attr="parentModel">` and widget definition of
|
||||
* `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
|
||||
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
|
||||
* in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
|
||||
* scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
|
||||
* can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
|
||||
* you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
|
||||
* `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
|
||||
* * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
|
||||
* passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
|
||||
* If no `attr` name is specified then the attribute name is assumed to be the same as the local
|
||||
* name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
|
||||
* localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
|
||||
* value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
|
||||
* `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
|
||||
* `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
|
||||
* optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
|
||||
* will be thrown upon discovering changes to the local value, since it will be impossible to sync
|
||||
* them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
|
||||
* method is used for tracking changes, and the equality check is based on object identity.
|
||||
* However, if an object literal or an array literal is passed as the binding expression, the
|
||||
* equality check is done by value (using the {@link angular.equals} function). It's also possible
|
||||
* to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
|
||||
* `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
|
||||
*
|
||||
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
|
||||
* If no `attr` name is specified then the attribute name is assumed to be the same as the
|
||||
* local name. Given `<widget my-attr="count = count + value">` and widget definition of
|
||||
* `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
|
||||
* a function wrapper for the `count = count + value` expression. Often it's desirable to
|
||||
* pass data from the isolated scope via an expression to the parent scope, this can be
|
||||
* done by passing a map of local variable names and values into the expression wrapper fn.
|
||||
* For example, if the expression is `increment(amount)` then we can specify the amount value
|
||||
* by calling the `localFn` as `localFn({amount: 22})`.
|
||||
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
|
||||
* no `attr` name is specified then the attribute name is assumed to be the same as the local name.
|
||||
* Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
|
||||
* localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
|
||||
* the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
|
||||
* via an expression to the parent scope. This can be done by passing a map of local variable names
|
||||
* and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
|
||||
* then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
|
||||
*
|
||||
* In general it's possible to apply more than one directive to one element, but there might be limitations
|
||||
* depending on the type of scope required by the directives. The following points will help explain these limitations.
|
||||
@@ -385,7 +388,7 @@
|
||||
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** The compile function cannot handle directives that recursively use themselves in their
|
||||
* own templates or compile functions. Compiling these directives results in an infinite loop and a
|
||||
* own templates or compile functions. Compiling these directives results in an infinite loop and
|
||||
* stack overflow errors.
|
||||
*
|
||||
* This can be avoided by manually using $compile in the postLink function to imperatively compile
|
||||
@@ -584,10 +587,9 @@
|
||||
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
|
||||
* `link()` or `compile()` functions. It has a variety of uses.
|
||||
*
|
||||
* accessing *Normalized attribute names:*
|
||||
* Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
|
||||
* the attributes object allows for normalized access to
|
||||
* the attributes.
|
||||
* * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
|
||||
* 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
|
||||
* to the attributes.
|
||||
*
|
||||
* * *Directive inter-communication:* All directives share the same instance of the attributes
|
||||
* object which allows the directives to use the attributes object as inter directive
|
||||
@@ -775,13 +777,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
// 'on' and be composed of only English letters.
|
||||
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
|
||||
var bindingCache = createMap();
|
||||
|
||||
function parseIsolateBindings(scope, directiveName, isController) {
|
||||
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
|
||||
|
||||
var bindings = {};
|
||||
var bindings = createMap();
|
||||
|
||||
forEach(scope, function(definition, scopeName) {
|
||||
if (definition in bindingCache) {
|
||||
bindings[scopeName] = bindingCache[definition];
|
||||
return;
|
||||
}
|
||||
var match = definition.match(LOCAL_REGEXP);
|
||||
|
||||
if (!match) {
|
||||
@@ -799,6 +806,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
optional: match[3] === '?',
|
||||
attrName: match[4] || scopeName
|
||||
};
|
||||
if (match[4]) {
|
||||
bindingCache[definition] = bindings[scopeName];
|
||||
}
|
||||
});
|
||||
|
||||
return bindings;
|
||||
@@ -891,11 +901,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
directive.name = directive.name || name;
|
||||
directive.require = directive.require || (directive.controller && directive.name);
|
||||
directive.restrict = directive.restrict || 'EA';
|
||||
var bindings = directive.$$bindings =
|
||||
parseDirectiveBindings(directive, directive.name);
|
||||
if (isObject(bindings.isolateScope)) {
|
||||
directive.$$isolateBindings = bindings.isolateScope;
|
||||
}
|
||||
directive.$$moduleName = directiveFactory.$$moduleName;
|
||||
directives.push(directive);
|
||||
} catch (e) {
|
||||
@@ -1148,7 +1153,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
(nodeName === 'img' && key === 'src')) {
|
||||
// sanitize a[href] and img[src] values
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
} else if (nodeName === 'img' && key === 'srcset') {
|
||||
} else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
|
||||
// sanitize img[srcset] values
|
||||
var result = "";
|
||||
|
||||
@@ -1255,7 +1260,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
var startSymbol = $interpolate.startSymbol(),
|
||||
endSymbol = $interpolate.endSymbol(),
|
||||
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
|
||||
denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
|
||||
? identity
|
||||
: function denormalizeTemplate(template) {
|
||||
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
|
||||
@@ -1299,13 +1304,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// modify it.
|
||||
$compileNodes = jqLite($compileNodes);
|
||||
}
|
||||
|
||||
var NOT_EMPTY = /\S+/;
|
||||
|
||||
// We can not compile top level text elements since text nodes can be merged and we will
|
||||
// not be able to attach scope data to them, so we will wrap them in <span>
|
||||
forEach($compileNodes, function(node, index) {
|
||||
if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
|
||||
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
|
||||
for (var i = 0, len = $compileNodes.length; i < len; i++) {
|
||||
var domNode = $compileNodes[i];
|
||||
|
||||
if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
|
||||
jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var compositeLinkFn =
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
@@ -1984,15 +1995,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
|
||||
|
||||
// For directives with element transclusion the element is a comment,
|
||||
// but jQuery .data doesn't support attaching data to comment nodes as it's hard to
|
||||
// clean up (http://bugs.jquery.com/ticket/8335).
|
||||
// For directives with element transclusion the element is a comment.
|
||||
// In this case .data will not attach any data.
|
||||
// Instead, we save the controllers for the element in a local hash and attach to .data
|
||||
// later, once we have the actual element.
|
||||
elementControllers[directive.name] = controllerInstance;
|
||||
if (!hasElementTranscludeDirective) {
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
|
||||
}
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
|
||||
}
|
||||
return elementControllers;
|
||||
}
|
||||
@@ -2161,6 +2169,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (startAttrName) {
|
||||
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
|
||||
}
|
||||
if (!directive.$$bindings) {
|
||||
var bindings = directive.$$bindings =
|
||||
parseDirectiveBindings(directive, directive.name);
|
||||
if (isObject(bindings.isolateScope)) {
|
||||
directive.$$isolateBindings = bindings.isolateScope;
|
||||
}
|
||||
}
|
||||
tDirectives.push(directive);
|
||||
match = directive;
|
||||
}
|
||||
@@ -2625,10 +2640,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
});
|
||||
attrs.$$observers[attrName].$$scope = scope;
|
||||
if (isString(attrs[attrName])) {
|
||||
lastValue = attrs[attrName];
|
||||
if (isString(lastValue)) {
|
||||
// If the attribute has been provided then we trigger an interpolation to ensure
|
||||
// the value is there for use in the link fn
|
||||
destination[scopeName] = $interpolate(attrs[attrName])(scope);
|
||||
destination[scopeName] = $interpolate(lastValue)(scope);
|
||||
} else if (isBoolean(lastValue)) {
|
||||
// If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
|
||||
// the value to boolean rather than a string, so we special case this situation
|
||||
destination[scopeName] = lastValue;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2649,8 +2669,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
lastValue = destination[scopeName] = parentGet(scope);
|
||||
throw $compileMinErr('nonassign',
|
||||
"Expression '{0}' used with directive '{1}' is non-assignable!",
|
||||
attrs[attrName], directive.name);
|
||||
"Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
|
||||
attrs[attrName], attrName, directive.name);
|
||||
};
|
||||
lastValue = destination[scopeName] = parentGet(scope);
|
||||
var parentValueWatch = function parentValueWatch(parentValue) {
|
||||
|
||||
@@ -32,6 +32,12 @@ var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
|
||||
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
|
||||
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
|
||||
var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
|
||||
var PARTIAL_VALIDATION_TYPES = createMap();
|
||||
forEach('date,datetime-local,month,time,week'.split(','), function(type) {
|
||||
PARTIAL_VALIDATION_TYPES[type] = true;
|
||||
});
|
||||
|
||||
var inputType = {
|
||||
|
||||
/**
|
||||
@@ -387,7 +393,7 @@ var inputType = {
|
||||
}]);
|
||||
</script>
|
||||
<form name="myForm" ng-controller="DateController as dateCtrl">
|
||||
<label for="exampleInput">Pick a between 8am and 5pm:</label>
|
||||
<label for="exampleInput">Pick a time between 8am and 5pm:</label>
|
||||
<input type="time" id="exampleInput" name="input" ng-model="example.value"
|
||||
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
|
||||
<div role="alert">
|
||||
@@ -1118,6 +1124,8 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
});
|
||||
}
|
||||
|
||||
var timeout;
|
||||
|
||||
var listener = function(ev) {
|
||||
if (timeout) {
|
||||
$browser.defer.cancel(timeout);
|
||||
@@ -1147,8 +1155,6 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if ($sniffer.hasEvent('input')) {
|
||||
element.on('input', listener);
|
||||
} else {
|
||||
var timeout;
|
||||
|
||||
var deferListener = function(ev, input, origValue) {
|
||||
if (!timeout) {
|
||||
timeout = $browser.defer(function() {
|
||||
@@ -1180,6 +1186,26 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// or form autocomplete on newer browser, we need "change" event to catch it
|
||||
element.on('change', listener);
|
||||
|
||||
// Some native input types (date-family) have the ability to change validity without
|
||||
// firing any input/change events.
|
||||
// For these event types, when native validators are present and the browser supports the type,
|
||||
// check for validity changes on various DOM events.
|
||||
if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
|
||||
element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
|
||||
if (!timeout) {
|
||||
var validity = this[VALIDITY_STATE_PROPERTY];
|
||||
var origBadInput = validity.badInput;
|
||||
var origTypeMismatch = validity.typeMismatch;
|
||||
timeout = $browser.defer(function() {
|
||||
timeout = null;
|
||||
if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
|
||||
listener(ev);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctrl.$render = function() {
|
||||
// Workaround for Firefox validation #12102.
|
||||
var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
|
||||
|
||||
@@ -188,8 +188,9 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
|
||||
restrict: 'A',
|
||||
compile: function ngBindHtmlCompile(tElement, tAttrs) {
|
||||
var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
|
||||
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
|
||||
return (value || '').toString();
|
||||
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
|
||||
// Unwrap the value to compare the actual inner safe value, not the wrapper object.
|
||||
return $sce.valueOf(val);
|
||||
});
|
||||
$compile.$$addBindingClass(tElement);
|
||||
|
||||
@@ -197,9 +198,9 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
|
||||
$compile.$$addBindingInfo(element, attr.ngBindHtml);
|
||||
|
||||
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
|
||||
// we re-evaluate the expr because we want a TrustedValueHolderType
|
||||
// for $sce, not a string
|
||||
element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
|
||||
// The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
|
||||
var value = ngBindHtmlGetter(scope);
|
||||
element.html($sce.getTrustedHtml(value) || '');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,11 @@ function classDirective(name, selector) {
|
||||
updateClasses(oldClasses, newClasses);
|
||||
}
|
||||
}
|
||||
oldVal = shallowCopy(newVal);
|
||||
if (isArray(newVal)) {
|
||||
oldVal = newVal.map(function(v) { return shallowCopy(v); });
|
||||
} else {
|
||||
oldVal = shallowCopy(newVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -921,6 +921,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* - {@link ng.directive:select select}
|
||||
* - {@link ng.directive:textarea textarea}
|
||||
*
|
||||
* # Complex Models (objects or collections)
|
||||
*
|
||||
* By default, `ngModel` watches the model by reference, not value. This is important to know when
|
||||
* binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
|
||||
* object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
|
||||
*
|
||||
* The model must be assigned an entirely new object or collection before a re-rendering will occur.
|
||||
*
|
||||
* Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
|
||||
* - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
|
||||
* if the select is given the `multiple` attribute.
|
||||
*
|
||||
* The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
|
||||
* first level of the object (or only changing the properties of an item in the collection if it's an array) will still
|
||||
* not trigger a re-rendering of the model.
|
||||
*
|
||||
* # CSS classes
|
||||
* The following CSS classes are added and removed on the associated input/select/textarea element
|
||||
* depending on the validity of the model.
|
||||
|
||||
@@ -472,14 +472,20 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var option = options.getOptionFromViewValue(value);
|
||||
|
||||
if (option && !option.disabled) {
|
||||
// Don't update the option when it is already selected.
|
||||
// For example, the browser will select the first option by default. In that case,
|
||||
// most properties are set automatically - except the `selected` attribute, which we
|
||||
// set always
|
||||
|
||||
if (selectElement[0].value !== option.selectValue) {
|
||||
removeUnknownOption();
|
||||
removeEmptyOption();
|
||||
|
||||
selectElement[0].value = option.selectValue;
|
||||
option.element.selected = true;
|
||||
option.element.setAttribute('selected', 'selected');
|
||||
}
|
||||
|
||||
option.element.setAttribute('selected', 'selected');
|
||||
} else {
|
||||
if (value === null || providedEmptyOption) {
|
||||
removeUnknownOption();
|
||||
|
||||
@@ -508,7 +508,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
|
||||
if (getBlockStart(block) != nextNode) {
|
||||
// existing item which got moved
|
||||
$animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
|
||||
$animate.move(getBlockNodes(block.clone), null, previousNode);
|
||||
}
|
||||
previousNode = getBlockEnd(block);
|
||||
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
|
||||
@@ -520,8 +520,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
var endNode = ngRepeatEndComment.cloneNode(false);
|
||||
clone[clone.length++] = endNode;
|
||||
|
||||
// TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
|
||||
$animate.enter(clone, null, jqLite(previousNode));
|
||||
$animate.enter(clone, null, previousNode);
|
||||
previousNode = endNode;
|
||||
// Note: We only need the first/last node of the cloned nodes.
|
||||
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
|
||||
|
||||
@@ -80,6 +80,9 @@ var SelectController =
|
||||
|
||||
// Tell the select control that an option, with the given value, has been added
|
||||
self.addOption = function(value, element) {
|
||||
// Skip comment nodes, as they only pollute the `optionsMap`
|
||||
if (element[0].nodeType === NODE_TYPE_COMMENT) return;
|
||||
|
||||
assertNotHasOwnProperty(value, '"option value"');
|
||||
if (value === '') {
|
||||
self.emptyOption = element;
|
||||
@@ -452,7 +455,6 @@ var optionDirective = ['$interpolate', function($interpolate) {
|
||||
restrict: 'E',
|
||||
priority: 100,
|
||||
compile: function(element, attr) {
|
||||
|
||||
if (isDefined(attr.value)) {
|
||||
// If the value attribute is defined, check if it contains an interpolation
|
||||
var interpolateValueFn = $interpolate(attr.value, true);
|
||||
@@ -466,7 +468,6 @@ var optionDirective = ['$interpolate', function($interpolate) {
|
||||
}
|
||||
|
||||
return function(scope, element, attr) {
|
||||
|
||||
// This is an optimization over using ^^ since we don't want to have to search
|
||||
// all the way to the root of the DOM for every single option element
|
||||
var selectCtrlName = '$selectController',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngRequired
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
*
|
||||
@@ -15,7 +16,7 @@
|
||||
* for more info.
|
||||
*
|
||||
* The validator will set the `required` error key to true if the `required` attribute is set and
|
||||
* calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty` with the
|
||||
* calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
|
||||
* {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
|
||||
* `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
|
||||
* custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
|
||||
|
||||
@@ -85,7 +85,7 @@ function currencyFilter($locale) {
|
||||
* Formats a number as text.
|
||||
*
|
||||
* If the input is null or undefined, it will just be returned.
|
||||
* If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
|
||||
* If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
|
||||
* If the input is not a number an empty string is returned.
|
||||
*
|
||||
*
|
||||
@@ -93,7 +93,9 @@ function currencyFilter($locale) {
|
||||
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
|
||||
* If this is not provided then the fraction size is computed from the current locale's number
|
||||
* formatting pattern. In the case of the default locale, it will be 3.
|
||||
* @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
|
||||
* @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
|
||||
* locale (e.g., in the en_US locale it will have "." as the decimal separator and
|
||||
* include "," group separators after each third digit).
|
||||
*
|
||||
* @example
|
||||
<example module="numberFilterExample">
|
||||
@@ -175,7 +177,7 @@ function parse(numStr) {
|
||||
}
|
||||
|
||||
// Count the number of leading zeros.
|
||||
for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
|
||||
for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
|
||||
|
||||
if (i == (zeros = numStr.length)) {
|
||||
// The digits are all zero.
|
||||
@@ -296,7 +298,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
|
||||
// extract decimals digits
|
||||
if (integerLen > 0) {
|
||||
decimals = digits.splice(integerLen);
|
||||
decimals = digits.splice(integerLen, digits.length);
|
||||
} else {
|
||||
decimals = digits;
|
||||
digits = [0];
|
||||
@@ -304,11 +306,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
|
||||
// format the integer digits with grouping separators
|
||||
var groups = [];
|
||||
if (digits.length > pattern.lgSize) {
|
||||
groups.unshift(digits.splice(-pattern.lgSize).join(''));
|
||||
if (digits.length >= pattern.lgSize) {
|
||||
groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
|
||||
}
|
||||
while (digits.length > pattern.gSize) {
|
||||
groups.unshift(digits.splice(-pattern.gSize).join(''));
|
||||
groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
|
||||
}
|
||||
if (digits.length) {
|
||||
groups.unshift(digits.join(''));
|
||||
@@ -607,13 +609,13 @@ function dateFilter($locale) {
|
||||
|
||||
var dateTimezoneOffset = date.getTimezoneOffset();
|
||||
if (timezone) {
|
||||
dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
|
||||
dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
||||
date = convertTimezoneToLocal(date, timezone, true);
|
||||
}
|
||||
forEach(parts, function(value) {
|
||||
fn = DATE_FORMATS[value];
|
||||
text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
|
||||
: value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
|
||||
: value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
|
||||
});
|
||||
|
||||
return text;
|
||||
|
||||
+48
-32
@@ -257,10 +257,9 @@ function $HttpProvider() {
|
||||
*
|
||||
* Object containing default values for all {@link ng.$http $http} requests.
|
||||
*
|
||||
* - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
|
||||
* that will provide the cache for all requests who set their `cache` property to `true`.
|
||||
* If you set the `defaults.cache = false` then only requests that specify their own custom
|
||||
* cache object will be cached. See {@link $http#caching $http Caching} for more information.
|
||||
* - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
|
||||
* {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
|
||||
* by default. See {@link $http#caching $http Caching} for more information.
|
||||
*
|
||||
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
|
||||
* Defaults value is `'XSRF-TOKEN'`.
|
||||
@@ -551,6 +550,15 @@ function $HttpProvider() {
|
||||
* the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
|
||||
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
|
||||
* That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
|
||||
* For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
|
||||
* function will be reflected on the scope and in any templates where the object is data-bound.
|
||||
* To prevent this, transform functions should have no side-effects.
|
||||
* If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
|
||||
* </div>
|
||||
*
|
||||
* ### Default Transformations
|
||||
*
|
||||
* The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
|
||||
@@ -608,26 +616,35 @@ function $HttpProvider() {
|
||||
*
|
||||
* ## Caching
|
||||
*
|
||||
* To enable caching, set the request configuration `cache` property to `true` (to use default
|
||||
* cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
|
||||
* When the cache is enabled, `$http` stores the response from the server in the specified
|
||||
* cache. The next time the same request is made, the response is served from the cache without
|
||||
* sending a request to the server.
|
||||
* {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
|
||||
* set the config.cache value or the default cache value to TRUE or to a cache object (created
|
||||
* with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
|
||||
* precedence over the default cache value.
|
||||
*
|
||||
* Note that even if the response is served from cache, delivery of the data is asynchronous in
|
||||
* the same way that real requests are.
|
||||
* In order to:
|
||||
* * cache all responses - set the default cache value to TRUE or to a cache object
|
||||
* * cache a specific response - set config.cache value to TRUE or to a cache object
|
||||
*
|
||||
* If there are multiple GET requests for the same URL that should be cached using the same
|
||||
* cache, but the cache is not populated yet, only one request to the server will be made and
|
||||
* the remaining requests will be fulfilled using the response from the first request.
|
||||
* If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
|
||||
* then the default `$cacheFactory($http)` object is used.
|
||||
*
|
||||
* You can change the default cache to a new object (built with
|
||||
* {@link ng.$cacheFactory `$cacheFactory`}) by updating the
|
||||
* {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
|
||||
* their `cache` property to `true` will now use this cache object.
|
||||
* The default cache value can be set by updating the
|
||||
* {@link ng.$http#defaults `$http.defaults.cache`} property or the
|
||||
* {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
|
||||
*
|
||||
* When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
|
||||
* the relevant cache object. The next time the same request is made, the response is returned
|
||||
* from the cache without sending a request to the server.
|
||||
*
|
||||
* Take note that:
|
||||
*
|
||||
* * Only GET and JSONP requests are cached.
|
||||
* * The cache key is the request URL including search parameters; headers are not considered.
|
||||
* * Cached responses are returned asynchronously, in the same way as responses from the server.
|
||||
* * If multiple identical requests are made using the same cache, which is not yet populated,
|
||||
* one request will be made to the server and remaining requests will return the same response.
|
||||
* * A cache-control header on the response does not affect if or how responses are cached.
|
||||
*
|
||||
* If you set the default cache to `false` then only requests that specify their own custom
|
||||
* cache object will be cached.
|
||||
*
|
||||
* ## Interceptors
|
||||
*
|
||||
@@ -749,13 +766,13 @@ function $HttpProvider() {
|
||||
*
|
||||
* ### Cross Site Request Forgery (XSRF) Protection
|
||||
*
|
||||
* [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
|
||||
* an unauthorized site can gain your user's private data. Angular provides a mechanism
|
||||
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
||||
* (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
|
||||
* JavaScript that runs on your domain could read the cookie, your server can be assured that
|
||||
* the XHR came from JavaScript running on your domain. The header will not be set for
|
||||
* cross-domain requests.
|
||||
* [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
|
||||
* which the attacker can trick an authenticated user into unknowingly executing actions on your
|
||||
* website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
|
||||
* $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
|
||||
* header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
|
||||
* cookie, your server can be assured that the XHR came from JavaScript running on your domain.
|
||||
* The header will not be set for cross-domain requests.
|
||||
*
|
||||
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
||||
* cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
|
||||
@@ -797,7 +814,7 @@ function $HttpProvider() {
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body, headers and status and returns its transformed (typically deserialized) version.
|
||||
* See {@link ng.$http#overriding-the-default-transformations-per-request
|
||||
* Overriding the Default TransformationjqLiks}
|
||||
* Overriding the Default Transformations}
|
||||
* - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
|
||||
* prepare the string representation of request parameters (specified as an object).
|
||||
* If specified as string, it is interpreted as function registered with the
|
||||
@@ -805,10 +822,9 @@ function $HttpProvider() {
|
||||
* by registering it as a {@link auto.$provide#service service}.
|
||||
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
|
||||
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
|
||||
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
* caching.
|
||||
* - **cache** – `{boolean|Object}` – A boolean value or object created with
|
||||
* {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
|
||||
* See {@link $http#caching $http Caching} for more information.
|
||||
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
|
||||
* that should abort the request when resolved.
|
||||
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
|
||||
@@ -199,6 +199,11 @@ function $InterpolateProvider() {
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* @knownIssue
|
||||
* It is currently not possible for an interpolated expression to contain the interpolation end
|
||||
* symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
|
||||
* an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
|
||||
*
|
||||
* @param {string} text The text with markup to interpolate.
|
||||
* @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
|
||||
* embedded expression in order to return an interpolation function. Strings with no
|
||||
|
||||
+1
-1
@@ -482,7 +482,7 @@ var locationPrototype = {
|
||||
* ```
|
||||
*
|
||||
* @param {(string|number)=} path New path
|
||||
* @return {string} path
|
||||
* @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
|
||||
*/
|
||||
path: locationGetterSetter('$$path', function(path) {
|
||||
path = path !== null ? path.toString() : '';
|
||||
|
||||
+51
-6
@@ -988,6 +988,9 @@ ASTCompiler.prototype = {
|
||||
intoId = intoId || this.nextId();
|
||||
self.recurse(ast.object, left, undefined, function() {
|
||||
self.if_(self.notNull(left), function() {
|
||||
if (create && create !== 1) {
|
||||
self.addEnsureSafeAssignContext(left);
|
||||
}
|
||||
if (ast.computed) {
|
||||
right = self.nextId();
|
||||
self.recurse(ast.property, right);
|
||||
@@ -1602,8 +1605,11 @@ ASTInterpreter.prototype = {
|
||||
rhs = right(scope, locals, assign, inputs);
|
||||
rhs = getStringValue(rhs);
|
||||
ensureSafeMemberName(rhs, expression);
|
||||
if (create && create !== 1 && lhs && !(lhs[rhs])) {
|
||||
lhs[rhs] = {};
|
||||
if (create && create !== 1) {
|
||||
ensureSafeAssignContext(lhs);
|
||||
if (lhs && !(lhs[rhs])) {
|
||||
lhs[rhs] = {};
|
||||
}
|
||||
}
|
||||
value = lhs[rhs];
|
||||
ensureSafeObject(value, expression);
|
||||
@@ -1618,8 +1624,11 @@ ASTInterpreter.prototype = {
|
||||
nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
|
||||
return function(scope, locals, assign, inputs) {
|
||||
var lhs = left(scope, locals, assign, inputs);
|
||||
if (create && create !== 1 && lhs && !(lhs[right])) {
|
||||
lhs[right] = {};
|
||||
if (create && create !== 1) {
|
||||
ensureSafeAssignContext(lhs);
|
||||
if (lhs && !(lhs[right])) {
|
||||
lhs[right] = {};
|
||||
}
|
||||
}
|
||||
var value = lhs != null ? lhs[right] : undefined;
|
||||
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
|
||||
@@ -1735,10 +1744,19 @@ function $ParseProvider() {
|
||||
csp: noUnsafeEval,
|
||||
expensiveChecks: true
|
||||
};
|
||||
var runningChecksEnabled = false;
|
||||
|
||||
return function $parse(exp, interceptorFn, expensiveChecks) {
|
||||
$parse.$$runningExpensiveChecks = function() {
|
||||
return runningChecksEnabled;
|
||||
};
|
||||
|
||||
return $parse;
|
||||
|
||||
function $parse(exp, interceptorFn, expensiveChecks) {
|
||||
var parsedExpression, oneTime, cacheKey;
|
||||
|
||||
expensiveChecks = expensiveChecks || runningChecksEnabled;
|
||||
|
||||
switch (typeof exp) {
|
||||
case 'string':
|
||||
exp = exp.trim();
|
||||
@@ -1764,6 +1782,9 @@ function $ParseProvider() {
|
||||
} else if (parsedExpression.inputs) {
|
||||
parsedExpression.$$watchDelegate = inputsWatchDelegate;
|
||||
}
|
||||
if (expensiveChecks) {
|
||||
parsedExpression = expensiveChecksInterceptor(parsedExpression);
|
||||
}
|
||||
cache[cacheKey] = parsedExpression;
|
||||
}
|
||||
return addInterceptor(parsedExpression, interceptorFn);
|
||||
@@ -1774,7 +1795,31 @@ function $ParseProvider() {
|
||||
default:
|
||||
return addInterceptor(noop, interceptorFn);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function expensiveChecksInterceptor(fn) {
|
||||
if (!fn) return fn;
|
||||
expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
|
||||
expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
|
||||
expensiveCheckFn.constant = fn.constant;
|
||||
expensiveCheckFn.literal = fn.literal;
|
||||
for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
|
||||
fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
|
||||
}
|
||||
expensiveCheckFn.inputs = fn.inputs;
|
||||
|
||||
return expensiveCheckFn;
|
||||
|
||||
function expensiveCheckFn(scope, locals, assign, inputs) {
|
||||
var expensiveCheckOldValue = runningChecksEnabled;
|
||||
runningChecksEnabled = true;
|
||||
try {
|
||||
return fn(scope, locals, assign, inputs);
|
||||
} finally {
|
||||
runningChecksEnabled = expensiveCheckOldValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
|
||||
|
||||
|
||||
+3
-3
@@ -13,15 +13,15 @@
|
||||
* [Kris Kowal's Q](https://github.com/kriskowal/q).
|
||||
*
|
||||
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
|
||||
* implementations, and the other which resembles ES6 promises to some degree.
|
||||
* implementations, and the other which resembles ES6 (ES2015) promises to some degree.
|
||||
*
|
||||
* # $q constructor
|
||||
*
|
||||
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
|
||||
* function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
|
||||
* function as the first argument. This is similar to the native Promise implementation from ES6,
|
||||
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
*
|
||||
* While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
|
||||
* While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
|
||||
* available yet.
|
||||
*
|
||||
* It can be used like so:
|
||||
|
||||
+7
-4
@@ -744,7 +744,7 @@ function $RootScopeProvider() {
|
||||
*
|
||||
*/
|
||||
$digest: function() {
|
||||
var watch, value, last,
|
||||
var watch, value, last, fn, get,
|
||||
watchers,
|
||||
length,
|
||||
dirty, ttl = TTL,
|
||||
@@ -790,7 +790,8 @@ function $RootScopeProvider() {
|
||||
// Most common watches are on primitives, in which case we can short
|
||||
// circuit it with === operator, only when === fails do we use .equals
|
||||
if (watch) {
|
||||
if ((value = watch.get(current)) !== (last = watch.last) &&
|
||||
get = watch.get;
|
||||
if ((value = get(current)) !== (last = watch.last) &&
|
||||
!(watch.eq
|
||||
? equals(value, last)
|
||||
: (typeof value === 'number' && typeof last === 'number'
|
||||
@@ -798,7 +799,8 @@ function $RootScopeProvider() {
|
||||
dirty = true;
|
||||
lastDirtyWatch = watch;
|
||||
watch.last = watch.eq ? copy(value, null) : value;
|
||||
watch.fn(value, ((last === initWatchVal) ? value : last), current);
|
||||
fn = watch.fn;
|
||||
fn(value, ((last === initWatchVal) ? value : last), current);
|
||||
if (ttl < 5) {
|
||||
logIdx = 4 - ttl;
|
||||
if (!watchLog[logIdx]) watchLog[logIdx] = [];
|
||||
@@ -998,7 +1000,7 @@ function $RootScopeProvider() {
|
||||
});
|
||||
}
|
||||
|
||||
asyncQueue.push({scope: this, expression: expr, locals: locals});
|
||||
asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
|
||||
},
|
||||
|
||||
$$postDigest: function(fn) {
|
||||
@@ -1090,6 +1092,7 @@ function $RootScopeProvider() {
|
||||
$applyAsync: function(expr) {
|
||||
var scope = this;
|
||||
expr && applyAsyncQueue.push($applyAsyncExpression);
|
||||
expr = $parse(expr);
|
||||
scheduleApplyAsync();
|
||||
|
||||
function $applyAsyncExpression() {
|
||||
|
||||
+15
-13
@@ -144,13 +144,15 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* Note: **an empty whitelist array will block all URLs**!
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** an empty whitelist array will block all URLs!
|
||||
* </div>
|
||||
*
|
||||
* @return {Array} the currently set whitelist array.
|
||||
*
|
||||
@@ -173,17 +175,17 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
*
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
*
|
||||
* @return {Array} the currently set blacklist array.
|
||||
*
|
||||
|
||||
@@ -31,7 +31,7 @@ function $TemplateRequestProvider() {
|
||||
// are included in there. This also makes Angular accept any script
|
||||
// directive, no matter its name. However, we still need to unwrap trusted
|
||||
// types.
|
||||
if (!isString(tpl) || !$templateCache.get(tpl)) {
|
||||
if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
|
||||
tpl = $sce.getTrustedResourceUrl(tpl);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"REMOVE_CLASS_SUFFIX": false,
|
||||
"EVENT_CLASS_PREFIX": false,
|
||||
"ACTIVE_CLASS_SUFFIX": false,
|
||||
"PREPARE_CLASS_SUFFIX": false,
|
||||
|
||||
"TRANSITION_DURATION_PROP": false,
|
||||
"TRANSITION_DELAY_PROP": false,
|
||||
@@ -48,7 +49,7 @@
|
||||
"assertArg": false,
|
||||
"isPromiseLike": false,
|
||||
"mergeClasses": false,
|
||||
"mergeAnimationOptions": false,
|
||||
"mergeAnimationDetails": false,
|
||||
"prepareAnimationOptions": false,
|
||||
"applyAnimationStyles": false,
|
||||
"applyAnimationFromStyles": false,
|
||||
|
||||
@@ -1,15 +1,100 @@
|
||||
'use strict';
|
||||
|
||||
var $$AnimateChildrenDirective = [function() {
|
||||
return function(scope, element, attrs) {
|
||||
var val = attrs.ngAnimateChildren;
|
||||
if (angular.isString(val) && val.length === 0) { //empty attribute
|
||||
element.data(NG_ANIMATE_CHILDREN_DATA, true);
|
||||
} else {
|
||||
attrs.$observe('ngAnimateChildren', function(value) {
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngAnimateChildren
|
||||
* @restrict AE
|
||||
* @element ANY
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* ngAnimateChildren allows you to specify that children of this element should animate even if any
|
||||
* of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move`
|
||||
* (structural) animation, child elements that also have an active structural animation are not animated.
|
||||
*
|
||||
* Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
|
||||
*
|
||||
*
|
||||
* @param {string} ngAnimateChildren If the value is empty, `true` or `on`,
|
||||
* then child animations are allowed. If the value is `false`, child animations are not allowed.
|
||||
*
|
||||
* @example
|
||||
* <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="mainController as main">
|
||||
<label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>
|
||||
<label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>
|
||||
<hr>
|
||||
<div ng-animate-children="{{main.animateChildren}}">
|
||||
<div ng-if="main.enterElement" class="container">
|
||||
List of items:
|
||||
<div ng-repeat="item in [0, 1, 2, 3]" class="item">Item {{item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
|
||||
.container.ng-enter,
|
||||
.container.ng-leave {
|
||||
transition: all ease 1.5s;
|
||||
}
|
||||
|
||||
.container.ng-enter,
|
||||
.container.ng-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.container.ng-leave,
|
||||
.container.ng-enter-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: firebrick;
|
||||
color: #FFF;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item.ng-enter,
|
||||
.item.ng-leave {
|
||||
transition: transform 1.5s ease;
|
||||
}
|
||||
|
||||
.item.ng-enter {
|
||||
transform: translateX(50px);
|
||||
}
|
||||
|
||||
.item.ng-enter-active {
|
||||
transform: translateX(0);
|
||||
}
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('ngAnimateChildren', ['ngAnimate'])
|
||||
.controller('mainController', function() {
|
||||
this.animateChildren = false;
|
||||
this.enterElement = false;
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
|
||||
return {
|
||||
link: function(scope, element, attrs) {
|
||||
var val = attrs.ngAnimateChildren;
|
||||
if (angular.isString(val) && val.length === 0) { //empty attribute
|
||||
element.data(NG_ANIMATE_CHILDREN_DATA, true);
|
||||
} else {
|
||||
// Interpolate and set the value, so that it is available to
|
||||
// animations that run right after compilation
|
||||
setData($interpolate(val)(scope));
|
||||
attrs.$observe('ngAnimateChildren', setData);
|
||||
}
|
||||
|
||||
function setData(value) {
|
||||
value = value === 'on' || value === 'true';
|
||||
element.data(NG_ANIMATE_CHILDREN_DATA, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -756,6 +756,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
element.off(events.join(' '), onAnimationProgress);
|
||||
}
|
||||
|
||||
//Cancel the fallback closing timeout and remove the timer data
|
||||
var animationTimerData = element.data(ANIMATE_TIMER_KEY);
|
||||
if (animationTimerData) {
|
||||
$timeout.cancel(animationTimerData[0].timer);
|
||||
element.removeData(ANIMATE_TIMER_KEY);
|
||||
}
|
||||
|
||||
// if the preparation function fails then the promise is not setup
|
||||
if (runner) {
|
||||
runner.complete(!rejected);
|
||||
|
||||
@@ -42,22 +42,21 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
});
|
||||
}
|
||||
|
||||
function hasAnimationClasses(options, and) {
|
||||
options = options || {};
|
||||
var a = (options.addClass || '').length > 0;
|
||||
var b = (options.removeClass || '').length > 0;
|
||||
function hasAnimationClasses(animation, and) {
|
||||
var a = (animation.addClass || '').length > 0;
|
||||
var b = (animation.removeClass || '').length > 0;
|
||||
return and ? a && b : a || b;
|
||||
}
|
||||
|
||||
rules.join.push(function(element, newAnimation, currentAnimation) {
|
||||
// if the new animation is class-based then we can just tack that on
|
||||
return !newAnimation.structural && hasAnimationClasses(newAnimation.options);
|
||||
return !newAnimation.structural && hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
// there is no need to animate anything if no classes are being added and
|
||||
// there is no structural animation that will be triggered
|
||||
return !newAnimation.structural && !hasAnimationClasses(newAnimation.options);
|
||||
return !newAnimation.structural && !hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
@@ -83,19 +82,17 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
|
||||
|
||||
var nA = newAnimation.options.addClass;
|
||||
var nR = newAnimation.options.removeClass;
|
||||
var cA = currentAnimation.options.addClass;
|
||||
var cR = currentAnimation.options.removeClass;
|
||||
var nA = newAnimation.addClass;
|
||||
var nR = newAnimation.removeClass;
|
||||
var cA = currentAnimation.addClass;
|
||||
var cR = currentAnimation.removeClass;
|
||||
|
||||
// early detection to save the global CPU shortage :)
|
||||
if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (hasMatchingClasses(nA, cR)) || (hasMatchingClasses(nR, cA));
|
||||
return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
|
||||
});
|
||||
|
||||
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
|
||||
@@ -106,6 +103,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
var activeAnimationsLookup = new $$HashMap();
|
||||
var disabledElementsLookup = new $$HashMap();
|
||||
var animationsEnabled = null;
|
||||
// $document might be mocked out in tests and won't include a real document.
|
||||
// Providing an empty object with hidden = true will prevent animations from running
|
||||
var rawDocument = $document[0] || {hidden: true};
|
||||
|
||||
function postDigestTaskFactory() {
|
||||
var postDigestCalled = false;
|
||||
@@ -167,8 +167,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
|
||||
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
|
||||
|
||||
function normalizeAnimationOptions(element, options) {
|
||||
return mergeAnimationOptions(element, options, {});
|
||||
function normalizeAnimationDetails(element, animation) {
|
||||
return mergeAnimationDetails(element, animation, {});
|
||||
}
|
||||
|
||||
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
||||
@@ -334,12 +334,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
|
||||
var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
|
||||
|
||||
var documentHidden = rawDocument.hidden;
|
||||
|
||||
// this is a hard disable of all animations for the application or on
|
||||
// the element itself, therefore there is no need to continue further
|
||||
// past this point if not enabled
|
||||
// Animations are also disabled if the document is currently hidden (page is not visible
|
||||
// to the user), because browsers slow down or do not flush calls to requestAnimationFrame
|
||||
var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node);
|
||||
var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
|
||||
var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
|
||||
var hasExistingAnimation = !!existingAnimation.state;
|
||||
|
||||
@@ -350,7 +352,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
if (skipAnimations) {
|
||||
// Callbacks should fire even if the document is hidden (regression fix for issue #14120)
|
||||
if (documentHidden) notifyProgress(runner, event, 'start');
|
||||
close();
|
||||
if (documentHidden) notifyProgress(runner, event, 'close');
|
||||
return runner;
|
||||
}
|
||||
|
||||
@@ -362,6 +367,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
structural: isStructural,
|
||||
element: element,
|
||||
event: event,
|
||||
addClass: options.addClass,
|
||||
removeClass: options.removeClass,
|
||||
close: close,
|
||||
options: options,
|
||||
runner: runner
|
||||
@@ -374,11 +381,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
close();
|
||||
return runner;
|
||||
} else {
|
||||
mergeAnimationOptions(element, existingAnimation.options, options);
|
||||
mergeAnimationDetails(element, existingAnimation, newAnimation);
|
||||
return existingAnimation.runner;
|
||||
}
|
||||
}
|
||||
|
||||
var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
|
||||
if (cancelAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
@@ -393,7 +399,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
existingAnimation.close();
|
||||
} else {
|
||||
// this will merge the new animation options into existing animation options
|
||||
mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
|
||||
mergeAnimationDetails(element, existingAnimation, newAnimation);
|
||||
|
||||
return existingAnimation.runner;
|
||||
}
|
||||
} else {
|
||||
@@ -403,12 +410,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
|
||||
if (joinAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
normalizeAnimationOptions(element, options);
|
||||
normalizeAnimationDetails(element, newAnimation);
|
||||
} else {
|
||||
applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
|
||||
|
||||
event = newAnimation.event = existingAnimation.event;
|
||||
options = mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
|
||||
options = mergeAnimationDetails(element, existingAnimation, newAnimation);
|
||||
|
||||
//we return the same runner since only the option values of this animation will
|
||||
//be fed into the `existingAnimation`.
|
||||
@@ -419,7 +426,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
} else {
|
||||
// normalization in this case means that it removes redundant CSS classes that
|
||||
// already exist (addClass) or do not exist (removeClass) on the element
|
||||
normalizeAnimationOptions(element, options);
|
||||
normalizeAnimationDetails(element, newAnimation);
|
||||
}
|
||||
|
||||
// when the options are merged and cleaned up we may end up not having to do
|
||||
@@ -429,7 +436,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
if (!isValidAnimation) {
|
||||
// animate (from/to) can be quickly checked first, otherwise we check if any classes are present
|
||||
isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)
|
||||
|| hasAnimationClasses(newAnimation.options);
|
||||
|| hasAnimationClasses(newAnimation);
|
||||
}
|
||||
|
||||
if (!isValidAnimation) {
|
||||
@@ -459,7 +466,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
var isValidAnimation = parentElement.length > 0
|
||||
&& (animationDetails.event === 'animate'
|
||||
|| animationDetails.structural
|
||||
|| hasAnimationClasses(animationDetails.options));
|
||||
|| hasAnimationClasses(animationDetails));
|
||||
|
||||
// this means that the previous animation was cancelled
|
||||
// even if the follow-up animation is the same event
|
||||
@@ -491,13 +498,18 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
|
||||
// this combined multiple class to addClass / removeClass into a setClass event
|
||||
// so long as a structural event did not take over the animation
|
||||
event = !animationDetails.structural && hasAnimationClasses(animationDetails.options, true)
|
||||
event = !animationDetails.structural && hasAnimationClasses(animationDetails, true)
|
||||
? 'setClass'
|
||||
: animationDetails.event;
|
||||
|
||||
markElementAnimationState(element, RUNNING_STATE);
|
||||
var realRunner = $$animation(element, event, animationDetails.options);
|
||||
|
||||
// this will update the runner's flow-control events based on
|
||||
// the `realRunner` object.
|
||||
runner.setHost(realRunner);
|
||||
notifyProgress(runner, event, 'start', {});
|
||||
|
||||
realRunner.done(function(status) {
|
||||
close(!status);
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
@@ -506,11 +518,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
notifyProgress(runner, event, 'close', {});
|
||||
});
|
||||
|
||||
// this will update the runner's flow-control events based on
|
||||
// the `realRunner` object.
|
||||
runner.setHost(realRunner);
|
||||
notifyProgress(runner, event, 'start', {});
|
||||
});
|
||||
|
||||
return runner;
|
||||
@@ -579,37 +586,38 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
* d) the element is not a child of the $rootElement
|
||||
*/
|
||||
function areAnimationsAllowed(element, parentElement, event) {
|
||||
var bodyElement = jqLite($document[0].body);
|
||||
var bodyElement = jqLite(rawDocument.body);
|
||||
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
|
||||
var rootElementDetected = isMatchingElement(element, $rootElement);
|
||||
var parentAnimationDetected = false;
|
||||
var animateChildren;
|
||||
var elementDisabled = disabledElementsLookup.get(getDomNode(element));
|
||||
|
||||
var parentHost = element.data(NG_ANIMATE_PIN_DATA);
|
||||
var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
parentElement = parentHost;
|
||||
}
|
||||
|
||||
while (parentElement && parentElement.length) {
|
||||
parentElement = getDomNode(parentElement);
|
||||
|
||||
while (parentElement) {
|
||||
if (!rootElementDetected) {
|
||||
// angular doesn't want to attempt to animate elements outside of the application
|
||||
// therefore we need to ensure that the rootElement is an ancestor of the current element
|
||||
rootElementDetected = isMatchingElement(parentElement, $rootElement);
|
||||
}
|
||||
|
||||
var parentNode = parentElement[0];
|
||||
if (parentNode.nodeType !== ELEMENT_NODE) {
|
||||
if (parentElement.nodeType !== ELEMENT_NODE) {
|
||||
// no point in inspecting the #document element
|
||||
break;
|
||||
}
|
||||
|
||||
var details = activeAnimationsLookup.get(parentNode) || {};
|
||||
var details = activeAnimationsLookup.get(parentElement) || {};
|
||||
// either an enter, leave or move animation will commence
|
||||
// therefore we can't allow any animations to take place
|
||||
// but if a parent animation is class-based then that's ok
|
||||
if (!parentAnimationDetected) {
|
||||
var parentElementDisabled = disabledElementsLookup.get(parentNode);
|
||||
var parentElementDisabled = disabledElementsLookup.get(parentElement);
|
||||
|
||||
if (parentElementDisabled === true && elementDisabled !== false) {
|
||||
// disable animations if the user hasn't explicitly enabled animations on the
|
||||
@@ -624,7 +632,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
if (isUndefined(animateChildren) || animateChildren === true) {
|
||||
var value = parentElement.data(NG_ANIMATE_CHILDREN_DATA);
|
||||
var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
|
||||
if (isDefined(value)) {
|
||||
animateChildren = value;
|
||||
}
|
||||
@@ -647,15 +655,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
|
||||
if (!rootElementDetected) {
|
||||
// If no rootElement is detected, check if the parentElement is pinned to another element
|
||||
parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
|
||||
parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
// The pin target element becomes the next parent element
|
||||
parentElement = parentHost;
|
||||
parentElement = getDomNode(parentHost);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
parentElement = parentElement.parent();
|
||||
parentElement = parentElement.parentNode;
|
||||
}
|
||||
|
||||
var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
|
||||
|
||||
@@ -135,6 +135,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
|
||||
options.tempClasses = null;
|
||||
}
|
||||
|
||||
var prepareClassName;
|
||||
if (isStructural) {
|
||||
prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
|
||||
$$jqLite.addClass(element, prepareClassName);
|
||||
}
|
||||
|
||||
animationQueue.push({
|
||||
// this data is used by the postDigest code and passed into
|
||||
// the driver step function
|
||||
@@ -357,6 +363,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
|
||||
if (tempClasses) {
|
||||
$$jqLite.addClass(element, tempClasses);
|
||||
}
|
||||
if (prepareClassName) {
|
||||
$$jqLite.removeClass(element, prepareClassName);
|
||||
prepareClassName = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateAnimationRunners(animation, newRunner) {
|
||||
|
||||
@@ -253,6 +253,34 @@
|
||||
* the CSS class once an animation has completed.)
|
||||
*
|
||||
*
|
||||
* ### The `ng-[event]-prepare` class
|
||||
*
|
||||
* This is a special class that can be used to prevent unwanted flickering / flash of content before
|
||||
* the actual animation starts. The class is added as soon as an animation is initialized, but removed
|
||||
* before the actual animation starts (after waiting for a $digest).
|
||||
* It is also only added for *structural* animations (`enter`, `move`, and `leave`).
|
||||
*
|
||||
* In practice, flickering can appear when nesting elements with structural animations such as `ngIf`
|
||||
* into elements that have class-based animations such as `ngClass`.
|
||||
*
|
||||
* ```html
|
||||
* <div ng-class="{red: myProp}">
|
||||
* <div ng-class="{blue: myProp}">
|
||||
* <div class="message" ng-if="myProp"></div>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating.
|
||||
* In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
|
||||
*
|
||||
* ```css
|
||||
* .message.ng-enter-prepare {
|
||||
* opacity: 0;
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* ## JavaScript-based Animations
|
||||
*
|
||||
* ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
|
||||
|
||||
@@ -21,6 +21,7 @@ var ADD_CLASS_SUFFIX = '-add';
|
||||
var REMOVE_CLASS_SUFFIX = '-remove';
|
||||
var EVENT_CLASS_PREFIX = 'ng-';
|
||||
var ACTIVE_CLASS_SUFFIX = '-active';
|
||||
var PREPARE_CLASS_SUFFIX = '-prepare';
|
||||
|
||||
var NG_ANIMATE_CLASSNAME = 'ng-animate';
|
||||
var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
|
||||
@@ -126,7 +127,7 @@ function stripCommentsFromElement(element) {
|
||||
if (element instanceof jqLite) {
|
||||
switch (element.length) {
|
||||
case 0:
|
||||
return [];
|
||||
return element;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -217,7 +218,10 @@ function applyAnimationToStyles(element, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function mergeAnimationOptions(element, target, newOptions) {
|
||||
function mergeAnimationDetails(element, oldAnimation, newAnimation) {
|
||||
var target = oldAnimation.options || {};
|
||||
var newOptions = newAnimation.options || {};
|
||||
|
||||
var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');
|
||||
var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
|
||||
var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);
|
||||
@@ -249,6 +253,9 @@ function mergeAnimationOptions(element, target, newOptions) {
|
||||
target.removeClass = null;
|
||||
}
|
||||
|
||||
oldAnimation.addClass = target.addClass;
|
||||
oldAnimation.removeClass = target.removeClass;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,16 +34,17 @@ angular.module('ngCookies', ['ng']).
|
||||
* The object may have following properties:
|
||||
*
|
||||
* - **path** - `{string}` - The cookie will be available only for this path and its
|
||||
* sub-paths. By default, this would be the URL that appears in your base tag.
|
||||
* sub-paths. By default, this is the URL that appears in your `<base>` tag.
|
||||
* - **domain** - `{string}` - The cookie will be available only for this domain and
|
||||
* its sub-domains. For obvious security reasons the user agent will not accept the
|
||||
* cookie if the current domain is not a sub domain or equals to the requested domain.
|
||||
* its sub-domains. For security reasons the user agent will not accept the cookie
|
||||
* if the current domain is not a sub-domain of this domain or equal to it.
|
||||
* - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
|
||||
* or a Date object indicating the exact date/time this cookie will expire.
|
||||
* - **secure** - `{boolean}` - The cookie will be available only in secured connection.
|
||||
* - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
|
||||
* secured connection.
|
||||
*
|
||||
* Note: by default the address that appears in your `<base>` tag will be used as path.
|
||||
* This is important so that cookies will be visible for all routes in case html5mode is enabled
|
||||
* Note: By default, the address that appears in your `<base>` tag will be used as the path.
|
||||
* This is important so that cookies will be visible for all routes when html5mode is enabled.
|
||||
*
|
||||
**/
|
||||
var defaults = this.defaults = {};
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "af-na",
|
||||
"localeID": "af_NA",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "af-za",
|
||||
"localeID": "af_ZA",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "af",
|
||||
"localeID": "af",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
+1
@@ -137,6 +137,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "agq-cm",
|
||||
"localeID": "agq_CM",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -137,6 +137,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "agq",
|
||||
"localeID": "agq",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -137,6 +137,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ak-gh",
|
||||
"localeID": "ak_GH",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -137,6 +137,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ak",
|
||||
"localeID": "ak",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "am-et",
|
||||
"localeID": "am_ET",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "am",
|
||||
"localeID": "am",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-001",
|
||||
"localeID": "ar_001",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-ae",
|
||||
"localeID": "ar_AE",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-bh",
|
||||
"localeID": "ar_BH",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-dj",
|
||||
"localeID": "ar_DJ",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-dz",
|
||||
"localeID": "ar_DZ",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-eg",
|
||||
"localeID": "ar_EG",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Vendored
+1
@@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||
]
|
||||
},
|
||||
"id": "ar-eh",
|
||||
"localeID": "ar_EH",
|
||||
"pluralCat": function(n, opt_precision) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user