Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f276ad0d51 | |||
| 33f817b99c | |||
| 6a4124d0fb | |||
| 26d1b34321 | |||
| 7fba6b603f | |||
| c115b37c33 | |||
| d20ba95e28 | |||
| 7945e5010a | |||
| 21d148aedc | |||
| 4c8aeefb62 | |||
| d384834fde | |||
| f975d8d448 | |||
| 2602daf993 | |||
| 9f681c459a | |||
| d9448dcb9f | |||
| e9c718a465 | |||
| 4dc44f7205 | |||
| 499e1b2adf | |||
| 28c6c4dae2 | |||
| 791148a328 | |||
| 01b1845088 | |||
| 56c861c9e1 | |||
| 451e0f6175 | |||
| aee7f1c72f | |||
| 5fe28a0422 | |||
| f56586d9a9 | |||
| ec0baadcb6 | |||
| 9147d5c04a | |||
| cb801378c9 | |||
| 79604f4628 | |||
| bf6cb8ab0d | |||
| de74b3fd85 | |||
| c7a92d2a9a | |||
| da284fc354 | |||
| 90da3059ce | |||
| 651fd05eca | |||
| 85d7e09bef | |||
| 243a57648b | |||
| 158dec330c | |||
| a1e63c8d4a | |||
| 8cc9978d15 | |||
| 78a404da0e | |||
| f1bb8369b5 | |||
| 76b6941b4c | |||
| e72990dc37 | |||
| f338e96ccc | |||
| 0ad2b70862 | |||
| 5261a374a9 | |||
| 832eba5fc9 | |||
| 2b569fc0b2 | |||
| ee6aeb08bf | |||
| 8d43d8b8e7 | |||
| d08f5c6986 | |||
| 3e7fa19197 | |||
| 5d379db72b | |||
| 514639b585 | |||
| 9cd9956dcb | |||
| c7813e9ebf | |||
| ef91b04cdd | |||
| 1acd97e18f | |||
| 513199ee9f | |||
| 33f3c40e93 | |||
| 696cb95d5e | |||
| 457fd21a1a | |||
| 3c6dfbf67d | |||
| 3277b885c4 | |||
| 48a256d04b | |||
| 0579430799 | |||
| 39ac68dac1 | |||
| 87fb44a5d3 | |||
| 5c76b406f7 | |||
| f665968daf | |||
| 5d1291c29d | |||
| ce7f400011 | |||
| 0fc8516b0c | |||
| 28b2a9b583 | |||
| e8549372fc | |||
| 5a434eb74e | |||
| 318de4db66 | |||
| a6c79bf15d | |||
| c6a10a755e | |||
| 1d7bd5bf4f | |||
| 94572e89c2 | |||
| fee7bac392 | |||
| dbb3b0f561 | |||
| b8bfed6a52 | |||
| 7215a89e7d | |||
| b7cca56091 | |||
| 7338e433f8 | |||
| 119ed07d5a | |||
| 5f98ae8323 | |||
| 8a598b43bb | |||
| e5dff4cfbe | |||
| 6c7a9cdd5f | |||
| 3b34f762fe | |||
| fa167ba747 |
+8
-3
@@ -1,19 +1,24 @@
|
||||
{
|
||||
"bitwise": true,
|
||||
"esversion": 6,
|
||||
"immed": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"nonew": true,
|
||||
"trailing": true,
|
||||
"maxlen": 200,
|
||||
"boss": true,
|
||||
"eqnull": true,
|
||||
"expr": true,
|
||||
"globalstrict": true,
|
||||
"laxbreak": true,
|
||||
"loopfunc": true,
|
||||
"strict": "global",
|
||||
"sub": true,
|
||||
"undef": true,
|
||||
"indent": 2
|
||||
"indent": 2,
|
||||
|
||||
"globals": {
|
||||
"ArrayBuffer": false,
|
||||
"Uint8Array": false
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.2'
|
||||
- '4.4'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
+294
@@ -1,3 +1,294 @@
|
||||
<a name="1.5.4"></a>
|
||||
# 1.5.4 graduated-sophistry (2016-04-14)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- do not use `noop()` as controller for multiple components
|
||||
([4c8aeefb](https://github.com/angular/angular.js/commit/4c8aeefb624de7436ad95f3cd525405e0c3f493e),
|
||||
[#14391](https://github.com/angular/angular.js/issues/14391), [#14402](https://github.com/angular/angular.js/issues/14402))
|
||||
- still trigger `$onChanges` even if the inner value already matches the new value
|
||||
([d9448dcb](https://github.com/angular/angular.js/commit/d9448dcb9f901ceb04deda1d5f3d5aac8442a718),
|
||||
[#14406](https://github.com/angular/angular.js/issues/14406))
|
||||
- handle boolean attributes in `@` bindings
|
||||
([499e1b2a](https://github.com/angular/angular.js/commit/499e1b2adf27f32d671123f8dceadb3df2ad84a9),
|
||||
[#14070](https://github.com/angular/angular.js/issues/14070))
|
||||
- don't throw if controller is named
|
||||
([e72990dc](https://github.com/angular/angular.js/commit/e72990dc3714c8b847185ddb64fd5fd00e5cceab))
|
||||
- ensure that `$onChanges` hook is called correctly
|
||||
([0ad2b708](https://github.com/angular/angular.js/commit/0ad2b70862d49ecc4355a16d767c0ca9358ecc3e),
|
||||
[#14355](https://github.com/angular/angular.js/issues/14355), [#14359](https://github.com/angular/angular.js/issues/14359))
|
||||
- **$injector:** ensure functions with overridden `toString()` are annotated properly
|
||||
([d384834f](https://github.com/angular/angular.js/commit/d384834fdee140a716298bd065f304f8fba4725e),
|
||||
[#14361](https://github.com/angular/angular.js/issues/14361))
|
||||
- **ngAnimate:**
|
||||
- remove event listeners only after all listeners have been called
|
||||
([79604f46](https://github.com/angular/angular.js/commit/79604f462899c118a99d610995083ff82d38aa35),
|
||||
[#14321](https://github.com/angular/angular.js/issues/14321))
|
||||
- fire callbacks when document is hidden
|
||||
([c7a92d2a](https://github.com/angular/angular.js/commit/c7a92d2a9a436dddd65de721c9837a93e915d939),
|
||||
[#14120](https://github.com/angular/angular.js/issues/14120))
|
||||
- fire callbacks in the correct order for certain skipped animations
|
||||
([90da3059](https://github.com/angular/angular.js/commit/90da3059cecfefaecf136b01cd87aee6775a8778))
|
||||
- **ngClass:** fix watching of an array expression containing an object
|
||||
([f975d8d4](https://github.com/angular/angular.js/commit/f975d8d4481e0b8cdba553f0e5ad9ec1688adae8),
|
||||
[#14405](https://github.com/angular/angular.js/issues/14405))
|
||||
- **ngMock:** fix collecting stack trace in `inject()` on IE10+, PhantomJS
|
||||
([e9c718a4](https://github.com/angular/angular.js/commit/e9c718a465d28b9f2691e3acab944f7c31aa9fb6),
|
||||
[#13591](https://github.com/angular/angular.js/issues/13591), [#13592](https://github.com/angular/angular.js/issues/13592), [#13593](https://github.com/angular/angular.js/issues/13593))
|
||||
- **ngOptions:** set select value when model matches disabled option
|
||||
([832eba5f](https://github.com/angular/angular.js/commit/832eba5fc952312e6b99127123e6e75bdf729006),
|
||||
[#12756](https://github.com/angular/angular.js/issues/12756))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- put custom annotations on DDO
|
||||
([f338e96c](https://github.com/angular/angular.js/commit/f338e96ccc739efc4b24022eae406c3d5451d422),
|
||||
[#14369](https://github.com/angular/angular.js/issues/14369), [#14279](https://github.com/angular/angular.js/issues/14279), [#14284](https://github.com/angular/angular.js/issues/14284))
|
||||
- add `isFirstChange()` method to onChanges object
|
||||
([8d43d8b8](https://github.com/angular/angular.js/commit/8d43d8b8e7aacf97ddb9aa48bff25db57249cdd5),
|
||||
[#14318](https://github.com/angular/angular.js/issues/14318), [#14323](https://github.com/angular/angular.js/issues/14323))
|
||||
- **$componentController:** provide isolated scope if none is passed (#14425)
|
||||
([33f817b9](https://github.com/angular/angular.js/commit/33f817b99cb20e566b381e7202235fe99b4a742a),
|
||||
[#14425](https://github.com/angular/angular.js/issues/14425))
|
||||
- **$http:**
|
||||
- support handling additional XHR events
|
||||
([01b18450](https://github.com/angular/angular.js/commit/01b18450882da9bb9c903d43c0daddbc03c2c35d) and
|
||||
[56c861c9](https://github.com/angular/angular.js/commit/56c861c9e114c45790865e5635eaae8d32eb649a),
|
||||
[#14367](https://github.com/angular/angular.js/issues/14367), [#11547](https://github.com/angular/angular.js/issues/11547), [#1934](https://github.com/angular/angular.js/issues/1934))
|
||||
- **$parse:** add the ability to define the identifier characters
|
||||
([3e7fa191](https://github.com/angular/angular.js/commit/3e7fa19197c54a764225ad27c0c0bf72263daa8d))
|
||||
- **ngAnimate:** let $animate.off() remove all listeners for an element
|
||||
([bf6cb8ab](https://github.com/angular/angular.js/commit/bf6cb8ab0d157083a1ed55743e3fffe728daa6f3))
|
||||
- **ngAria:** add support for aria-readonly based on ngReadonly
|
||||
([ec0baadc](https://github.com/angular/angular.js/commit/ec0baadcb68a4fa8da27d76b7e6a4e0840acd7fa),
|
||||
[#14140](https://github.com/angular/angular.js/issues/14140), [#14077](https://github.com/angular/angular.js/issues/14077))
|
||||
- **ngParseExt:** new ngParseExt module
|
||||
([d08f5c69](https://github.com/angular/angular.js/commit/d08f5c698624f6243685b16f2d458cb9a980ebde))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** use createMap() for directive bindings to allow fast `forEach`
|
||||
([c115b37c](https://github.com/angular/angular.js/commit/c115b37c336f3a5936187279057b29c76078caf2),
|
||||
[#12529](https://github.com/angular/angular.js/issues/12529))
|
||||
- **ngOptions:** use `documentFragment` to populate `select` options
|
||||
([6a4124d0](https://github.com/angular/angular.js/commit/6a4124d0fb17cd7fc0e8bf5a1ca4d785a1d11c1c),
|
||||
[#13607](https://github.com/angular/angular.js/issues/13607), [#13239](https://github.com/angular/angular.js/issues/13239), [#12076](https://github.com/angular/angular.js/issues/12076))
|
||||
|
||||
|
||||
|
||||
<a name="1.5.3"></a>
|
||||
# 1.5.3 diplohaplontic-meiosis (2016-03-25)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** workaround a GC bug in Chrome < 50
|
||||
([513199ee](https://github.com/angular/angular.js/commit/513199ee9f1c8eef1240983d6e52c824404adb98),
|
||||
[#14041](https://github.com/angular/angular.js/issues/14041), [#14286](https://github.com/angular/angular.js/issues/14286))
|
||||
- **$sniffer:** fix history sniffing in Chrome Packaged Apps
|
||||
([457fd21a](https://github.com/angular/angular.js/commit/457fd21a1a0c10c66245c32a73602f3a09038bda),
|
||||
[#11932](https://github.com/angular/angular.js/issues/11932), [#13945](https://github.com/angular/angular.js/issues/13945))
|
||||
- **formatNumber:** handle small numbers correctly when `gSize` !== `lgSize`
|
||||
([3277b885](https://github.com/angular/angular.js/commit/3277b885c4dec3edd51b8e8c3d1776057d6d4d1d),
|
||||
[#14289](https://github.com/angular/angular.js/issues/14289), [#14290](https://github.com/angular/angular.js/issues/14290))
|
||||
- **ngAnimate:** run structural animations with cancelled out class changes
|
||||
([c7813e9e](https://github.com/angular/angular.js/commit/c7813e9ebf793fe89380dcad54e8e002fafdd985),
|
||||
[#14249](https://github.com/angular/angular.js/issues/14249))
|
||||
- **ngMessages:** don't crash when nested messages are removed
|
||||
([ef91b04c](https://github.com/angular/angular.js/commit/ef91b04cdd794f308617bca7ebd0b1b747e4f7de),
|
||||
[#14183](https://github.com/angular/angular.js/issues/14183), [#14242](https://github.com/angular/angular.js/issues/14242))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** add more lifecycle hooks to directive controllers
|
||||
([9cd9956d](https://github.com/angular/angular.js/commit/9cd9956dcbc8382e8e8757a805398bd251bbc67e),
|
||||
[#14127](https://github.com/angular/angular.js/issues/14127), [#14030](https://github.com/angular/angular.js/issues/14030), [#14020](https://github.com/angular/angular.js/issues/14020), [#13991](https://github.com/angular/angular.js/issues/13991), [#14302](https://github.com/angular/angular.js/issues/14302))
|
||||
|
||||
|
||||
|
||||
<a name="1.5.2"></a>
|
||||
# 1.5.2 differential-recovery (2016-03-18)
|
||||
|
||||
This release reverts a breaking change that accidentally made it into the 1.5.1 release. See
|
||||
[fee7bac3](https://github.com/angular/angular.js/commit/fee7bac392db24b6006d6a57ba71526f3afa102c)
|
||||
for more info.
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **ngAnimate.$animate:** remove animation callbacks when the element is removed
|
||||
([ce7f4000](https://github.com/angular/angular.js/commit/ce7f400011e1e2e1b9316f18ce87b87b79d878b4))
|
||||
|
||||
|
||||
<a name="1.5.1"></a>
|
||||
# 1.5.1 equivocal-sophistication (2016-03-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **core:** only call `console.log` when `window.console` exists
|
||||
([ce138f3c](https://github.com/angular/angular.js/commit/ce138f3c552f8bf741721ab8d10994ed35a4b2f5),
|
||||
[#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))
|
||||
- **$compile:** allow directives to have decorators
|
||||
([0728cc2f](https://github.com/angular/angular.js/commit/0728cc2f2bb04d5dbdfca41f3afacea16c75ee07))
|
||||
- **$resource:** fix parse errors on older Android WebViews
|
||||
([df8db7b4](https://github.com/angular/angular.js/commit/df8db7b446b5bae83afef457d706d2805e597f29),
|
||||
[#13989](https://github.com/angular/angular.js/issues/13989))
|
||||
- **$routeProvider:** properly handle optional eager path named groups
|
||||
([c0797c68](https://github.com/angular/angular.js/commit/c0797c68866c9ef8ff3c2f6985e6eb9374346151),
|
||||
[#14011](https://github.com/angular/angular.js/issues/14011))
|
||||
- **copy:** add support for copying `Blob` objects
|
||||
([e9d579b6](https://github.com/angular/angular.js/commit/e9d579b608c2be8fdcf0326d0679a76bb9ae5b6e),
|
||||
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
|
||||
- **dateFilter:** correctly format BC years
|
||||
([e36205f5](https://github.com/angular/angular.js/commit/e36205f5af82b69362def7d2b6eeeb038f592311))
|
||||
- **formatNumber:** allow negative fraction size
|
||||
([e046c170](https://github.com/angular/angular.js/commit/e046c170bcf677f26e61af6470cb5fd2f751c969),
|
||||
[#13913](https://github.com/angular/angular.js/issues/13913))
|
||||
- **input:** re-validate when partially editing date-family inputs
|
||||
([e383804c](https://github.com/angular/angular.js/commit/e383804c4ab62278fbaf4fdfaa03caeacff77fc4),
|
||||
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
|
||||
- **input\[date\]:** support years with more than 4 digits
|
||||
([d76951f1](https://github.com/angular/angular.js/commit/d76951f1747abd2da6e320d4ff9019f170d9793f),
|
||||
[#13735](https://github.com/angular/angular.js/issues/13735), [#13905](https://github.com/angular/angular.js/issues/13905))
|
||||
- **ngOptions:** always set the 'selected' attribute for selected options
|
||||
([9f5a1722](https://github.com/angular/angular.js/commit/9f5a172291ff6926dcd246f0972288916a4c9bf6),
|
||||
[#14115](https://github.com/angular/angular.js/issues/14115))
|
||||
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template
|
||||
([8237482d](https://github.com/angular/angular.js/commit/8237482d49e76e2c4994fe6207e3c9799ef04163),
|
||||
[#1213](https://github.com/angular/angular.js/issues/1213), [#6812](https://github.com/angular/angular.js/issues/6812), [#14088](https://github.com/angular/angular.js/issues/14088))
|
||||
- **ngMock:**
|
||||
- attach `$injector` to `$rootElement` and prevent memory leak due to attached data
|
||||
([75373dd4](https://github.com/angular/angular.js/commit/75373dd4bdae6c6035272942c69444c386f824cd),
|
||||
[#14022](https://github.com/angular/angular.js/issues/14022), [#14094](https://github.com/angular/angular.js/issues/14094), [#14098](https://github.com/angular/angular.js/issues/14098))
|
||||
- don't break if `$rootScope.$destroy()` is not a function
|
||||
([50ed8712](https://github.com/angular/angular.js/commit/50ed8712566d601c9fb76b71f7b534b5bc803a36),
|
||||
[#14106](https://github.com/angular/angular.js/issues/14106), [#14107](https://github.com/angular/angular.js/issues/14107))
|
||||
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
|
||||
([d16faf9f](https://github.com/angular/angular.js/commit/d16faf9f2b9bd2b85d95e71d902cec0269282f2c),
|
||||
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** add custom annotations to the controller
|
||||
([0c800930](https://github.com/angular/angular.js/commit/0c8009300b819c39c5e4892856724a731a8dcda6),
|
||||
[#14114](https://github.com/angular/angular.js/issues/14114))
|
||||
- **$controllerProvider:** add a `has()` method for checking the existence of a controller
|
||||
([bb9575db](https://github.com/angular/angular.js/commit/bb9575dbd3428176216355df7b2933d2a72783cd),
|
||||
[#13951](https://github.com/angular/angular.js/issues/13951), [#14109](https://github.com/angular/angular.js/issues/14109))
|
||||
- **dateFilter:** add support for STANDALONEMONTH in format (`LLLL`)
|
||||
([3e5b25b3](https://github.com/angular/angular.js/commit/3e5b25b33f278376def432698c704b1807fdb8c0),
|
||||
[#13999](https://github.com/angular/angular.js/issues/13999), [#14013](https://github.com/angular/angular.js/issues/14013))
|
||||
- **ngMock:** add `sharedInjector()` to `angular.mock.module`
|
||||
([a46ab60f](https://github.com/angular/angular.js/commit/a46ab60fd5bf94896f0761e858ef38b998eb0f80),
|
||||
[#14093](https://github.com/angular/angular.js/issues/14093), [#10238](https://github.com/angular/angular.js/issues/10238))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **ngRepeat:** avoid duplicate jqLite wrappers
|
||||
([632e15a3](https://github.com/angular/angular.js/commit/632e15a3afdcd30168700cec1367bd81966400d4))
|
||||
- **ngAnimate:**
|
||||
- avoid jqLite/jQuery for upward DOM traversal
|
||||
([35251bd4](https://github.com/angular/angular.js/commit/35251bd4ce23251b5e9a2860cf414726c194721e))
|
||||
- avoid `$.fn.data` overhead with jQuery
|
||||
([15915e60](https://github.com/angular/angular.js/commit/15915e606fdf5114592db1a0a5e3f12e639d7cdb))
|
||||
|
||||
|
||||
<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.5.0"></a>
|
||||
# 1.5.0 ennoblement-facilitation (2016-02-05)
|
||||
|
||||
@@ -33,6 +324,9 @@
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Upgrade to 1.5.1
|
||||
This version of AngularJS is problematic due to a issue during its release. Please upgrade to version [1.5.2](#1.5.2).
|
||||
|
||||
- **ngAria:** due to [d06431e5](https://github.com/angular/angular.js/commit/d06431e5309bb0125588877451dc79b935808134),
|
||||
Where appropriate, ngAria now applies ARIA to custom controls only, not native inputs. Because of this, support for `aria-multiline` on textareas has been removed.
|
||||
|
||||
|
||||
+16
-3
@@ -135,6 +135,9 @@ module.exports = function(grunt) {
|
||||
ngMock: {
|
||||
files: { src: 'src/ngMock/**/*.js' },
|
||||
},
|
||||
ngParseExt: {
|
||||
files: { src: 'src/ngParseExt/**/*.js' },
|
||||
},
|
||||
ngResource: {
|
||||
files: { src: 'src/ngResource/**/*.js' },
|
||||
},
|
||||
@@ -231,7 +234,11 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-aria.js',
|
||||
src: util.wrap(files['angularModules']['ngAria'], 'module')
|
||||
},
|
||||
'promises-aplus-adapter': {
|
||||
parseext: {
|
||||
dest: 'build/angular-parse-ext.js',
|
||||
src: util.wrap(files['angularModules']['ngParseExt'], 'module')
|
||||
},
|
||||
"promises-aplus-adapter": {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
}
|
||||
@@ -249,7 +256,8 @@ module.exports = function(grunt) {
|
||||
resource: 'build/angular-resource.js',
|
||||
route: 'build/angular-route.js',
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
aria: 'build/angular-aria.js'
|
||||
aria: 'build/angular-aria.js',
|
||||
parseext: 'build/angular-parse-ext.js'
|
||||
},
|
||||
|
||||
|
||||
@@ -264,12 +272,17 @@ module.exports = function(grunt) {
|
||||
],
|
||||
options: {
|
||||
disallowed: [
|
||||
'fit',
|
||||
'iit',
|
||||
'xit',
|
||||
'fthey',
|
||||
'tthey',
|
||||
'xthey',
|
||||
'fdescribe',
|
||||
'ddescribe',
|
||||
'xdescribe'
|
||||
'xdescribe',
|
||||
'it.only',
|
||||
'describe.only'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,20 +8,21 @@ synchronizes data from your UI (view) with your JavaScript objects (model) throu
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control.
|
||||
|
||||
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
|
||||
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. Best of all?? It makes development fun!
|
||||
It also helps with server-side communication, taming async callbacks with promises and deferreds,
|
||||
and it makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. Best of all? It makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Web site: https://angularjs.org
|
||||
* Tutorial: https://docs.angularjs.org/tutorial
|
||||
* API Docs: https://docs.angularjs.org/api
|
||||
* Developer Guide: https://docs.angularjs.org/guide
|
||||
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
* Dashboard: https://dashboard.angularjs.org
|
||||
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
|
||||
[Once you have set up your environment](https://docs.angularjs.org/misc/contribute), just run:
|
||||
|
||||
grunt package
|
||||
|
||||
@@ -37,8 +38,12 @@ To execute end-to-end (e2e) tests, use:
|
||||
grunt package
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
|
||||
To learn more about the grunt tasks, run `grunt --help`
|
||||
|
||||
Contribute & Develop
|
||||
--------------------
|
||||
|
||||
We've set up a separate document for our [contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
@@ -48,7 +53,7 @@ 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.
|
||||
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.
|
||||
|
||||
|
||||
Vendored
+5
@@ -120,6 +120,10 @@ var angularFiles = {
|
||||
'ngMessages': [
|
||||
'src/ngMessages/messages.js'
|
||||
],
|
||||
'ngParseExt': [
|
||||
'src/ngParseExt/ucd.js',
|
||||
'src/ngParseExt/module.js'
|
||||
],
|
||||
'ngResource': [
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
@@ -205,6 +209,7 @@ var angularFiles = {
|
||||
"karmaModules": [
|
||||
'build/angular.js',
|
||||
'@angularSrcModules',
|
||||
'test/modules/no_bootstrap.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
"dump": false,
|
||||
|
||||
/* e2e */
|
||||
"protractor": false,
|
||||
"browser": false,
|
||||
"element": false,
|
||||
"by": false,
|
||||
"$": false,
|
||||
"$$": false,
|
||||
|
||||
/* testabilityPatch / matchers */
|
||||
"inject": false,
|
||||
@@ -39,4 +42,4 @@
|
||||
"browserTrigger": false,
|
||||
"jqLiteCacheSize": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ describe("DocsController", function() {
|
||||
|
||||
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
|
||||
$window._gaq = [];
|
||||
spyOn($location, 'path').andReturn('x/y/z');
|
||||
spyOn($location, 'path').and.returnValue('x/y/z');
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
|
||||
@@ -4,7 +4,7 @@ describe('errors', function() {
|
||||
// Mock `ngSanitize` module
|
||||
angular.
|
||||
module('ngSanitize', []).
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').andCallFake(angular.identity));
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
|
||||
|
||||
beforeEach(module('errors'));
|
||||
|
||||
@@ -103,12 +103,12 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should pass the final string through `$sanitize`', function() {
|
||||
$sanitize.reset();
|
||||
$sanitize.calls.reset();
|
||||
|
||||
var input = 'start https://foo/bar?baz#qux end';
|
||||
var output = errorLinkFilter(input);
|
||||
|
||||
expect($sanitize.callCount).toBe(1);
|
||||
expect($sanitize).toHaveBeenCalledTimes(1);
|
||||
expect($sanitize).toHaveBeenCalledWith(output);
|
||||
});
|
||||
});
|
||||
@@ -123,7 +123,7 @@ describe('errors', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.decorator('errorLinkFilter', function() {
|
||||
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
|
||||
errorLinkFilter.andCallFake(angular.identity);
|
||||
errorLinkFilter.and.callFake(angular.identity);
|
||||
|
||||
return errorLinkFilter;
|
||||
});
|
||||
@@ -142,7 +142,7 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should interpolate the contents against `$location.search()`', function() {
|
||||
spyOn($location, 'search').andReturn({p0: 'foo', p1: 'bar'});
|
||||
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
|
||||
expect(elem.html()).toBe('foo = foo, bar = bar');
|
||||
@@ -150,10 +150,10 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should pass the interpolated text through `errorLinkFilter`', function() {
|
||||
$location.search = jasmine.createSpy('search').andReturn({p0: 'foo'});
|
||||
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
|
||||
expect(errorLinkFilter.callCount).toBe(1);
|
||||
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
|
||||
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
|
||||
});
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ module.exports = new Package('angularjs', [
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/tutorial-step'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/sortOrder'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/installation'));
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
name: 'installation'
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
{% extends "base.template.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% if doc.title %}{$ doc.title | marked $}{% else %}{$ doc.name | code $}{% endif %}
|
||||
</h1>
|
||||
|
||||
{% if doc.installation or doc.installation == '' %}
|
||||
{$ doc.installation | marked $}
|
||||
{% else %}
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>First include {$ doc.packageFile | code $} in your HTML:</p>
|
||||
|
||||
{% code %}
|
||||
<script src="angular.js">
|
||||
<script src="{$ doc.packageFile $}">
|
||||
{% endcode %}
|
||||
|
||||
<p>You can download this file from the following places:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://developers.google.com/speed/libraries/devguide#angularjs">Google CDN</a><br>
|
||||
e.g. {$ ("//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/" + doc.packageFile) | code $}
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://bower.io">Bower</a><br>
|
||||
e.g. {% code %}bower install {$ doc.packageName $}@X.Y.Z{% endcode %}
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://code.angularjs.org/">code.angularjs.org</a><br>
|
||||
e.g. {% code %}"//code.angularjs.org/X.Y.Z/{$ doc.packageFile $}"{% endcode %}
|
||||
</li>
|
||||
</ul>
|
||||
<p>where X.Y.Z is the AngularJS version you are running.</p>
|
||||
<p>Then load the module in your application by adding it as a dependent module:</p>
|
||||
{% code %}
|
||||
angular.module('app', ['{$ doc.name $}']);
|
||||
{% endcode %}
|
||||
|
||||
<p>With that you're ready to get started!</p>
|
||||
{% endif %}
|
||||
|
||||
{$ doc.description | marked $}
|
||||
|
||||
{% if doc.knownIssueDocs %}
|
||||
<div class="known-issues">
|
||||
<h2 id="known-issues">Known Issues</h2>
|
||||
<table class="definition-table">
|
||||
<tr><th>Name</th><th>Description</th></tr>
|
||||
{% for issueDoc in doc.knownIssueDocs -%}
|
||||
<tr>
|
||||
<td>{$ issueDoc.id | link(issueDoc.name, issueDoc) $}</td>
|
||||
<td>
|
||||
{% for issue in issueDoc.knownIssues -%}
|
||||
{$ issue | marked $}
|
||||
{% endfor -%}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor -%}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="component-breakdown">
|
||||
<h2>Module Components</h2>
|
||||
{% for componentGroup in doc.componentGroups %}
|
||||
<div>
|
||||
<h3 class="component-heading" id="{$ componentGroup.groupType | dashCase $}">{$ componentGroup.groupType | title $}</h3>
|
||||
<table class="definition-table">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
{% for component in componentGroup.components %}
|
||||
<tr>
|
||||
<td>{$ component.id | link(component.name, component) $}</td>
|
||||
<td>{$ component.description | firstParagraph | marked $}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if doc.usage %}
|
||||
<h2>Usage</h2>
|
||||
{$ doc.usage | marked $}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,8 +1,8 @@
|
||||
@ngdoc error
|
||||
@name $compile:baddir
|
||||
@fullName Invalid Directive Name
|
||||
@fullName Invalid Directive/Component Name
|
||||
@description
|
||||
|
||||
This error occurs when the name of a directive is not valid.
|
||||
This error occurs when the name of a directive or component is not valid.
|
||||
|
||||
Directives must start with a lowercase character and must not contain leading or trailing whitespaces.
|
||||
Directives and Components must start with a lowercase character and must not contain leading or trailing whitespaces.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
@ngdoc error
|
||||
@name $compile:infchng
|
||||
@fullName Unstable `$onChanges` hooks
|
||||
@description
|
||||
|
||||
This error occurs when the application's model becomes unstable because some `$onChanges` hooks are causing updates which then trigger
|
||||
further calls to `$onChanges` that can never complete.
|
||||
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
|
||||
|
||||
For example, the situation can occur by setting up a `$onChanges()` hook which triggers an event on the component, which subsequently
|
||||
triggers the component's bound inputs to be updated:
|
||||
|
||||
```html
|
||||
<c1 prop="a" on-change="a = -a"></c1>
|
||||
```
|
||||
|
||||
```js
|
||||
function Controller1() {}
|
||||
Controller1.$onChanges = function() {
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
mod.component('c1', {
|
||||
controller: Controller1,
|
||||
bindings: {'prop': '<', onChange: '&'}
|
||||
}
|
||||
```
|
||||
|
||||
The maximum number of allowed iterations of the `$onChanges` hooks is controlled via TTL setting which can be configured via
|
||||
{@link ng.$compileProvider#onChangesTtl `$compileProvider.onChangesTtl`}.
|
||||
@@ -43,7 +43,7 @@ well. Consider the following template:
|
||||
|
||||
```
|
||||
<div class='container'>
|
||||
<div class='wrapper>
|
||||
<div class='wrapper'>
|
||||
...
|
||||
</div> <!-- wrapper -->
|
||||
</div> <!-- container -->
|
||||
|
||||
@@ -10,7 +10,7 @@ The goal of ngAria is to improve Angular's default accessibility by enabling com
|
||||
[ARIA](http://www.w3.org/TR/wai-aria/) attributes that convey state or semantic information for
|
||||
assistive technologies used by persons with disabilities.
|
||||
|
||||
##Including ngAria
|
||||
## Including ngAria
|
||||
|
||||
Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
|
||||
standard AngularJS directives and quietly injects accessibility support into your application
|
||||
@@ -20,7 +20,7 @@ at runtime.
|
||||
angular.module('myApp', ['ngAria'])...
|
||||
```
|
||||
|
||||
###Using ngAria
|
||||
### Using ngAria
|
||||
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
|
||||
added it as a dependency, you can test a few things:
|
||||
* Using your favorite element inspector, look for attributes added by ngAria in your own code.
|
||||
@@ -28,12 +28,13 @@ added it as a dependency, you can test a few things:
|
||||
* Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
|
||||
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
|
||||
|
||||
##Supported directives
|
||||
## Supported directives
|
||||
Currently, ngAria interfaces with the following directives:
|
||||
|
||||
* {@link guide/accessibility#ngmodel ngModel}
|
||||
* {@link guide/accessibility#ngdisabled ngDisabled}
|
||||
* {@link guide/accessibility#ngrequired ngRequired}
|
||||
* {@link guide/accessibility#ngreadonly ngReadonly}
|
||||
* {@link guide/accessibility#ngvaluechecked ngChecked}
|
||||
* {@link guide/accessibility#ngvaluechecked ngValue}
|
||||
* {@link guide/accessibility#ngshow ngShow}
|
||||
@@ -57,12 +58,62 @@ attributes (if they have not been explicitly specified by the developer):
|
||||
* aria-valuenow
|
||||
* aria-invalid
|
||||
* aria-required
|
||||
* aria-readonly
|
||||
|
||||
###Example
|
||||
### Example
|
||||
|
||||
<example module="ngAria_ngModelExample" deps="angular-aria.js">
|
||||
<file name="index.html">
|
||||
<style>
|
||||
<file name="index.html">
|
||||
<form ng-controller="formsController">
|
||||
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
|
||||
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
|
||||
aria-label="Custom Checkbox" show-attrs>
|
||||
<span class="icon" aria-hidden="true"></span>
|
||||
Custom Checkbox
|
||||
</some-checkbox>
|
||||
</form>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
|
||||
.controller('formsController', function($scope){
|
||||
$scope.checked = false;
|
||||
$scope.toggleCheckbox = function(){
|
||||
$scope.checked = !$scope.checked;
|
||||
};
|
||||
})
|
||||
.directive('someCheckbox', function(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function($scope, $el, $attrs) {
|
||||
$el.on('keypress', function(event){
|
||||
event.preventDefault();
|
||||
if(event.keyCode === 32 || event.keyCode === 13){
|
||||
$scope.toggleCheckbox();
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('showAttrs', function() {
|
||||
return function($scope, $el, $attrs) {
|
||||
var pre = document.createElement('pre');
|
||||
$el.after(pre);
|
||||
$scope.$watch(function() {
|
||||
var $attrs = {};
|
||||
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
|
||||
if (item.name !== 'show-$attrs') {
|
||||
$attrs[item.name] = item.value;
|
||||
}
|
||||
});
|
||||
return $attrs;
|
||||
}, function(newAttrs, oldAttrs) {
|
||||
pre.textContent = JSON.stringify(newAttrs, null, 2);
|
||||
}, true);
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="style.css">
|
||||
[role=checkbox] {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
@@ -81,58 +132,7 @@ attributes (if they have not been explicitly specified by the developer):
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<form ng-controller="formsController">
|
||||
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
|
||||
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
|
||||
aria-label="Custom Checkbox" show-attrs>
|
||||
<span class="icon" aria-hidden="true"></span>
|
||||
Custom Checkbox
|
||||
</some-checkbox>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
|
||||
.controller('formsController', function($scope){
|
||||
$scope.checked = false;
|
||||
$scope.toggleCheckbox = function(){
|
||||
$scope.checked = !$scope.checked;
|
||||
}
|
||||
})
|
||||
.directive('someCheckbox', function(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function($scope, $el, $attrs) {
|
||||
$el.on('keypress', function(event){
|
||||
event.preventDefault();
|
||||
if(event.keyCode === 32 || event.keyCode === 13){
|
||||
$scope.toggleCheckbox();
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.directive('showAttrs', function() {
|
||||
return function($scope, $el, $attrs) {
|
||||
var pre = document.createElement('pre');
|
||||
$el.after(pre);
|
||||
$scope.$watch(function() {
|
||||
var $attrs = {};
|
||||
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
|
||||
if (item.name !== 'show-$attrs') {
|
||||
$attrs[item.name] = item.value;
|
||||
}
|
||||
});
|
||||
return $attrs;
|
||||
}, function(newAttrs, oldAttrs) {
|
||||
pre.textContent = JSON.stringify(newAttrs, null, 2);
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</file>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
|
||||
@@ -147,7 +147,7 @@ To ease the transition between native inputs and custom controls, ngAria now sup
|
||||
The original directives were created for native inputs only, so ngAria extends
|
||||
support to custom elements by managing `aria-checked` for accessibility.
|
||||
|
||||
###Example
|
||||
### Example
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val"></custom-checkbox>
|
||||
@@ -169,7 +169,7 @@ using ngAria with {@link ng.ngDisabled ngDisabled} will also
|
||||
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
|
||||
custom controls to be more accessible.
|
||||
|
||||
###Example
|
||||
### Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-disabled="disabled"></md-checkbox>
|
||||
@@ -181,8 +181,10 @@ Becomes:
|
||||
<md-checkbox disabled aria-disabled="true"></md-checkbox>
|
||||
```
|
||||
|
||||
>You can check whether a control is legitimately disabled for a screen reader by visiting
|
||||
<div class="alert alert-info">
|
||||
You can check whether a control is legitimately disabled for a screen reader by visiting
|
||||
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
|
||||
</div>
|
||||
|
||||
<h2 id="ngrequired">ngRequired</h2>
|
||||
|
||||
@@ -191,7 +193,7 @@ The boolean `required` attribute is only valid for native form controls such as
|
||||
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
|
||||
`aria-required`. This tells accessibility APIs when a custom control is required.
|
||||
|
||||
###Example
|
||||
### Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val"></md-checkbox>
|
||||
@@ -203,9 +205,28 @@ Becomes:
|
||||
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
|
||||
```
|
||||
|
||||
<h2 id="ngreadonly">ngReadonly</h2>
|
||||
|
||||
The boolean `readonly` attribute is only valid for native form controls such as `input` and
|
||||
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
|
||||
as required, using ngAria with {@link ng.ngReadonly ngReadonly} will also add
|
||||
`aria-readonly`. This tells accessibility APIs when a custom control is read-only.
|
||||
|
||||
### Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-readonly="val"></md-checkbox>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox ng-readonly="val" aria-readonly="true"></md-checkbox>
|
||||
```
|
||||
|
||||
<h2 id="ngshow">ngShow</h2>
|
||||
|
||||
>The {@link ng.ngShow ngShow} directive shows or hides the
|
||||
The {@link ng.ngShow ngShow} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngShow` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -222,7 +243,7 @@ screen reader users won't accidentally focus on "mystery elements". Managing tab
|
||||
child control can be complex and affect performance, so it’s best to just stick with the default
|
||||
`display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
|
||||
|
||||
###Example
|
||||
### Example
|
||||
```css
|
||||
.ng-hide {
|
||||
display: block;
|
||||
@@ -242,7 +263,7 @@ Becomes:
|
||||
|
||||
<h2 id="nghide">ngHide</h2>
|
||||
|
||||
>The {@link ng.ngHide ngHide} directive shows or hides the
|
||||
The {@link ng.ngHide ngHide} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngHide` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -283,11 +304,11 @@ Becomes:
|
||||
|
||||
<h2 id="ngmessages">ngMessages</h2>
|
||||
|
||||
The new ngMessages module makes it easy to display form validation or other messages with priority
|
||||
The ngMessages module makes it easy to display form validation or other messages with priority
|
||||
sequencing and animation. To expose these visual messages to screen readers,
|
||||
ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown,
|
||||
regardless of the user's focus location.
|
||||
###Example
|
||||
### Example
|
||||
|
||||
```html
|
||||
<div ng-messages="myForm.myName.$error">
|
||||
@@ -305,7 +326,7 @@ Becomes:
|
||||
</div>
|
||||
```
|
||||
|
||||
##Disabling attributes
|
||||
## Disabling attributes
|
||||
The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
|
||||
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
|
||||
tell ngAria to ignore the attribute globally.
|
||||
@@ -343,7 +364,7 @@ tell ngAria to ignore the attribute globally.
|
||||
</file>
|
||||
</example>
|
||||
|
||||
##Common Accessibility Patterns
|
||||
## Common Accessibility Patterns
|
||||
|
||||
Accessibility best practices that apply to web apps in general also apply to Angular.
|
||||
|
||||
|
||||
@@ -430,10 +430,12 @@ You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class
|
||||
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
|
||||
case of the spinner, this would be:
|
||||
|
||||
```css
|
||||
.spinner.ng-animate {
|
||||
transition: 0s none;
|
||||
animation: 0s none;
|
||||
}
|
||||
```
|
||||
|
||||
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
|
||||
than any styles that are independent from ngAnimate.
|
||||
|
||||
@@ -33,7 +33,7 @@ Here is a table of the main concepts used in the Component Router.
|
||||
|
||||
## Component-based Applications
|
||||
|
||||
It recommended to develop AngularJS applications as a hierarchy of Components. Each Component
|
||||
It is recommended to develop AngularJS applications as a hierarchy of Components. Each Component
|
||||
is an isolated part of the application, which is responsible for its own user interface and has
|
||||
a well defined programmatic interface to the Component that contains it. Take a look at the
|
||||
{@link guide/component component guide} for more information.
|
||||
@@ -105,7 +105,7 @@ Here we have specified that the **Root Component** is the component directive wi
|
||||
Remember to instantiate this **Root Component** in our `index.html` file.
|
||||
|
||||
```html
|
||||
<my-app><my-app>
|
||||
<my-app></my-app>
|
||||
```
|
||||
|
||||
## Route Matching
|
||||
@@ -124,9 +124,9 @@ This process continues until we run out of **Routing Components** or consume the
|
||||
|
||||

|
||||
|
||||
In the previous diagram can see that the URL `/heros/2` has been matched against the `App`, `Heroes` and
|
||||
In the previous diagram, we can see that the URL `/heros/4` has been matched against the `App`, `Heroes` and
|
||||
`HeroDetail` **Routing Components**. The **Routers** for each of the **Routing Components** consumed a part
|
||||
of the URL: "/", "/heroes" and "/2" respectively.
|
||||
of the URL: "/", "/heroes" and "/4" respectively.
|
||||
|
||||
The result is that we end up with a hierarchy of **Routing Components** rendered in **Outlets**, via the
|
||||
{@link ngOutlet} directive, in each **Routing Component's** template, as you can see in the following diagram.
|
||||
@@ -462,7 +462,7 @@ to display list and detail views of Heroes and Crises.
|
||||
|
||||
## Install the libraries
|
||||
|
||||
It is simplest to use npm to install the **Component Router** module. For this guide we will also install
|
||||
It is easier to use npm to install the **Component Router** module. For this guide we will also install
|
||||
AngularJS itself via npm:
|
||||
|
||||
```bash
|
||||
@@ -485,7 +485,7 @@ Just like any Angular application, we load the JavaScript files into our `index.
|
||||
|
||||
## Create the `app` module
|
||||
|
||||
In the app.js file, create the main application module `app` which depends upon the `ngComponentRouter`
|
||||
In the app.js file, create the main application module `app` which depends on the `ngComponentRouter`
|
||||
module, which is provided by the **Component Router** script.
|
||||
|
||||
```js
|
||||
@@ -494,10 +494,10 @@ angular.module('app', ['ngComponentRouter'])
|
||||
|
||||
We must choose what **Location Mode** the **Router** should use. We are going to use HTML5 mode locations,
|
||||
so that we will not have hash-based paths. We must rely on the browser to provide `pushState` support,
|
||||
which is true of most modern browsers. See {@link $locationProvider#html5Mode} for more information.
|
||||
which is true for most modern browsers. See {@link $locationProvider#html5Mode} for more information.
|
||||
|
||||
<div class="alert alert-info">
|
||||
Using HTML5 mode means that we can have clean URLs for our application routes but it does require that our
|
||||
Using HTML5 mode means that we can have clean URLs for our application routes. However, HTML5 mode does require that our
|
||||
web server, which hosts the application, understands that it must respond with the index.html file for
|
||||
requests to URLs that represent all our application routes. We are going to use the `lite-server` web server
|
||||
to do this for us.
|
||||
@@ -550,7 +550,7 @@ Bootstrap the Angular application and add the top level App Component.
|
||||
|
||||
# Implementing the AppComponent
|
||||
|
||||
In the previous section we created a single top level **App Component**. Let's now create some more
|
||||
In the previous section we have created a single top level **App Component**. Let's now create some more
|
||||
**Routing Components** and wire up **Route Config** for those. We start with a Heroes Feature, which
|
||||
will display one of two views.
|
||||
|
||||
@@ -590,7 +590,7 @@ of this view will be rendered.
|
||||
### ngLink
|
||||
|
||||
We have used the `ng-link` directive to create a link to navigate to the Heroes Component. By using this
|
||||
directive we don't need to know what the actual URL will be. We can leave the Router to generate that for us.
|
||||
directive we don't need to know what the actual URL will be. We can let the Router generate that for us.
|
||||
|
||||
We have included a link to the Crisis Center but have not included the `ng-link` directive as we have not yet
|
||||
implemented the CrisisCenter component.
|
||||
@@ -765,7 +765,7 @@ function HeroListComponent(heroService) {
|
||||
Running the application should update the browser's location to `/heroes` and display the list of heroes
|
||||
returned from the `heroService`.
|
||||
|
||||
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay activation of the
|
||||
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay the activation of the
|
||||
Route until the heroes have arrived successfully. This is similar to how a `resolve` works in {@link ngRoute}.
|
||||
|
||||
|
||||
@@ -956,9 +956,9 @@ respectively.
|
||||
|
||||
**How do I prevent navigation from occurring?**
|
||||
|
||||
Each **Component** can provide the `$routerCanActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
|
||||
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$routerCanActivate` hook is a
|
||||
static method defined on either the **Component Definition Object** or the **Component's** constructor function.
|
||||
Each **Component** can provide the `$canActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
|
||||
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$canActivate` hook is used as a
|
||||
static method defined on the **Component Definition Object**.
|
||||
|
||||
The **Router** will call these hooks to control navigation from one **Route** to another. Each of these hooks can
|
||||
return a `boolean` or a Promise that will resolve to a `boolean`.
|
||||
@@ -966,7 +966,7 @@ return a `boolean` or a Promise that will resolve to a `boolean`.
|
||||
During a navigation, some **Components** will become inactive and some will become active. Before the navigation
|
||||
can complete, all the **Components** must agree that they can be deactivated or activated, respectively.
|
||||
|
||||
The **Router** will call the `$routerCanDeactivate` and `$routerCanActivate` hooks, if they are provided. If any
|
||||
The **Router** will call the `$routerCanDeactivate` and `$canActivate` hooks, if they are provided. If any
|
||||
of the hooks resolve to `false` then the navigation is cancelled.
|
||||
|
||||
### Dialog Box Service
|
||||
|
||||
@@ -29,7 +29,7 @@ and link functions are unavailable
|
||||
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
|
||||
|
||||
* The name of the Component (as string).
|
||||
* The Component config object (note that, unlike the `.directive()` method, this method does **not** take a factory function.
|
||||
* The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.)
|
||||
|
||||
<example name="heroComponentSimple" module="heroApp">
|
||||
<file name="index.js">
|
||||
@@ -133,6 +133,8 @@ components should follow a few simple conventions:
|
||||
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
|
||||
the owner component via the correct event.
|
||||
```html
|
||||
<!-- note that we use snake case for bindings in the template as usual -->
|
||||
<editable-field on-update="$ctrl.update('location', value)"></editable-field><br>
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
```
|
||||
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
|
||||
@@ -147,6 +149,30 @@ components should follow a few simple conventions:
|
||||
}
|
||||
```
|
||||
|
||||
- **Components have a well-defined lifecycle**
|
||||
Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life
|
||||
of the component. The following hook methods can be implemented:
|
||||
|
||||
* `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
|
||||
had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
this element). This is a good place to put initialization code for your controller.
|
||||
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
|
||||
are the names of the bound properties that have changed, and the values are an object of the form
|
||||
`{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a component such as
|
||||
cloning the bound value to prevent accidental mutation of the outer value.
|
||||
* `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
|
||||
external resources, watches and event handlers.
|
||||
* `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
|
||||
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
|
||||
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
|
||||
they are waiting for their template to load asynchronously and their own compilation and linking has been
|
||||
suspended until that occurs.
|
||||
This hook can be considered analogous to the `ngAfterViewInit` and `ngAfterContentInit` hooks in Angular 2.
|
||||
Since the compilation process is rather different in Angular 1 there is no direct mapping and care should
|
||||
be taken when upgrading.
|
||||
|
||||
By implementing these methods, your component can hook into its lifecycle.
|
||||
|
||||
- **An application is a tree of components:**
|
||||
Ideally, the whole application should be a tree of components that implement clearly defined inputs
|
||||
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ subsystem takes care of the rest.
|
||||
<file name="script.js">
|
||||
angular.
|
||||
module('myServiceModule', []).
|
||||
controller('MyController', ['$scope','notify', function ($scope, notify) {
|
||||
controller('MyController', ['$scope', 'notify', function ($scope, notify) {
|
||||
$scope.callNotify = function(msg) {
|
||||
notify(msg);
|
||||
};
|
||||
|
||||
@@ -16,9 +16,9 @@ becoming an Angular expert.
|
||||
starter app with a directory layout, test harness, and scripts to begin building your application.
|
||||
|
||||
|
||||
#Further Steps
|
||||
# Further Steps
|
||||
|
||||
##Watch Videos
|
||||
## Watch Videos
|
||||
|
||||
If you haven’t had a chance to watch the videos from the homepage, please check out:
|
||||
|
||||
@@ -29,13 +29,13 @@ If you haven’t had a chance to watch the videos from the homepage, please chec
|
||||
And visit our [YouTube channel](http://www.youtube.com/user/angularjs) for more AngularJS video presentations and
|
||||
tutorials.
|
||||
|
||||
##Subscribe
|
||||
## Subscribe
|
||||
|
||||
* Subscribe to the [mailing list](http://groups.google.com/forum/?fromgroups#!forum/angular). Ask questions here!
|
||||
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F®ion=follow_link&screen_name=angularjs&source=followbutton&variant=2.0)
|
||||
* Add us to your circles on [Google+](https://plus.google.com/110323587230527980117/posts)
|
||||
|
||||
##Read more
|
||||
## Read more
|
||||
|
||||
The AngularJS documentation includes the {@link guide/index Developer Guide} covering concepts and the
|
||||
{@link ./api API Reference} for syntax and usage.
|
||||
|
||||
+44
-5
@@ -1,5 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var stripJsonComments = require('strip-json-comments');
|
||||
|
||||
var gulp = require('gulp');
|
||||
var log = require('gulp-util').log;
|
||||
var concat = require('gulp-concat');
|
||||
@@ -25,6 +29,23 @@ var ignoredFiles = '!src/angular.bind.js';
|
||||
var assets = 'app/assets/**/*';
|
||||
|
||||
|
||||
var getJshintConfig = function(filepath) {
|
||||
return JSON.parse(stripJsonComments(fs.readFileSync(filepath, {encoding: 'utf-8'})));
|
||||
};
|
||||
|
||||
var getMergedJshintConfig = function(filepath) {
|
||||
// "extends" doesn't work in configuration passed by an object, we need to do the extending ourselves.
|
||||
var config = getJshintConfig(filepath);
|
||||
var baseConfig = getJshintConfig('../.jshintrc-base');
|
||||
_.merge(config, baseConfig);
|
||||
delete config.extends;
|
||||
|
||||
// Examples don't run in strict mode; accept that for now.
|
||||
config.strict = false;
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
pattern = pattern || '/**/*';
|
||||
sourceFolder = sourceFolder || bowerFolder;
|
||||
@@ -90,17 +111,35 @@ gulp.task('assets', ['bower'], function() {
|
||||
|
||||
gulp.task('doc-gen', ['bower'], function() {
|
||||
var dgeni = new Dgeni([require('./config')]);
|
||||
return dgeni.generate().catch(function(error) {
|
||||
return dgeni.generate().catch(function() {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// JSHint the example and protractor test files
|
||||
gulp.task('jshint', ['doc-gen'], function() {
|
||||
gulp.src([outputFolder + '/ptore2e/**/*.js', outputFolder + '/examples/**/*.js'])
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('jshint-stylish'))
|
||||
.pipe(jshint.reporter('fail'));
|
||||
var examplesConfig = getMergedJshintConfig('../docs/app/test/.jshintrc');
|
||||
// Some tests use `alert` which is not assumed to be available even with `"browser": true`.
|
||||
examplesConfig.globals.alert = false;
|
||||
|
||||
var protractorConfig = getMergedJshintConfig('../docs/app/e2e/.jshintrc');
|
||||
|
||||
return merge(
|
||||
gulp.src([
|
||||
outputFolder + '/examples/**/*.js',
|
||||
'!' + outputFolder + '/examples/**/protractor.js',
|
||||
])
|
||||
.pipe(jshint(examplesConfig))
|
||||
.pipe(jshint.reporter('jshint-stylish'))
|
||||
.pipe(jshint.reporter('fail')),
|
||||
gulp.src([
|
||||
outputFolder + '/ptore2e/**/*.js',
|
||||
outputFolder + '/examples/**/protractor.js',
|
||||
])
|
||||
.pipe(jshint(protractorConfig))
|
||||
.pipe(jshint.reporter('jshint-stylish'))
|
||||
.pipe(jshint.reporter('fail'))
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -9,4 +9,8 @@ npm run test-i18n
|
||||
|
||||
node src/closureSlurper.js
|
||||
|
||||
npm run test-i18n-ucd
|
||||
|
||||
echo "Generating ngParseExt"
|
||||
node ucd/src/extract.js
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
var extractValues = require('../src/extractValues.js').extractValues;
|
||||
var stream = require('stream');
|
||||
|
||||
function StringStream(str) {
|
||||
return new stream.Readable({
|
||||
read: function(n) {
|
||||
this.push(str);
|
||||
str = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('extractValues', function() {
|
||||
it('should extract the values from the xml', function(done) {
|
||||
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="N"></char></repertoire></ucd>';
|
||||
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
|
||||
expect(values).toEqual({ IDS_Y : [ [ '0001', '0002' ] ] });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extract the values from the xml if the last element matches', function(done) {
|
||||
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="Y"></char></repertoire></ucd>';
|
||||
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
|
||||
expect(values).toEqual({ IDS_Y : [ [ '0001', '0003' ] ] });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support `reserved`', function(done) {
|
||||
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><reserved first-cp="0002" last-cp="0005" IDS="N"></reserved><char cp="0006" IDS="Y"></char></repertoire></ucd>';
|
||||
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
|
||||
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support `surrogate`', function(done) {
|
||||
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><surrogate first-cp="0002" last-cp="0005" IDS="N"></surrogate><char cp="0006" IDS="Y"></char></repertoire></ucd>';
|
||||
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
|
||||
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support `noncharactere`', function(done) {
|
||||
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><noncharacter first-cp="0002" last-cp="0005" IDS="N"></noncharacter><char cp="0006" IDS="Y"></char></repertoire></ucd>';
|
||||
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
|
||||
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
var generateCodeModule = require('../src/generateCode.js');
|
||||
var generateCode = generateCodeModule.generateCode;
|
||||
var generateFunction = generateCodeModule.generateFunction;
|
||||
|
||||
describe('generateFunction', function() {
|
||||
it('should generate function with ranges', function() {
|
||||
expect(generateFunction([ [ '0001', '0003' ] ], 'IDS_Y')).toEqual('\
|
||||
function IDS_Y(cp) {\n\
|
||||
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
|
||||
return false;\n\
|
||||
}\n');
|
||||
});
|
||||
|
||||
it('should generate function with multiple ranges', function() {
|
||||
expect(generateFunction([ [ '0001', '0003' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
|
||||
function IDS_Y(cp) {\n\
|
||||
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
|
||||
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
|
||||
return false;\n\
|
||||
}\n');
|
||||
});
|
||||
|
||||
it('should generate function with unique values', function() {
|
||||
expect(generateFunction([ [ '0001', '0001' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
|
||||
function IDS_Y(cp) {\n\
|
||||
if (cp === 0x0001) return true;\n\
|
||||
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
|
||||
return false;\n\
|
||||
}\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateCode', function() {
|
||||
it('should generate the function for all the values', function() {
|
||||
expect(generateCode({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ], IDC_Y : [ [ '0002', '0002' ], [ '0007', '0007' ] ] })).toEqual('\
|
||||
/******************************************************\n\
|
||||
* Generated file, do not modify *\n\
|
||||
* *\n\
|
||||
*****************************************************/\n\
|
||||
"use strict";\n\
|
||||
function IDS_Y(cp) {\n\
|
||||
if (cp === 0x0001) return true;\n\
|
||||
if (cp === 0x0006) return true;\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
function IDC_Y(cp) {\n\
|
||||
if (cp === 0x0002) return true;\n\
|
||||
if (cp === 0x0007) return true;\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
var fs = require('fs');
|
||||
var zlib = require('zlib');
|
||||
var extractValues = require('./extractValues').extractValues;
|
||||
var generateCode = require('./generateCode').generateCode;
|
||||
var generateextractValues = require('./extractValues').extractValues;
|
||||
// ID_Start and ID_Continue
|
||||
var propertiesToExtract = {'IDS': 'Y', 'IDC': 'Y'};
|
||||
|
||||
function main() {
|
||||
extractValues(
|
||||
fs.createReadStream('./ucd/src/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
|
||||
propertiesToExtract,
|
||||
writeFile);
|
||||
|
||||
function writeFile(validRanges) {
|
||||
var code = generateCode(validRanges);
|
||||
try {
|
||||
var stats = fs.lstatSync('../src/ngParseExt');
|
||||
} catch (e) {
|
||||
fs.mkdirSync('../src/ngParseExt');
|
||||
}
|
||||
fs.writeFile('../src/ngParseExt/ucd.js', code);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Extract values from a stream.
|
||||
*/
|
||||
|
||||
exports.extractValues = extractValues;
|
||||
|
||||
var sax = require('sax/lib/sax');
|
||||
var saxStrict = true;
|
||||
var saxOptions = {};
|
||||
var validXMLTagNames = { char: 'Y', reserved: 'Y', surrogate: 'Y', noncharacter: 'Y'};
|
||||
|
||||
function extractValues(stream, propertiesToExtract, callback) {
|
||||
var saxStream = sax.createStream(saxStrict, saxOptions);
|
||||
var firstValid = {};
|
||||
var lastValid = {};
|
||||
var keys = Object.keys(propertiesToExtract);
|
||||
var keyValues = keys.map(function(k) { return propertiesToExtract[k]; });
|
||||
var validRanges = {};
|
||||
|
||||
for (var i in keys) {
|
||||
validRanges[keys[i] + '_' + keyValues[i]] = [];
|
||||
}
|
||||
saxStream.onopentag = onOpenTag;
|
||||
stream
|
||||
.pipe(saxStream)
|
||||
.on('end', doCallback);
|
||||
|
||||
function onOpenTag(node) {
|
||||
var property;
|
||||
if (validXMLTagNames[node.name]) {
|
||||
for (var i in keys) {
|
||||
property = keyValues[i];
|
||||
if (node.attributes[keys[i]] === property) validProperty(keys[i] + '_' + property, node);
|
||||
else invalidProperty(keys[i] + '_' + property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validProperty(property, node) {
|
||||
if (!firstValid[property]) firstValid[property] =
|
||||
node.attributes.cp || node.attributes['first-cp'];
|
||||
lastValid[property] = node.attributes.cp || node.attributes['last-cp'];
|
||||
}
|
||||
|
||||
function invalidProperty(property) {
|
||||
if (!firstValid[property]) return;
|
||||
validRanges[property].push([firstValid[property], lastValid[property]]);
|
||||
firstValid[property] = null;
|
||||
}
|
||||
|
||||
function doCallback() {
|
||||
for (var i in keys) {
|
||||
property = keys[i] + '_' + keyValues[i];
|
||||
invalidProperty(property);
|
||||
}
|
||||
callback(validRanges);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
exports.generateCode = generateCode;
|
||||
exports.generateFunction = generateFunction;
|
||||
|
||||
function generateCode(validRanges) {
|
||||
var code = '/******************************************************\n' +
|
||||
' * Generated file, do not modify *\n' +
|
||||
' * *\n' +
|
||||
' *****************************************************/\n' +
|
||||
'"use strict";\n';
|
||||
var keys = Object.keys(validRanges);
|
||||
for (var i in keys) {
|
||||
code += generateFunction(validRanges[keys[i]], keys[i]);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
function generateFunction(positiveElements, functionName) {
|
||||
var result = [];
|
||||
result.push('function ', functionName, '(cp) {\n');
|
||||
positiveElements.forEach(function(range) {
|
||||
if (range[0] === range[1]) {
|
||||
result.push(' if (cp === 0x', range[0], ')');
|
||||
} else {
|
||||
result.push(' if (0x', range[0], ' <= cp && cp <= 0x', range[1], ')');
|
||||
}
|
||||
result.push(' return true;\n');
|
||||
});
|
||||
result.push(' return false;\n}\n');
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
Binary file not shown.
+2056
-4338
File diff suppressed because it is too large
Load Diff
Generated
+3754
-7281
File diff suppressed because it is too large
Load Diff
+14
-9
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "^1.5.0-beta.2",
|
||||
"branchVersion": "1.5.x",
|
||||
"branchPattern": "1.5.*",
|
||||
"distTag": "beta",
|
||||
"distTag": "latest",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -17,7 +17,8 @@
|
||||
"preinstall": "node scripts/npm/check-node-modules.js --purge",
|
||||
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
|
||||
"commit": "git-cz",
|
||||
"test-i18n": "jasmine-node i18n/spec"
|
||||
"test-i18n": "jasmine-node i18n/spec",
|
||||
"test-i18n-ucd": "jasmine-node i18n/ucd/spec"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
@@ -29,7 +30,7 @@
|
||||
"commitizen": "^2.3.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.11.0",
|
||||
"dgeni-packages": "^0.12.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"glob": "^6.0.1",
|
||||
"grunt": "~0.4.2",
|
||||
@@ -38,7 +39,7 @@
|
||||
"grunt-contrib-compress": "~0.12.0",
|
||||
"grunt-contrib-connect": "~0.8.0",
|
||||
"grunt-contrib-copy": "~0.6.0",
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-contrib-jshint": "^1.0.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs": "^2.1.0",
|
||||
@@ -47,19 +48,21 @@
|
||||
"gulp": "~3.8.0",
|
||||
"gulp-concat": "^2.4.1",
|
||||
"gulp-foreach": "0.0.1",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"gulp-jshint": "^2.0.0",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
"jshint-stylish": "~1.0.0",
|
||||
"jshint": "^2.9.1",
|
||||
"jshint-stylish": "^2.1.0",
|
||||
"karma": "^0.13.19",
|
||||
"karma-browserstack-launcher": "^0.1.8",
|
||||
"karma-chrome-launcher": "^0.2.2",
|
||||
"karma-firefox-launcher": "^0.1.7",
|
||||
"karma-jasmine": "^0.1.6",
|
||||
"karma-jasmine": "^0.3.7",
|
||||
"karma-junit-reporter": "^0.3.8",
|
||||
"karma-ng-scenario": "^0.1.0",
|
||||
"karma-sauce-launcher": "^0.3.0",
|
||||
@@ -74,10 +77,12 @@
|
||||
"q-io": "^1.10.9",
|
||||
"qq": "^0.3.5",
|
||||
"rewire": "~2.1.0",
|
||||
"sax": "^1.1.1",
|
||||
"semver": "~4.0.3",
|
||||
"shelljs": "~0.3.0",
|
||||
"sorted-object": "^1.0.0",
|
||||
"stringmap": "^0.2.2"
|
||||
"stringmap": "^0.2.2",
|
||||
"strip-json-comments": "^2.0.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
@@ -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'
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ REPOS=(
|
||||
angular-message-format
|
||||
angular-messages
|
||||
angular-mocks
|
||||
angular-parse-ext
|
||||
angular-resource
|
||||
angular-route
|
||||
angular-sanitize
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
/* auto/injector.js */
|
||||
"createInjector": false,
|
||||
|
||||
|
||||
+45
-9
@@ -102,6 +102,7 @@
|
||||
* @ngdoc module
|
||||
* @name ng
|
||||
* @module ng
|
||||
* @installation
|
||||
* @description
|
||||
*
|
||||
* # ng (core module)
|
||||
@@ -168,7 +169,7 @@ var
|
||||
* documentMode is an IE-only property
|
||||
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
|
||||
*/
|
||||
msie = document.documentMode;
|
||||
msie = window.document.documentMode;
|
||||
|
||||
|
||||
/**
|
||||
@@ -457,7 +458,7 @@ function identity($) {return $;}
|
||||
identity.$inject = [];
|
||||
|
||||
|
||||
function valueFn(value) {return function() {return value;};}
|
||||
function valueFn(value) {return function valueRef() {return value;};}
|
||||
|
||||
function hasCustomToString(obj) {
|
||||
return isFunction(obj.toString) && obj.toString !== toString;
|
||||
@@ -819,7 +820,7 @@ function copy(source, destination) {
|
||||
|
||||
function copyRecurse(source, destination) {
|
||||
var h = destination.$$hashKey;
|
||||
var result, key;
|
||||
var key;
|
||||
if (isArray(source)) {
|
||||
for (var i = 0, ii = source.length; i < ii; i++) {
|
||||
destination.push(copyElement(source[i]));
|
||||
@@ -978,6 +979,41 @@ function shallowCopy(src, dst) {
|
||||
* @param {*} o1 Object or value to compare.
|
||||
* @param {*} o2 Object or value to compare.
|
||||
* @returns {boolean} True if arguments are equal.
|
||||
*
|
||||
* @example
|
||||
<example module="equalsExample" name="equalsExample">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<form novalidate>
|
||||
<h3>User 1</h3>
|
||||
Name: <input type="text" ng-model="user1.name">
|
||||
Age: <input type="number" ng-model="user1.age">
|
||||
|
||||
<h3>User 2</h3>
|
||||
Name: <input type="text" ng-model="user2.name">
|
||||
Age: <input type="number" ng-model="user2.age">
|
||||
|
||||
<div>
|
||||
<br/>
|
||||
<input type="button" value="Compare" ng-click="compare()">
|
||||
</div>
|
||||
User 1: <pre>{{user1 | json}}</pre>
|
||||
User 2: <pre>{{user2 | json}}</pre>
|
||||
Equal: <pre>{{result}}</pre>
|
||||
</form>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.user1 = {};
|
||||
$scope.user2 = {};
|
||||
$scope.result;
|
||||
$scope.compare = function() {
|
||||
$scope.result = angular.equals($scope.user1, $scope.user2);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function equals(o1, o2) {
|
||||
if (o1 === o2) return true;
|
||||
@@ -1024,8 +1060,8 @@ var csp = function() {
|
||||
if (!isDefined(csp.rules)) {
|
||||
|
||||
|
||||
var ngCspElement = (document.querySelector('[ng-csp]') ||
|
||||
document.querySelector('[data-ng-csp]'));
|
||||
var ngCspElement = (window.document.querySelector('[ng-csp]') ||
|
||||
window.document.querySelector('[data-ng-csp]'));
|
||||
|
||||
if (ngCspElement) {
|
||||
var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
|
||||
@@ -1100,7 +1136,7 @@ var jq = function() {
|
||||
var i, ii = ngAttrPrefixes.length, prefix, name;
|
||||
for (i = 0; i < ii; ++i) {
|
||||
prefix = ngAttrPrefixes[i];
|
||||
if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
|
||||
if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
|
||||
name = el.getAttribute(prefix + 'jq');
|
||||
break;
|
||||
}
|
||||
@@ -1165,7 +1201,7 @@ function toJsonReplacer(key, value) {
|
||||
val = undefined;
|
||||
} else if (isWindow(value)) {
|
||||
val = '$WINDOW';
|
||||
} else if (value && document === value) {
|
||||
} else if (value && window.document === value) {
|
||||
val = '$DOCUMENT';
|
||||
} else if (isScope(value)) {
|
||||
val = '$SCOPE';
|
||||
@@ -1617,11 +1653,11 @@ function bootstrap(element, modules, config) {
|
||||
element = jqLite(element);
|
||||
|
||||
if (element.injector()) {
|
||||
var tag = (element[0] === document) ? 'document' : startingTag(element);
|
||||
var tag = (element[0] === window.document) ? 'document' : startingTag(element);
|
||||
//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(/>/,'>'));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document, undefined) {
|
||||
(function(window) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
jqLite(document).ready(function() {
|
||||
angularInit(document, bootstrap);
|
||||
jqLite(window.document).ready(function() {
|
||||
angularInit(window.document, bootstrap);
|
||||
});
|
||||
|
||||
})(window, document);
|
||||
})(window);
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name auto
|
||||
* @installation
|
||||
* @description
|
||||
*
|
||||
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
|
||||
@@ -70,7 +71,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
var $injectorMinErr = minErr('$injector');
|
||||
|
||||
function extractArgs(fn) {
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
|
||||
var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
|
||||
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
|
||||
return args;
|
||||
}
|
||||
|
||||
+7
-4
@@ -114,6 +114,9 @@
|
||||
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
|
||||
* parent element is reached.
|
||||
*
|
||||
* @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
|
||||
* https://github.com/angular/angular.js/issues/14251 for more information.
|
||||
*
|
||||
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
|
||||
* @returns {Object} jQuery object.
|
||||
*/
|
||||
@@ -240,7 +243,7 @@ function jqLiteBuildFragment(html, context) {
|
||||
}
|
||||
|
||||
function jqLiteParseHTML(html, context) {
|
||||
context = context || document;
|
||||
context = context || window.document;
|
||||
var parsed;
|
||||
|
||||
if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
|
||||
@@ -266,7 +269,7 @@ function jqLiteWrapNode(node, wrapper) {
|
||||
|
||||
|
||||
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
||||
var jqLiteContains = Node.prototype.contains || function(arg) {
|
||||
var jqLiteContains = window.Node.prototype.contains || function(arg) {
|
||||
// jshint bitwise: false
|
||||
return !!(this.compareDocumentPosition(arg) & 16);
|
||||
// jshint bitwise: true
|
||||
@@ -538,8 +541,8 @@ var JQLitePrototype = JQLite.prototype = {
|
||||
}
|
||||
|
||||
// check if document is already loaded
|
||||
if (document.readyState === 'complete') {
|
||||
setTimeout(trigger);
|
||||
if (window.document.readyState === 'complete') {
|
||||
window.setTimeout(trigger);
|
||||
} else {
|
||||
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
|
||||
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
|
||||
|
||||
+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()}.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
(function(window, angular) {
|
||||
|
||||
+6
-1
@@ -326,6 +326,9 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* // remove all the animation event listeners listening for `enter`
|
||||
* $animate.off('enter');
|
||||
*
|
||||
* // remove listeners for all animation events from the container element
|
||||
* $animate.off(container);
|
||||
*
|
||||
* // remove all the animation event listeners listening for `enter` on the given element and its children
|
||||
* $animate.off('enter', container);
|
||||
*
|
||||
@@ -334,7 +337,9 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* $animate.off('enter', container, callback);
|
||||
* ```
|
||||
*
|
||||
* @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
|
||||
* @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
|
||||
* addClass, removeClass, etc...), or the container element. If it is the element, all other
|
||||
* arguments are ignored.
|
||||
* @param {DOMElement=} container the container element the event listener was placed on
|
||||
* @param {Function=} callback the callback function that was registered as the listener
|
||||
*/
|
||||
|
||||
+8
-10
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
function Browser(window, document, $log, $sniffer) {
|
||||
var self = this,
|
||||
rawDocument = document[0],
|
||||
location = window.location,
|
||||
history = window.history,
|
||||
setTimeout = window.setTimeout,
|
||||
@@ -87,7 +86,14 @@ function Browser(window, document, $log, $sniffer) {
|
||||
var cachedState, lastHistoryState,
|
||||
lastBrowserUrl = location.href,
|
||||
baseElement = document.find('base'),
|
||||
pendingLocation = null;
|
||||
pendingLocation = null,
|
||||
getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
|
||||
try {
|
||||
return history.state;
|
||||
} catch (e) {
|
||||
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
|
||||
}
|
||||
};
|
||||
|
||||
cacheState();
|
||||
lastHistoryState = cachedState;
|
||||
@@ -195,14 +201,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
fireUrlChange();
|
||||
}
|
||||
|
||||
function getCurrentState() {
|
||||
try {
|
||||
return history.state;
|
||||
} catch (e) {
|
||||
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
|
||||
}
|
||||
}
|
||||
|
||||
// This variable should be used *only* inside the cacheState function.
|
||||
var lastCachedState = null;
|
||||
function cacheState() {
|
||||
|
||||
+227
-47
@@ -293,9 +293,23 @@
|
||||
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
|
||||
*
|
||||
* The controller can provide the following methods that act as life-cycle hooks:
|
||||
* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
|
||||
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
|
||||
* had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
* this element). This is a good place to put initialization code for your controller.
|
||||
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
|
||||
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
|
||||
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
|
||||
* component such as cloning the bound value to prevent accidental mutation of the outer value.
|
||||
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
|
||||
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
|
||||
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
|
||||
* components will have their `$onDestroy()` hook called before child components.
|
||||
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
|
||||
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
|
||||
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
|
||||
* they are waiting for their template to load asynchronously and their own compilation and linking has been
|
||||
* suspended until that occurs.
|
||||
*
|
||||
*
|
||||
* #### `require`
|
||||
* Require another directive and inject its controller as the fourth argument to the linking function. The
|
||||
@@ -832,6 +846,9 @@
|
||||
|
||||
var $compileMinErr = minErr('$compile');
|
||||
|
||||
function UNINITIALIZED_VALUE() {}
|
||||
var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $compileProvider
|
||||
@@ -856,7 +873,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
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) {
|
||||
@@ -928,11 +945,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
function assertValidDirectiveName(name) {
|
||||
var letter = name.charAt(0);
|
||||
if (!letter || letter !== lowercase(letter)) {
|
||||
throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
|
||||
throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
|
||||
}
|
||||
if (name !== name.trim()) {
|
||||
throw $compileMinErr('baddir',
|
||||
"Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
|
||||
"Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
|
||||
name);
|
||||
}
|
||||
}
|
||||
@@ -1075,7 +1092,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
this.component = function registerComponent(name, options) {
|
||||
var controller = options.controller || noop;
|
||||
var controller = options.controller || function() {};
|
||||
|
||||
function factory($injector) {
|
||||
function makeInjectable(fn) {
|
||||
@@ -1089,7 +1106,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
var template = (!options.template && !options.templateUrl ? '' : options.template);
|
||||
return {
|
||||
var ddo = {
|
||||
controller: controller,
|
||||
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
|
||||
template: makeInjectable(template),
|
||||
@@ -1100,14 +1117,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
restrict: 'E',
|
||||
require: options.require
|
||||
};
|
||||
|
||||
// Copy annotations (starting with $) over to the DDO
|
||||
forEach(options, function(val, key) {
|
||||
if (key.charAt(0) === '$') ddo[key] = val;
|
||||
});
|
||||
|
||||
return ddo;
|
||||
}
|
||||
|
||||
// Copy any annotation properties (starting with $) over to the factory function
|
||||
// TODO(pete) remove the following `forEach` before we release 1.6.0
|
||||
// The component-router@0.2.0 looks for the annotations on the controller constructor
|
||||
// Nothing in Angular looks for annotations on the factory function but we can't remove
|
||||
// it from 1.5.x yet.
|
||||
|
||||
// Copy any annotation properties (starting with $) over to the factory and controller constructor functions
|
||||
// These could be used by libraries such as the new component router
|
||||
forEach(options, function(val, key) {
|
||||
if (key.charAt(0) === '$') {
|
||||
factory[key] = val;
|
||||
controller[key] = val;
|
||||
// Don't try to copy over annotations to named controller
|
||||
if (isFunction(controller)) controller[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1207,6 +1237,36 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return debugInfoEnabled;
|
||||
};
|
||||
|
||||
|
||||
var TTL = 10;
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $compileProvider#onChangesTtl
|
||||
* @description
|
||||
*
|
||||
* Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
|
||||
* assuming that the model is unstable.
|
||||
*
|
||||
* The current default is 10 iterations.
|
||||
*
|
||||
* In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
|
||||
* in several iterations of calls to these hooks. However if an application needs more than the default 10
|
||||
* iterations to stabilize then you should investigate what is causing the model to continuously change during
|
||||
* the `$onChanges` hook execution.
|
||||
*
|
||||
* Increasing the TTL could have performance implications, so you should not change it without proper justification.
|
||||
*
|
||||
* @param {number} limit The number of `$onChanges` hook iterations.
|
||||
* @returns {number|object} the current limit (or `this` if called as a setter for chaining)
|
||||
*/
|
||||
this.onChangesTtl = function(value) {
|
||||
if (arguments.length) {
|
||||
TTL = value;
|
||||
return this;
|
||||
}
|
||||
return TTL;
|
||||
};
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
|
||||
'$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
|
||||
@@ -1214,8 +1274,38 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
$controller, $rootScope, $sce, $animate, $$sanitizeUri) {
|
||||
|
||||
var SIMPLE_ATTR_NAME = /^\w/;
|
||||
var specialAttrHolder = document.createElement('div');
|
||||
var Attributes = function(element, attributesToCopy) {
|
||||
var specialAttrHolder = window.document.createElement('div');
|
||||
|
||||
|
||||
|
||||
var onChangesTtl = TTL;
|
||||
// The onChanges hooks should all be run together in a single digest
|
||||
// When changes occur, the call to trigger their hooks will be added to this queue
|
||||
var onChangesQueue;
|
||||
|
||||
// This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
|
||||
function flushOnChangesQueue() {
|
||||
try {
|
||||
if (!(--onChangesTtl)) {
|
||||
// We have hit the TTL limit so reset everything
|
||||
onChangesQueue = undefined;
|
||||
throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
|
||||
}
|
||||
// We must run this hook in an apply since the $$postDigest runs outside apply
|
||||
$rootScope.$apply(function() {
|
||||
for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
|
||||
onChangesQueue[i]();
|
||||
}
|
||||
// Reset the queue to trigger a new schedule next time there is a change
|
||||
onChangesQueue = undefined;
|
||||
});
|
||||
} finally {
|
||||
onChangesTtl++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Attributes(element, attributesToCopy) {
|
||||
if (attributesToCopy) {
|
||||
var keys = Object.keys(attributesToCopy);
|
||||
var i, l, key;
|
||||
@@ -1229,7 +1319,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
this.$$element = element;
|
||||
};
|
||||
}
|
||||
|
||||
Attributes.prototype = {
|
||||
/**
|
||||
@@ -1515,7 +1605,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (debugInfoEnabled) {
|
||||
content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
|
||||
}
|
||||
return document.createComment(content);
|
||||
return window.document.createComment(content);
|
||||
};
|
||||
|
||||
return compile;
|
||||
@@ -1529,6 +1619,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>
|
||||
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] = window.document.createElement('span'));
|
||||
}
|
||||
}
|
||||
|
||||
var compositeLinkFn =
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
@@ -1718,8 +1821,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
|
||||
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
|
||||
if (!transcludedScope) {
|
||||
transcludedScope = scope.$new(false, containingScope);
|
||||
@@ -1731,7 +1833,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
transcludeControllers: controllers,
|
||||
futureParentElement: futureParentElement
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// We need to attach the transclusion slots onto the `boundTranscludeFn`
|
||||
// so that they are available inside the `controllersBoundTransclude` function
|
||||
@@ -1896,7 +1998,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
|
||||
return function(scope, element, attrs, controllers, transcludeFn) {
|
||||
return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
|
||||
element = groupScan(element[0], attrStart, attrEnd);
|
||||
return linkFn(scope, element, attrs, controllers, transcludeFn);
|
||||
};
|
||||
@@ -1919,7 +2021,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (eager) {
|
||||
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
|
||||
}
|
||||
return function() {
|
||||
return function lazyCompilation() {
|
||||
if (!compiled) {
|
||||
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
|
||||
|
||||
@@ -2068,6 +2170,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
compileNode = $compileNode[0];
|
||||
replaceWith(jqCollection, sliceArgs($template), compileNode);
|
||||
|
||||
// Support: Chrome < 50
|
||||
// https://github.com/angular/angular.js/issues/14041
|
||||
|
||||
// In the versions of V8 prior to Chrome 50, the document fragment that is created
|
||||
// in the `replaceWith` function is improperly garbage collected despite still
|
||||
// being referenced by the `parentNode` property of all of the child nodes. By adding
|
||||
// a reference to the fragment via a different property, we can avoid that incorrect
|
||||
// behavior.
|
||||
// TODO: remove this line after Chrome 50 has been released
|
||||
$template[0].$$parentNode = $template[0].parentNode;
|
||||
|
||||
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
|
||||
replaceDirective && replaceDirective.name, {
|
||||
// Don't pass in:
|
||||
@@ -2208,7 +2321,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
replaceDirective = directive;
|
||||
}
|
||||
|
||||
/* jshint -W021 */
|
||||
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
|
||||
/* jshint +W021 */
|
||||
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
|
||||
controllerDirectives: controllerDirectives,
|
||||
newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
|
||||
@@ -2272,7 +2387,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
|
||||
var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
|
||||
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
|
||||
attrs, scopeBindingInfo;
|
||||
|
||||
if (compileNode === linkNode) {
|
||||
attrs = templateAttrs;
|
||||
@@ -2311,23 +2426,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
compile.$$addScopeClass($element, true);
|
||||
isolateScope.$$isolateBindings =
|
||||
newIsolateScopeDirective.$$isolateBindings;
|
||||
removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
|
||||
scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
|
||||
isolateScope.$$isolateBindings,
|
||||
newIsolateScopeDirective);
|
||||
if (removeScopeBindingWatches) {
|
||||
isolateScope.$on('$destroy', removeScopeBindingWatches);
|
||||
if (scopeBindingInfo.removeWatches) {
|
||||
isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize bindToController bindings
|
||||
// Initialize controllers
|
||||
for (var name in elementControllers) {
|
||||
var controllerDirective = controllerDirectives[name];
|
||||
var controller = elementControllers[name];
|
||||
var bindings = controllerDirective.$$bindings.bindToController;
|
||||
|
||||
// Initialize bindToController bindings
|
||||
if (controller.identifier && bindings) {
|
||||
removeControllerBindingWatches =
|
||||
controller.bindingInfo =
|
||||
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
|
||||
} else {
|
||||
controller.bindingInfo = {};
|
||||
}
|
||||
|
||||
var controllerResult = controller();
|
||||
@@ -2335,11 +2453,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// If the controller constructor has a return value, overwrite the instance
|
||||
// from setupControllers
|
||||
controller.instance = controllerResult;
|
||||
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
|
||||
removeControllerBindingWatches && removeControllerBindingWatches();
|
||||
removeControllerBindingWatches =
|
||||
controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
|
||||
controller.bindingInfo =
|
||||
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
|
||||
}
|
||||
|
||||
// Store controllers on the $element data
|
||||
// For transclude comment nodes this will be a noop and will be done at transclusion time
|
||||
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
|
||||
}
|
||||
|
||||
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
|
||||
@@ -2350,10 +2471,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger the `$onInit` method on all controllers that have one
|
||||
// Handle the init and destroy lifecycle hooks on all controllers that have them
|
||||
forEach(elementControllers, function(controller) {
|
||||
if (isFunction(controller.instance.$onInit)) {
|
||||
controller.instance.$onInit();
|
||||
var controllerInstance = controller.instance;
|
||||
if (isFunction(controllerInstance.$onChanges)) {
|
||||
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
|
||||
}
|
||||
if (isFunction(controllerInstance.$onInit)) {
|
||||
controllerInstance.$onInit();
|
||||
}
|
||||
if (isFunction(controllerInstance.$onDestroy)) {
|
||||
controllerScope.$on('$destroy', function callOnDestroyHook() {
|
||||
controllerInstance.$onDestroy();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2390,6 +2520,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
);
|
||||
}
|
||||
|
||||
// Trigger $postLink lifecycle hooks
|
||||
forEach(elementControllers, function(controller) {
|
||||
var controllerInstance = controller.instance;
|
||||
if (isFunction(controllerInstance.$postLink)) {
|
||||
controllerInstance.$postLink();
|
||||
}
|
||||
});
|
||||
|
||||
// This is the function that is injected as `$transclude`.
|
||||
// Note: all arguments are optional!
|
||||
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
|
||||
@@ -2489,14 +2627,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
controller = attrs[directive.name];
|
||||
}
|
||||
|
||||
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
|
||||
|
||||
// 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;
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
|
||||
elementControllers[directive.name] = $controller(controller, locals, true, directive.controllerAs);
|
||||
}
|
||||
return elementControllers;
|
||||
}
|
||||
@@ -2795,7 +2926,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
switch (type) {
|
||||
case 'svg':
|
||||
case 'math':
|
||||
var wrapper = document.createElement('div');
|
||||
var wrapper = window.document.createElement('div');
|
||||
wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
|
||||
return wrapper.childNodes[0].childNodes;
|
||||
default:
|
||||
@@ -2939,7 +3070,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// - remove them from the DOM
|
||||
// - allow them to still be traversed with .nextSibling
|
||||
// - allow a single fragment.qSA to fetch all elements being removed
|
||||
var fragment = document.createDocumentFragment();
|
||||
var fragment = window.document.createDocumentFragment();
|
||||
for (i = 0; i < removeCount; i++) {
|
||||
fragment.appendChild(elementsToRemove[i]);
|
||||
}
|
||||
@@ -2985,7 +3116,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// only occurs for isolate scopes and new scopes with controllerAs.
|
||||
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
|
||||
var removeWatchCollection = [];
|
||||
forEach(bindings, function(definition, scopeName) {
|
||||
var initialChanges = {};
|
||||
var changes;
|
||||
forEach(bindings, function initializeBinding(definition, scopeName) {
|
||||
var attrName = definition.attrName,
|
||||
optional = definition.optional,
|
||||
mode = definition.mode, // @, =, or &
|
||||
@@ -2999,7 +3132,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
destination[scopeName] = attrs[attrName] = void 0;
|
||||
}
|
||||
attrs.$observe(attrName, function(value) {
|
||||
if (isString(value)) {
|
||||
if (isString(value) || isBoolean(value)) {
|
||||
var oldValue = destination[scopeName];
|
||||
recordChanges(scopeName, value, oldValue);
|
||||
destination[scopeName] = value;
|
||||
}
|
||||
});
|
||||
@@ -3014,6 +3149,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// the value to boolean rather than a string, so we special case this situation
|
||||
destination[scopeName] = lastValue;
|
||||
}
|
||||
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
@@ -3027,7 +3163,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function(a, b) { return a === b || (a !== a && b !== b); };
|
||||
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -3069,9 +3205,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
|
||||
destination[scopeName] = parentGet(scope);
|
||||
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
|
||||
|
||||
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
|
||||
destination[scopeName] = newParentValue;
|
||||
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
|
||||
if (newValue === oldValue) {
|
||||
// If the new and old values are identical then this is the first time the watch has been triggered
|
||||
// So instead we use the current value on the destination as the old value
|
||||
oldValue = destination[scopeName];
|
||||
}
|
||||
recordChanges(scopeName, newValue, oldValue);
|
||||
destination[scopeName] = newValue;
|
||||
}, parentGet.literal);
|
||||
|
||||
removeWatchCollection.push(removeWatch);
|
||||
@@ -3091,15 +3234,52 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
});
|
||||
|
||||
return removeWatchCollection.length && function removeWatches() {
|
||||
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
|
||||
removeWatchCollection[i]();
|
||||
function recordChanges(key, currentValue, previousValue) {
|
||||
if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
|
||||
// If we have not already scheduled the top level onChangesQueue handler then do so now
|
||||
if (!onChangesQueue) {
|
||||
scope.$$postDigest(flushOnChangesQueue);
|
||||
onChangesQueue = [];
|
||||
}
|
||||
// If we have not already queued a trigger of onChanges for this controller then do so now
|
||||
if (!changes) {
|
||||
changes = {};
|
||||
onChangesQueue.push(triggerOnChangesHook);
|
||||
}
|
||||
// If the has been a change on this property already then we need to reuse the previous value
|
||||
if (changes[key]) {
|
||||
previousValue = changes[key].previousValue;
|
||||
}
|
||||
// Store this change
|
||||
changes[key] = new SimpleChange(previousValue, currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
function triggerOnChangesHook() {
|
||||
destination.$onChanges(changes);
|
||||
// Now clear the changes so that we schedule onChanges when more changes arrive
|
||||
changes = undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
initialChanges: initialChanges,
|
||||
removeWatches: removeWatchCollection.length && function removeWatches() {
|
||||
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
|
||||
removeWatchCollection[i]();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
function SimpleChange(previous, current) {
|
||||
this.previousValue = previous;
|
||||
this.currentValue = current;
|
||||
}
|
||||
SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
|
||||
|
||||
|
||||
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
|
||||
/**
|
||||
* Converts all accepted directives format into proper directive name.
|
||||
|
||||
@@ -92,7 +92,7 @@ function $ControllerProvider() {
|
||||
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
|
||||
*/
|
||||
return function(expression, locals, later, ident) {
|
||||
return function $controller(expression, locals, later, ident) {
|
||||
// PRIVATE API:
|
||||
// param `later` --- indicates that the controller's constructor is invoked at a later time.
|
||||
// If true, $controller will allocate the object with the correct
|
||||
@@ -143,7 +143,7 @@ function $ControllerProvider() {
|
||||
}
|
||||
|
||||
var instantiate;
|
||||
return instantiate = extend(function() {
|
||||
return instantiate = extend(function $controllerInit() {
|
||||
var result = $injector.invoke(expression, instance, locals, constructor);
|
||||
if (result !== instance && (isObject(result) || isFunction(result))) {
|
||||
instance = result;
|
||||
|
||||
@@ -393,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">
|
||||
@@ -1114,7 +1114,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (!$sniffer.android) {
|
||||
var composing = false;
|
||||
|
||||
element.on('compositionstart', function(data) {
|
||||
element.on('compositionstart', function() {
|
||||
composing = true;
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -292,7 +292,7 @@ var ngIncludeFillContentDirective = ['$compile',
|
||||
// support innerHTML, so detect this here and try to generate the contents
|
||||
// specially.
|
||||
$element.empty();
|
||||
$compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
|
||||
$compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
|
||||
function namespaceAdaptedClone(clone) {
|
||||
$element.append(clone);
|
||||
}, {futureParentElement: $element});
|
||||
|
||||
@@ -265,9 +265,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
};
|
||||
ngModelSet = function($scope, newValue) {
|
||||
if (isFunction(parsedNgModel($scope))) {
|
||||
invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
|
||||
invokeModelSetter($scope, {$$$p: newValue});
|
||||
} else {
|
||||
parsedNgModelAssign($scope, ctrl.$modelValue);
|
||||
parsedNgModelAssign($scope, newValue);
|
||||
}
|
||||
};
|
||||
} else if (!parsedNgModel.assign) {
|
||||
@@ -644,7 +644,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
setValidity(name, undefined);
|
||||
validatorPromises.push(promise.then(function() {
|
||||
setValidity(name, true);
|
||||
}, function(error) {
|
||||
}, function() {
|
||||
allValid = false;
|
||||
setValidity(name, false);
|
||||
}));
|
||||
@@ -1118,7 +1118,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
|
||||
});
|
||||
}
|
||||
|
||||
element.on('blur', function(ev) {
|
||||
element.on('blur', function() {
|
||||
if (modelCtrl.$touched) return;
|
||||
|
||||
if ($rootScope.$$phase) {
|
||||
|
||||
+46
-102
@@ -245,7 +245,7 @@ var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s
|
||||
// jshint maxlen: 100
|
||||
|
||||
|
||||
var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
|
||||
|
||||
function parseOptionsExpression(optionsExp, selectElement, scope) {
|
||||
|
||||
@@ -342,8 +342,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
|
||||
var value = optionValues[key];
|
||||
|
||||
var locals = getLocals(optionValues[key], key);
|
||||
var selectValue = getTrackByValueFn(optionValues[key], locals);
|
||||
var locals = getLocals(value, key);
|
||||
var selectValue = getTrackByValueFn(value, locals);
|
||||
watchedArray.push(selectValue);
|
||||
|
||||
// Only need to watch the displayFn if there is a specific label expression
|
||||
@@ -406,8 +406,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
// we can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
var optionTemplate = document.createElement('option'),
|
||||
optGroupTemplate = document.createElement('optgroup');
|
||||
var optionTemplate = window.document.createElement('option'),
|
||||
optGroupTemplate = window.document.createElement('optgroup');
|
||||
|
||||
function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
|
||||
|
||||
@@ -432,7 +432,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
var options;
|
||||
var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
|
||||
|
||||
// This stores the newly created options before they are appended to the select.
|
||||
// Since the contents are removed from the fragment when it is appended,
|
||||
// we only need to create it once.
|
||||
var listFragment = $document[0].createDocumentFragment();
|
||||
|
||||
var renderEmptyOption = function() {
|
||||
if (!providedEmptyOption) {
|
||||
@@ -467,7 +470,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
selectCtrl.writeValue = function writeNgOptionsValue(value) {
|
||||
var option = options.getOptionFromViewValue(value);
|
||||
|
||||
if (option && !option.disabled) {
|
||||
if (option) {
|
||||
// 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
|
||||
@@ -529,7 +532,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
if (value) {
|
||||
value.forEach(function(item) {
|
||||
var option = options.getOptionFromViewValue(item);
|
||||
if (option && !option.disabled) option.element.selected = true;
|
||||
if (option) option.element.selected = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -581,6 +584,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
emptyOption = jqLite(optionTemplate.cloneNode(false));
|
||||
}
|
||||
|
||||
selectElement.empty();
|
||||
|
||||
// We need to do this here to ensure that the options object is defined
|
||||
// when we first hit it in writeNgOptionsValue
|
||||
updateOptions();
|
||||
@@ -590,6 +595,12 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
function addOptionElement(option, parent) {
|
||||
var optionElement = optionTemplate.cloneNode(false);
|
||||
parent.appendChild(optionElement);
|
||||
updateOptionElement(option, optionElement);
|
||||
}
|
||||
|
||||
|
||||
function updateOptionElement(option, element) {
|
||||
option.element = element;
|
||||
@@ -606,133 +617,66 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
if (option.value !== element.value) element.value = option.selectValue;
|
||||
}
|
||||
|
||||
function addOrReuseElement(parent, current, type, templateElement) {
|
||||
var element;
|
||||
// Check whether we can reuse the next element
|
||||
if (current && lowercase(current.nodeName) === type) {
|
||||
// The next element is the right type so reuse it
|
||||
element = current;
|
||||
} else {
|
||||
// The next element is not the right type so create a new one
|
||||
element = templateElement.cloneNode(false);
|
||||
if (!current) {
|
||||
// There are no more elements so just append it to the select
|
||||
parent.appendChild(element);
|
||||
} else {
|
||||
// The next element is not a group so insert the new one
|
||||
parent.insertBefore(element, current);
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
function removeExcessElements(current) {
|
||||
var next;
|
||||
while (current) {
|
||||
next = current.nextSibling;
|
||||
jqLiteRemove(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function skipEmptyAndUnknownOptions(current) {
|
||||
var emptyOption_ = emptyOption && emptyOption[0];
|
||||
var unknownOption_ = unknownOption && unknownOption[0];
|
||||
|
||||
// We cannot rely on the extracted empty option being the same as the compiled empty option,
|
||||
// because the compiled empty option might have been replaced by a comment because
|
||||
// it had an "element" transclusion directive on it (such as ngIf)
|
||||
if (emptyOption_ || unknownOption_) {
|
||||
while (current &&
|
||||
(current === emptyOption_ ||
|
||||
current === unknownOption_ ||
|
||||
current.nodeType === NODE_TYPE_COMMENT ||
|
||||
(nodeName_(current) === 'option' && current.value === ''))) {
|
||||
current = current.nextSibling;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
function updateOptions() {
|
||||
|
||||
var previousValue = options && selectCtrl.readValue();
|
||||
|
||||
// We must remove all current options, but cannot simply set innerHTML = null
|
||||
// since the providedEmptyOption might have an ngIf on it that inserts comments which we
|
||||
// must preserve.
|
||||
// Instead, iterate over the current option elements and remove them or their optgroup
|
||||
// parents
|
||||
if (options) {
|
||||
|
||||
for (var i = options.items.length - 1; i >= 0; i--) {
|
||||
var option = options.items[i];
|
||||
if (option.group) {
|
||||
jqLiteRemove(option.element.parentNode);
|
||||
} else {
|
||||
jqLiteRemove(option.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options = ngOptions.getOptions();
|
||||
|
||||
var groupMap = {};
|
||||
var currentElement = selectElement[0].firstChild;
|
||||
var groupElementMap = {};
|
||||
|
||||
// Ensure that the empty option is always there if it was explicitly provided
|
||||
if (providedEmptyOption) {
|
||||
selectElement.prepend(emptyOption);
|
||||
}
|
||||
|
||||
currentElement = skipEmptyAndUnknownOptions(currentElement);
|
||||
|
||||
options.items.forEach(function updateOption(option) {
|
||||
var group;
|
||||
options.items.forEach(function addOption(option) {
|
||||
var groupElement;
|
||||
var optionElement;
|
||||
|
||||
if (isDefined(option.group)) {
|
||||
|
||||
// This option is to live in a group
|
||||
// See if we have already created this group
|
||||
group = groupMap[option.group];
|
||||
groupElement = groupElementMap[option.group];
|
||||
|
||||
if (!group) {
|
||||
if (!groupElement) {
|
||||
|
||||
// We have not already created this group
|
||||
groupElement = addOrReuseElement(selectElement[0],
|
||||
currentElement,
|
||||
'optgroup',
|
||||
optGroupTemplate);
|
||||
// Move to the next element
|
||||
currentElement = groupElement.nextSibling;
|
||||
groupElement = optGroupTemplate.cloneNode(false);
|
||||
listFragment.appendChild(groupElement);
|
||||
|
||||
// Update the label on the group element
|
||||
groupElement.label = option.group;
|
||||
|
||||
// Store it for use later
|
||||
group = groupMap[option.group] = {
|
||||
groupElement: groupElement,
|
||||
currentOptionElement: groupElement.firstChild
|
||||
};
|
||||
|
||||
groupElementMap[option.group] = groupElement;
|
||||
}
|
||||
|
||||
// So now we have a group for this option we add the option to the group
|
||||
optionElement = addOrReuseElement(group.groupElement,
|
||||
group.currentOptionElement,
|
||||
'option',
|
||||
optionTemplate);
|
||||
updateOptionElement(option, optionElement);
|
||||
// Move to the next element
|
||||
group.currentOptionElement = optionElement.nextSibling;
|
||||
addOptionElement(option, groupElement);
|
||||
|
||||
} else {
|
||||
|
||||
// This option is not in a group
|
||||
optionElement = addOrReuseElement(selectElement[0],
|
||||
currentElement,
|
||||
'option',
|
||||
optionTemplate);
|
||||
updateOptionElement(option, optionElement);
|
||||
// Move to the next element
|
||||
currentElement = optionElement.nextSibling;
|
||||
addOptionElement(option, listFragment);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Now remove all excess options and group
|
||||
Object.keys(groupMap).forEach(function(key) {
|
||||
removeExcessElements(groupMap[key].currentOptionElement);
|
||||
});
|
||||
removeExcessElements(currentElement);
|
||||
selectElement[0].appendChild(listFragment);
|
||||
|
||||
ngModelCtrl.$render();
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
* <div ng-controller="ExampleController">
|
||||
* <input ng-model="title" aria-label="title"> <br/>
|
||||
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
|
||||
* <pane title="{{title}}"><span>{{text}}</span></pane>
|
||||
* <pane title="{{title}}">{{text}}</pane>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
|
||||
@@ -20,7 +20,7 @@ function chromeHack(optionElement) {
|
||||
* added `<option>` elements, perhaps by an `ngRepeat` directive.
|
||||
*/
|
||||
var SelectController =
|
||||
['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
['$element', '$scope', function($element, $scope) {
|
||||
|
||||
var self = this,
|
||||
optionsMap = new HashMap();
|
||||
@@ -34,7 +34,7 @@ var SelectController =
|
||||
//
|
||||
// We can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
self.unknownOption = jqLite(document.createElement('option'));
|
||||
self.unknownOption = jqLite(window.document.createElement('option'));
|
||||
self.renderUnknownOption = function(val) {
|
||||
var unknownVal = '? ' + hashKey(val) + ' ?';
|
||||
self.unknownOption.val(unknownVal);
|
||||
|
||||
@@ -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">
|
||||
@@ -323,7 +325,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
|
||||
// format the integer digits with grouping separators
|
||||
var groups = [];
|
||||
if (digits.length > pattern.lgSize) {
|
||||
if (digits.length >= pattern.lgSize) {
|
||||
groups.unshift(digits.splice(-pattern.lgSize).join(''));
|
||||
}
|
||||
while (digits.length > pattern.gSize) {
|
||||
|
||||
+28
-2
@@ -47,7 +47,7 @@ function $HttpParamSerializerProvider() {
|
||||
forEachSorted(params, function(value, key) {
|
||||
if (value === null || isUndefined(value)) return;
|
||||
if (isArray(value)) {
|
||||
forEach(value, function(v, k) {
|
||||
forEach(value, function(v) {
|
||||
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
|
||||
});
|
||||
} else {
|
||||
@@ -801,6 +801,12 @@ function $HttpProvider() {
|
||||
* - **headers** – `{Object}` – Map of strings or functions which return strings representing
|
||||
* HTTP headers to send to the server. If the return value of a function is null, the
|
||||
* header will not be sent. Functions accept a config object as an argument.
|
||||
* - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
|
||||
* To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
|
||||
* The handler will be called in the context of a `$apply` block.
|
||||
* - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
|
||||
* object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
|
||||
* The handler will be called in the context of a `$apply` block.
|
||||
* - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
|
||||
* - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
|
||||
* - **transformRequest** –
|
||||
@@ -1259,11 +1265,31 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
|
||||
config.withCredentials, config.responseType);
|
||||
config.withCredentials, config.responseType,
|
||||
createApplyHandlers(config.eventHandlers),
|
||||
createApplyHandlers(config.uploadEventHandlers));
|
||||
}
|
||||
|
||||
return promise;
|
||||
|
||||
function createApplyHandlers(eventHandlers) {
|
||||
if (eventHandlers) {
|
||||
var applyHandlers = {};
|
||||
forEach(eventHandlers, function(eventHandler, key) {
|
||||
applyHandlers[key] = function() {
|
||||
if (useApplyAsync) {
|
||||
$rootScope.$applyAsync(eventHandler);
|
||||
} else if ($rootScope.$$phase) {
|
||||
eventHandler();
|
||||
} else {
|
||||
$rootScope.$apply(eventHandler);
|
||||
}
|
||||
};
|
||||
});
|
||||
return applyHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback registered to $httpBackend():
|
||||
|
||||
@@ -54,7 +54,7 @@ function $HttpBackendProvider() {
|
||||
|
||||
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
|
||||
// TODO(vojta): fix the signature
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
url = url || $browser.url();
|
||||
|
||||
@@ -114,6 +114,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
xhr.onerror = requestError;
|
||||
xhr.onabort = requestError;
|
||||
|
||||
forEach(eventHandlers, function(value, key) {
|
||||
xhr.addEventListener(key, value);
|
||||
});
|
||||
|
||||
forEach(uploadEventHandlers, function(value, key) {
|
||||
xhr.upload.addEventListener(key, value);
|
||||
});
|
||||
|
||||
if (withCredentials) {
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
|
||||
+87
-9
@@ -150,7 +150,7 @@ Lexer.prototype = {
|
||||
this.readString(ch);
|
||||
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
|
||||
this.readNumber();
|
||||
} else if (this.isIdent(ch)) {
|
||||
} else if (this.isIdentifierStart(this.peekMultichar())) {
|
||||
this.readIdent();
|
||||
} else if (this.is(ch, '(){}[].,;:?')) {
|
||||
this.tokens.push({index: this.index, text: ch});
|
||||
@@ -194,12 +194,49 @@ Lexer.prototype = {
|
||||
ch === '\n' || ch === '\v' || ch === '\u00A0');
|
||||
},
|
||||
|
||||
isIdent: function(ch) {
|
||||
isIdentifierStart: function(ch) {
|
||||
return this.options.isIdentifierStart ?
|
||||
this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
|
||||
this.isValidIdentifierStart(ch);
|
||||
},
|
||||
|
||||
isValidIdentifierStart: function(ch) {
|
||||
return ('a' <= ch && ch <= 'z' ||
|
||||
'A' <= ch && ch <= 'Z' ||
|
||||
'_' === ch || ch === '$');
|
||||
},
|
||||
|
||||
isIdentifierContinue: function(ch) {
|
||||
return this.options.isIdentifierContinue ?
|
||||
this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
|
||||
this.isValidIdentifierContinue(ch);
|
||||
},
|
||||
|
||||
isValidIdentifierContinue: function(ch, cp) {
|
||||
return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
|
||||
},
|
||||
|
||||
codePointAt: function(ch) {
|
||||
if (ch.length === 1) return ch.charCodeAt(0);
|
||||
/*jshint bitwise: false*/
|
||||
return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
|
||||
/*jshint bitwise: true*/
|
||||
},
|
||||
|
||||
peekMultichar: function() {
|
||||
var ch = this.text.charAt(this.index);
|
||||
var peek = this.peek();
|
||||
if (!peek) {
|
||||
return ch;
|
||||
}
|
||||
var cp1 = ch.charCodeAt(0);
|
||||
var cp2 = peek.charCodeAt(0);
|
||||
if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
|
||||
return ch + peek;
|
||||
}
|
||||
return ch;
|
||||
},
|
||||
|
||||
isExpOperator: function(ch) {
|
||||
return (ch === '-' || ch === '+' || this.isNumber(ch));
|
||||
},
|
||||
@@ -248,12 +285,13 @@ Lexer.prototype = {
|
||||
|
||||
readIdent: function() {
|
||||
var start = this.index;
|
||||
this.index += this.peekMultichar().length;
|
||||
while (this.index < this.text.length) {
|
||||
var ch = this.text.charAt(this.index);
|
||||
if (!(this.isIdent(ch) || this.isNumber(ch))) {
|
||||
var ch = this.peekMultichar();
|
||||
if (!this.isIdentifierContinue(ch)) {
|
||||
break;
|
||||
}
|
||||
this.index++;
|
||||
this.index += ch.length;
|
||||
}
|
||||
this.tokens.push({
|
||||
index: start,
|
||||
@@ -1183,7 +1221,13 @@ ASTCompiler.prototype = {
|
||||
},
|
||||
|
||||
nonComputedMember: function(left, right) {
|
||||
return left + '.' + right;
|
||||
var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
|
||||
var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
|
||||
if (SAFE_IDENTIFIER.test(right)) {
|
||||
return left + '.' + right;
|
||||
} else {
|
||||
return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
|
||||
}
|
||||
},
|
||||
|
||||
computedMember: function(left, right) {
|
||||
@@ -1449,7 +1493,7 @@ ASTInterpreter.prototype = {
|
||||
return context ? {value: locals} : locals;
|
||||
};
|
||||
case AST.NGValueParameter:
|
||||
return function(scope, locals, assign, inputs) {
|
||||
return function(scope, locals, assign) {
|
||||
return context ? {value: assign} : assign;
|
||||
};
|
||||
}
|
||||
@@ -1746,6 +1790,7 @@ function $ParseProvider() {
|
||||
'null': null,
|
||||
'undefined': undefined
|
||||
};
|
||||
var identStart, identContinue;
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -1762,17 +1807,50 @@ function $ParseProvider() {
|
||||
literals[literalName] = literalValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $parseProvider#setIdentifierFns
|
||||
* @description
|
||||
*
|
||||
* Allows defining the set of characters that are allowed in Angular expressions. The function
|
||||
* `identifierStart` will get called to know if a given character is a valid character to be the
|
||||
* first character for an identifier. The function `identifierContinue` will get called to know if
|
||||
* a given character is a valid character to be a follow-up identifier character. The functions
|
||||
* `identifierStart` and `identifierContinue` will receive as arguments the single character to be
|
||||
* identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
|
||||
* mind that the `string` parameter can be two characters long depending on the character
|
||||
* representation. It is expected for the function to return `true` or `false`, whether that
|
||||
* character is allowed or not.
|
||||
*
|
||||
* Since this function will be called extensivelly, keep the implementation of these functions fast,
|
||||
* as the performance of these functions have a direct impact on the expressions parsing speed.
|
||||
*
|
||||
* @param {function=} identifierStart The function that will decide whether the given character is
|
||||
* a valid identifier start character.
|
||||
* @param {function=} identifierContinue The function that will decide whether the given character is
|
||||
* a valid identifier continue character.
|
||||
*/
|
||||
this.setIdentifierFns = function(identifierStart, identifierContinue) {
|
||||
identStart = identifierStart;
|
||||
identContinue = identifierContinue;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.$get = ['$filter', function($filter) {
|
||||
var noUnsafeEval = csp().noUnsafeEval;
|
||||
var $parseOptions = {
|
||||
csp: noUnsafeEval,
|
||||
expensiveChecks: false,
|
||||
literals: copy(literals)
|
||||
literals: copy(literals),
|
||||
isIdentifierStart: isFunction(identStart) && identStart,
|
||||
isIdentifierContinue: isFunction(identContinue) && identContinue
|
||||
},
|
||||
$parseOptionsExpensive = {
|
||||
csp: noUnsafeEval,
|
||||
expensiveChecks: true,
|
||||
literals: copy(literals)
|
||||
literals: copy(literals),
|
||||
isIdentifierStart: isFunction(identStart) && identStart,
|
||||
isIdentifierContinue: isFunction(identContinue) && identContinue
|
||||
};
|
||||
var runningChecksEnabled = false;
|
||||
|
||||
|
||||
+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:
|
||||
|
||||
+1
-1
@@ -750,7 +750,7 @@ function $RootScopeProvider() {
|
||||
dirty, ttl = TTL,
|
||||
next, current, target = this,
|
||||
watchLog = [],
|
||||
logIdx, logMsg, asyncTask;
|
||||
logIdx, asyncTask;
|
||||
|
||||
beginPhase('$digest');
|
||||
// Check for changes to browser url that happened in sync before the call to $digest
|
||||
|
||||
+5
-1
@@ -17,6 +17,10 @@
|
||||
function $SnifferProvider() {
|
||||
this.$get = ['$window', '$document', function($window, $document) {
|
||||
var eventSupport = {},
|
||||
// Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
|
||||
// the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
|
||||
isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
|
||||
hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
|
||||
android =
|
||||
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
|
||||
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
|
||||
@@ -61,7 +65,7 @@ function $SnifferProvider() {
|
||||
// so let's not use the history API also
|
||||
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
|
||||
// jshint -W018
|
||||
history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
|
||||
history: !!(hasHistoryPushState && !(android < 4) && !boxee),
|
||||
// jshint +W018
|
||||
hasEvent: function(event) {
|
||||
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var $compileMinErr = minErr('$compile');
|
||||
var $templateRequestMinErr = minErr('$compile');
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
@@ -96,7 +96,7 @@ function $TemplateRequestProvider() {
|
||||
|
||||
function handleError(resp) {
|
||||
if (!ignoreRequestError) {
|
||||
throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
|
||||
throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
|
||||
tpl, resp.status, resp.statusText);
|
||||
}
|
||||
return $q.reject(resp);
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
// doesn't know about mocked locations and resolves URLs to the real document - which is
|
||||
// exactly the behavior needed here. There is little value is mocking these out for this
|
||||
// service.
|
||||
var urlParsingNode = document.createElement("a");
|
||||
var urlParsingNode = window.document.createElement("a");
|
||||
var originUrl = urlResolve(window.location.href);
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"extends": "../../.jshintrc-base",
|
||||
"maxlen": false, /* ngAnimate docs contain wide tables */
|
||||
"newcap": false,
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"noop": false,
|
||||
|
||||
|
||||
@@ -82,6 +82,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
// cancel the animation if classes added / removed in both animation cancel each other out,
|
||||
// but only if the current animation isn't structural
|
||||
|
||||
if (currentAnimation.structural) return false;
|
||||
|
||||
var nA = newAnimation.addClass;
|
||||
var nR = newAnimation.removeClass;
|
||||
var cA = currentAnimation.addClass;
|
||||
@@ -169,7 +174,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
||||
var contains = Node.prototype.contains || function(arg) {
|
||||
var contains = window.Node.prototype.contains || function(arg) {
|
||||
// jshint bitwise: false
|
||||
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
|
||||
// jshint bitwise: true
|
||||
@@ -194,7 +199,24 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
return matches;
|
||||
}
|
||||
|
||||
return {
|
||||
function filterFromRegistry(list, matchContainer, matchCallback) {
|
||||
var containerNode = extractElementNode(matchContainer);
|
||||
return list.filter(function(entry) {
|
||||
var isMatch = entry.node === containerNode &&
|
||||
(!matchCallback || entry.callback === matchCallback);
|
||||
return !isMatch;
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupEventListeners(phase, element) {
|
||||
if (phase === 'close' && !element[0].parentNode) {
|
||||
// If the element is not attached to a parentNode, it has been removed by
|
||||
// the domOperation, and we can safely remove the event callbacks
|
||||
$animate.off(element);
|
||||
}
|
||||
}
|
||||
|
||||
var $animate = {
|
||||
on: function(event, container, callback) {
|
||||
var node = extractElementNode(container);
|
||||
callbackRegistry[event] = callbackRegistry[event] || [];
|
||||
@@ -202,24 +224,36 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
node: node,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
// Remove the callback when the element is removed from the DOM
|
||||
jqLite(container).on('$destroy', function() {
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
|
||||
if (!animationDetails) {
|
||||
// If there's an animation ongoing, the callback calling code will remove
|
||||
// the event listeners. If we'd remove here, the callbacks would be removed
|
||||
// before the animation ends
|
||||
$animate.off(event, container, callback);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
off: function(event, container, callback) {
|
||||
if (arguments.length === 1 && !angular.isString(arguments[0])) {
|
||||
container = arguments[0];
|
||||
for (var eventType in callbackRegistry) {
|
||||
callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = callbackRegistry[event];
|
||||
if (!entries) return;
|
||||
|
||||
callbackRegistry[event] = arguments.length === 1
|
||||
? null
|
||||
: filterFromRegistry(entries, container, callback);
|
||||
|
||||
function filterFromRegistry(list, matchContainer, matchCallback) {
|
||||
var containerNode = extractElementNode(matchContainer);
|
||||
return list.filter(function(entry) {
|
||||
var isMatch = entry.node === containerNode &&
|
||||
(!matchCallback || entry.callback === matchCallback);
|
||||
return !isMatch;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
pin: function(element, parentElement) {
|
||||
@@ -269,6 +303,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
};
|
||||
|
||||
return $animate;
|
||||
|
||||
function queueAnimation(element, event, initialOptions) {
|
||||
// we always make a copy of the options since
|
||||
// there should never be any side effects on
|
||||
@@ -331,12 +367,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
|
||||
var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
|
||||
|
||||
var documentHidden = $document[0].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;
|
||||
|
||||
@@ -347,7 +385,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;
|
||||
}
|
||||
|
||||
@@ -497,6 +538,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
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);
|
||||
@@ -505,11 +551,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;
|
||||
@@ -526,7 +567,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
forEach(callbacks, function(callback) {
|
||||
callback(element, phase, data);
|
||||
});
|
||||
cleanupEventListeners(phase, element);
|
||||
});
|
||||
} else {
|
||||
cleanupEventListeners(phase, element);
|
||||
}
|
||||
});
|
||||
runner.progress(event, phase, data);
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
* <div ng-show="bool" class="fade">
|
||||
* Show and hide me
|
||||
* </div>
|
||||
* <button ng-click="bool=true">Toggle</button>
|
||||
* <button ng-click="bool=!bool">Toggle</button>
|
||||
*
|
||||
* <style>
|
||||
* .fade.ng-hide {
|
||||
|
||||
+10
-3
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
|
||||
* directives are supported:
|
||||
* `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
|
||||
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
|
||||
* `ngDblClick`, and `ngMessages`.
|
||||
*
|
||||
* Below is a more detailed breakdown of the attributes handled by ngAria:
|
||||
@@ -25,8 +25,9 @@
|
||||
* |---------------------------------------------|----------------------------------------------------------------------------------------|
|
||||
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
|
||||
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
|
||||
* | {@link ng.directive:ngRequired ngRequired} | aria-required |
|
||||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked |
|
||||
* | {@link ng.directive:ngRequired ngRequired} | aria-required
|
||||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
|
||||
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly ||
|
||||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
|
||||
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
|
||||
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
|
||||
@@ -91,6 +92,7 @@ function $AriaProvider() {
|
||||
var config = {
|
||||
ariaHidden: true,
|
||||
ariaChecked: true,
|
||||
ariaReadonly: true,
|
||||
ariaDisabled: true,
|
||||
ariaRequired: true,
|
||||
ariaInvalid: true,
|
||||
@@ -108,6 +110,7 @@ function $AriaProvider() {
|
||||
*
|
||||
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
|
||||
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
|
||||
* - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
|
||||
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
|
||||
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
|
||||
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
|
||||
@@ -170,6 +173,7 @@ function $AriaProvider() {
|
||||
* The full list of directives that interface with ngAria:
|
||||
* * **ngModel**
|
||||
* * **ngChecked**
|
||||
* * **ngReadonly**
|
||||
* * **ngRequired**
|
||||
* * **ngDisabled**
|
||||
* * **ngValue**
|
||||
@@ -209,6 +213,9 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
.directive('ngChecked', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngReadonly', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngRequired', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
|
||||
}])
|
||||
|
||||
@@ -1,8 +1,36 @@
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngComponentRouter
|
||||
* @installation
|
||||
* ## Installation
|
||||
*
|
||||
* Currently, the **Component Router** module must be installed via `npm`, it is not yet available
|
||||
* on Bower or the Google CDN.
|
||||
*
|
||||
* ```bash
|
||||
* npm install @angular/router --save
|
||||
* ```
|
||||
*
|
||||
* Include `angular_1_router.js` in your HTML:
|
||||
* ```html
|
||||
* <script src="/node_modules/@angular/router/angular1/angular_1_router.js"></script>
|
||||
*```
|
||||
*
|
||||
* You also need to include ES6 shims to support running on Internet Explorer:
|
||||
* ```html
|
||||
* <!-- IE required polyfills, in this exact order -->
|
||||
* <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
|
||||
* <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
|
||||
* <script src="https://npmcdn.com/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
|
||||
* ```
|
||||
*
|
||||
* Then load the module in your application by adding it as a dependent module:
|
||||
*
|
||||
* ```js
|
||||
* angular.module('app', ['ngComponentRouter']);
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* The new Angular Router
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -41,7 +69,7 @@
|
||||
*
|
||||
* There is only one instance of this type in a Component Router application injectable as the
|
||||
* {@link $rootRouter} service. This **Router** is associate with the **Top Level Component**
|
||||
* ({@link $routerRootComponent}). It acts as the connection betweent he **Routers** and the **Location**.
|
||||
* ({@link $routerRootComponent}). It acts as the connection between the **Routers** and the **Location**.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -62,7 +90,7 @@
|
||||
* @name RouteDefinition
|
||||
* @description
|
||||
*
|
||||
* Each item in a the **RouteConfig** for a **Routing Component** is an instance of
|
||||
* Each item in the **RouteConfig** for a **Routing Component** is an instance of
|
||||
* this type. It can have the following properties:
|
||||
*
|
||||
* * `path` or (`regex` and `serializer) - defines how to recognize and generate this route
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
"extends": "../../.jshintrc-base",
|
||||
"bitwise": false, /* locale files use bitwise operators */
|
||||
"maxlen": false, /* locale files are generated from a 3rd party library that has long lines */
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false
|
||||
},
|
||||
"-W041": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"goog": false // see src/module_closure.prefix
|
||||
}
|
||||
|
||||
@@ -410,6 +410,13 @@ angular.module('ngMessages', [])
|
||||
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
@@ -444,6 +451,7 @@ angular.module('ngMessages', [])
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
@@ -455,8 +463,11 @@ angular.module('ngMessages', [])
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.previousSibling || prevNode.parentNode;
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -669,8 +680,8 @@ function ngMessageDirectiveFactory() {
|
||||
// when we are destroying the node later.
|
||||
var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
|
||||
|
||||
// in the event that the parent element is destroyed
|
||||
// by any other structural directive then it's time
|
||||
// in the event that the element or a parent element is destroyed
|
||||
// by another structural directive then it's time
|
||||
// to deregister the message from the controller
|
||||
currentElement.on('$destroy', function() {
|
||||
if (currentElement && currentElement.$$attachId === $$attachId) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"expect": false,
|
||||
"jQuery": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+50
-18
@@ -1321,12 +1321,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
}
|
||||
|
||||
// TODO(vojta): change params to: method, url, data, headers, callback
|
||||
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType) {
|
||||
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
|
||||
|
||||
var xhr = new MockXhr(),
|
||||
expectation = expectations[0],
|
||||
wasExpected = false;
|
||||
|
||||
xhr.$$events = eventHandlers;
|
||||
xhr.upload.$$events = uploadEventHandlers;
|
||||
|
||||
function prettyPrint(data) {
|
||||
return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
|
||||
? data
|
||||
@@ -1416,12 +1419,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
* order to change how a matched request is handled.
|
||||
*
|
||||
* - respond –
|
||||
* `{function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}`
|
||||
* ```js
|
||||
* {function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}
|
||||
* ```
|
||||
* – The respond method takes a set of static data to be returned or a function that can
|
||||
* return an array containing response status (number), response data (string), response
|
||||
* headers (Object), and the text for the status (string). The respond method returns the
|
||||
* `requestHandler` object for possible overrides.
|
||||
* return an array containing response status (number), response data (Array|Object|string),
|
||||
* response headers (Object), and the text for the status (string). The respond method returns
|
||||
* the `requestHandler` object for possible overrides.
|
||||
*/
|
||||
$httpBackend.when = function(method, url, data, headers, keys) {
|
||||
var definition = new MockHttpExpectation(method, url, data, headers, keys),
|
||||
@@ -1606,12 +1611,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
* order to change how a matched request is handled.
|
||||
*
|
||||
* - respond –
|
||||
* `{function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}`
|
||||
* ```
|
||||
* { function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}
|
||||
* ```
|
||||
* – The respond method takes a set of static data to be returned or a function that can
|
||||
* return an array containing response status (number), response data (string), response
|
||||
* headers (Object), and the text for the status (string). The respond method returns the
|
||||
* `requestHandler` object for possible overrides.
|
||||
* return an array containing response status (number), response data (Array|Object|string),
|
||||
* response headers (Object), and the text for the status (string). The respond method returns
|
||||
* the `requestHandler` object for possible overrides.
|
||||
*/
|
||||
$httpBackend.expect = function(method, url, data, headers, keys) {
|
||||
var expectation = new MockHttpExpectation(method, url, data, headers, keys),
|
||||
@@ -2005,6 +2012,20 @@ function MockXhr() {
|
||||
};
|
||||
|
||||
this.abort = angular.noop;
|
||||
|
||||
// This section simulates the events on a real XHR object (and the upload object)
|
||||
// When we are testing $httpBackend (inside the angular project) we make partial use of this
|
||||
// but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
|
||||
this.$$events = {};
|
||||
this.addEventListener = function(name, listener) {
|
||||
if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
|
||||
this.$$events[name].push(listener);
|
||||
};
|
||||
|
||||
this.upload = {
|
||||
$$events: {},
|
||||
addEventListener: this.addEventListener
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2174,8 +2195,8 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
||||
* A service that can be used to create instances of component controllers.
|
||||
* <div class="alert alert-info">
|
||||
* Be aware that the controller will be instantiated and attached to the scope as specified in
|
||||
* the component definition object. That means that you must always provide a `$scope` object
|
||||
* in the `locals` param.
|
||||
* the component definition object. If you do not provide a `$scope` object in the `locals` param
|
||||
* then the helper will create a new isolated scope as a child of `$rootScope`.
|
||||
* </div>
|
||||
* @param {string} componentName the name of the component whose controller we want to instantiate
|
||||
* @param {Object} locals Injection locals for Controller.
|
||||
@@ -2185,7 +2206,7 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
||||
* @return {Object} Instance of requested controller.
|
||||
*/
|
||||
angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
|
||||
this.$get = ['$controller','$injector', function($controller,$injector) {
|
||||
this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
|
||||
return function $componentController(componentName, locals, bindings, ident) {
|
||||
// get all directives associated to the component name
|
||||
var directives = $injector.get(componentName + 'Directive');
|
||||
@@ -2203,6 +2224,9 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
|
||||
}
|
||||
// get the info of the component
|
||||
var directiveInfo = candidateDirectives[0];
|
||||
// create a scope if needed
|
||||
locals = locals || {};
|
||||
locals.$scope = locals.$scope || $rootScope.$new(true);
|
||||
return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
|
||||
};
|
||||
}];
|
||||
@@ -2326,11 +2350,13 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
* `respond` or `passThrough` again in order to change how a matched request is handled.
|
||||
*
|
||||
* - respond –
|
||||
* `{function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}`
|
||||
* ```
|
||||
* { function([status,] data[, headers, statusText])
|
||||
* | function(function(method, url, data, headers, params)}
|
||||
* ```
|
||||
* – The respond method takes a set of static data to be returned or a function that can return
|
||||
* an array containing response status (number), response data (string), response headers
|
||||
* (Object), and the text for the status (string).
|
||||
* an array containing response status (number), response data (Array|Object|string), response
|
||||
* headers (Object), and the text for the status (string).
|
||||
* - passThrough – `{function()}` – Any request matching a backend definition with
|
||||
* `passThrough` handler will be passed through to the real backend (an XHR request will be made
|
||||
* to the server.)
|
||||
@@ -2892,6 +2918,12 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||
window.inject = angular.mock.inject = function() {
|
||||
var blockFns = Array.prototype.slice.call(arguments, 0);
|
||||
var errorForStack = new Error('Declaration Location');
|
||||
// IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
|
||||
if (!errorForStack.stack) {
|
||||
try {
|
||||
throw errorForStack;
|
||||
} catch (e) {}
|
||||
}
|
||||
return wasInjectorCreated() ? workFn.call(currentSpec) : workFn;
|
||||
/////////////////////
|
||||
function workFn() {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngParseExt
|
||||
* @packageName angular-parse-ext
|
||||
* @description
|
||||
*
|
||||
* # ngParseExt
|
||||
*
|
||||
* The `ngParseExt` module provides functionality to allow Unicode characters in
|
||||
* identifiers inside Angular expressions.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngParseExt"></div>
|
||||
*
|
||||
* This module allows the usage of any identifier that follows ES6 identifier naming convention
|
||||
* to be used as an identifier in an Angular expression. ES6 delegates some of the identifier
|
||||
* rules definition to Unicode, this module uses ES6 and Unicode 8.0 identifiers convention.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global angularParseExtModule: true,
|
||||
IDS_Y,
|
||||
IDC_Y
|
||||
*/
|
||||
|
||||
function isValidIdentifierStart(ch, cp) {
|
||||
return ch === '$' ||
|
||||
ch === '_' ||
|
||||
IDS_Y(cp);
|
||||
}
|
||||
|
||||
function isValidIdentifierContinue(ch, cp) {
|
||||
return ch === '$' ||
|
||||
ch === '_' ||
|
||||
cp === 0x200C || // <ZWNJ>
|
||||
cp === 0x200D || // <ZWJ>
|
||||
IDC_Y(cp);
|
||||
}
|
||||
|
||||
angular.module('ngParseExt', [])
|
||||
.config(['$parseProvider', function($parseProvider) {
|
||||
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
|
||||
}]);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"ngRouteModule": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
*
|
||||
* The enter and leave animation occur concurrently.
|
||||
*
|
||||
* @knownIssue If `ngView` is contained in an asynchronously loaded template (e.g. in another
|
||||
* directive's templateUrl or in a template loaded using `ngInclude`), then you need to
|
||||
* make sure that `$route` is instantiated in time to capture the initial
|
||||
* `$locationChangeStart` event and load the appropriate view. One way to achieve this
|
||||
* is to have it as a dependency in a `.run` block:
|
||||
* `myModule.run(['$route', function() {}]);`
|
||||
*
|
||||
* @scope
|
||||
* @priority 400
|
||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
*/
|
||||
/* global -ngRouteModule */
|
||||
var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
provider('$route', $RouteProvider).
|
||||
// Ensure `$route` will be instantiated in time to capture the initial
|
||||
// `$locationChangeSuccess` event. This is necessary in case `ngView` is
|
||||
// included in an asynchronously loaded template.
|
||||
run(['$route', angular.noop]),
|
||||
provider('$route', $RouteProvider),
|
||||
$routeMinErr = angular.$$minErr('ngRoute');
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"htmlSanitizeWriter": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ function htmlParser(html, handler) {
|
||||
mXSSAttempts--;
|
||||
|
||||
// strip custom-namespaced attributes on IE<=11
|
||||
if (document.documentMode <= 11) {
|
||||
if (window.document.documentMode) {
|
||||
stripCustomNsAttrs(inertBodyElement);
|
||||
}
|
||||
html = inertBodyElement.innerHTML; //trigger mXSS
|
||||
@@ -484,7 +484,7 @@ function htmlSanitizeWriter(buf, uriValidator) {
|
||||
* @param node Root element to process
|
||||
*/
|
||||
function stripCustomNsAttrs(node) {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
||||
var attrs = node.attributes;
|
||||
for (var i = 0, l = attrs.length; i < l; i++) {
|
||||
var attrNode = attrs[i];
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"includes": false,
|
||||
"asyncForEach": false,
|
||||
@@ -19,4 +20,4 @@
|
||||
"$runner": false,
|
||||
"callerFile": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
|
||||
self.context.find('#test-frames').append('<iframe>');
|
||||
frame = self.getFrame_();
|
||||
|
||||
frame.load(function() {
|
||||
frame.on('load', function() {
|
||||
frame.off();
|
||||
try {
|
||||
var $window = self.getWindow_();
|
||||
|
||||
@@ -104,7 +104,7 @@ angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
|
||||
*/
|
||||
angular.scenario.setUpAndRun = function(config) {
|
||||
var href = window.location.href;
|
||||
var body = _jQuery(document.body);
|
||||
var body = _jQuery(window.document.body);
|
||||
var output = [];
|
||||
var objModel = new angular.scenario.ObjectModel($runner);
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -5,7 +5,7 @@
|
||||
(function(previousOnLoad) {
|
||||
var prefix = (function() {
|
||||
var filename = /(.*\/)angular-bootstrap.js(#(.*))?/;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var scripts = window.document.getElementsByTagName("script");
|
||||
for (var j = 0; j < scripts.length; j++) {
|
||||
var src = scripts[j].src;
|
||||
if (src && src.match(filename)) {
|
||||
@@ -16,11 +16,11 @@
|
||||
})();
|
||||
|
||||
function addScript(path) {
|
||||
document.write('<script type="text/javascript" src="' + prefix + path + '"></script>');
|
||||
window.document.write('<script type="text/javascript" src="' + prefix + path + '"></script>');
|
||||
}
|
||||
|
||||
function addCSS(path) {
|
||||
document.write('<link rel="stylesheet" type="text/css" href="' + prefix + path + '"/>');
|
||||
window.document.write('<link rel="stylesheet" type="text/css" href="' + prefix + path + '"/>');
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
addCSS("../../css/angular-scenario.css");
|
||||
addScript("../../lib/jquery/jquery.js");
|
||||
document.write(
|
||||
window.document.write(
|
||||
'<script type="text/javascript">' +
|
||||
'var _jQuery = jQuery.noConflict(true);' +
|
||||
'</script>'
|
||||
@@ -54,7 +54,7 @@
|
||||
addScript("output/Xml.js");
|
||||
|
||||
// Create the runner (which also sets up the global API)
|
||||
document.write(
|
||||
window.document.write(
|
||||
'<script type="text/javascript">' +
|
||||
' var $runner = new angular.scenario.Runner(window);' +
|
||||
'</script>');
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document){
|
||||
(function(window){
|
||||
var _jQuery = window.jQuery.noConflict(true);
|
||||
|
||||
@@ -2,7 +2,7 @@ bindJQuery();
|
||||
publishExternalAPI(angular);
|
||||
|
||||
var $runner = new angular.scenario.Runner(window),
|
||||
scripts = document.getElementsByTagName('script'),
|
||||
scripts = window.document.getElementsByTagName('script'),
|
||||
script = scripts[scripts.length - 1],
|
||||
config = {};
|
||||
|
||||
@@ -14,9 +14,9 @@ angular.forEach(script.attributes, function(attr) {
|
||||
});
|
||||
|
||||
if (config.autotest) {
|
||||
JQLite(document).ready(function() {
|
||||
JQLite(window.document).ready(function() {
|
||||
angular.scenario.setUpAndRun(config);
|
||||
});
|
||||
}
|
||||
})(window, document);
|
||||
})(window);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
evnt = new TransitionEvent(eventType, eventData);
|
||||
}
|
||||
catch (e) {
|
||||
evnt = document.createEvent('TransitionEvent');
|
||||
evnt = window.document.createEvent('TransitionEvent');
|
||||
evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
|
||||
}
|
||||
}
|
||||
@@ -74,14 +74,14 @@
|
||||
evnt = new AnimationEvent(eventType, eventData);
|
||||
}
|
||||
catch (e) {
|
||||
evnt = document.createEvent('AnimationEvent');
|
||||
evnt = window.document.createEvent('AnimationEvent');
|
||||
evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
|
||||
}
|
||||
}
|
||||
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
|
||||
evnt = createTouchEvent(element, eventType, x, y);
|
||||
} else {
|
||||
evnt = document.createEvent('MouseEvents');
|
||||
evnt = window.document.createEvent('MouseEvents');
|
||||
x = x || 0;
|
||||
y = y || 0;
|
||||
evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
|
||||
@@ -120,12 +120,12 @@
|
||||
if ('_cached' in supportsTouchEvents) {
|
||||
return supportsTouchEvents._cached;
|
||||
}
|
||||
if (!document.createTouch || !document.createTouchList) {
|
||||
if (!window.document.createTouch || !window.document.createTouchList) {
|
||||
supportsTouchEvents._cached = false;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
document.createEvent('TouchEvent');
|
||||
window.document.createEvent('TouchEvent');
|
||||
} catch (e) {
|
||||
supportsTouchEvents._cached = false;
|
||||
return false;
|
||||
@@ -135,12 +135,12 @@
|
||||
}
|
||||
|
||||
function createTouchEvent(element, eventType, x, y) {
|
||||
var evnt = new Event(eventType);
|
||||
var evnt = new window.Event(eventType);
|
||||
x = x || 0;
|
||||
y = y || 0;
|
||||
|
||||
var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
|
||||
var touches = document.createTouchList(touch);
|
||||
var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
|
||||
var touches = window.document.createTouchList(touch);
|
||||
|
||||
evnt.touches = touches;
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ angular.scenario.dsl('binding', function() {
|
||||
*/
|
||||
angular.scenario.dsl('input', function() {
|
||||
var chain = {};
|
||||
var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
|
||||
var supportInputEvent = 'oninput' in window.document.createElement('div') && !msie;
|
||||
|
||||
chain.enter = function(value, event) {
|
||||
return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"ngTouch": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -131,9 +131,9 @@
|
||||
|
||||
/* jasmine / karma */
|
||||
"it": false,
|
||||
"iit": false,
|
||||
"fit": false,
|
||||
"describe": false,
|
||||
"ddescribe": false,
|
||||
"fdescribe": false,
|
||||
"beforeEach": false,
|
||||
"afterEach": false,
|
||||
"expect": false,
|
||||
@@ -144,7 +144,7 @@
|
||||
"runs": false,
|
||||
"dump": false,
|
||||
"they": false,
|
||||
"tthey": false,
|
||||
"fthey": false,
|
||||
"xthey": false,
|
||||
"assertCompareNodes": false,
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
"createMockStyleSheet": false,
|
||||
"browserSupportsCssAnimations": false,
|
||||
"browserTrigger": false,
|
||||
"jqLiteCacheSize": false
|
||||
"jqLiteCacheSize": false,
|
||||
"createAsync": false
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -983,7 +983,7 @@ describe('angular', function() {
|
||||
describe('csp', function() {
|
||||
|
||||
function mockCspElement(cspAttrName, cspAttrValue) {
|
||||
return spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
return spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector == '[' + cspAttrName + ']') {
|
||||
var html = '<div ' + cspAttrName + (cspAttrValue ? ('="' + cspAttrValue + '" ') : '') + '></div>';
|
||||
return jqLite(html)[0];
|
||||
@@ -1009,7 +1009,7 @@ describe('angular', function() {
|
||||
|
||||
|
||||
it('should return true for noUnsafeEval if eval causes a CSP security policy error', function() {
|
||||
window.Function.andCallFake(function() { throw new Error('CSP test'); });
|
||||
window.Function.and.callFake(function() { throw new Error('CSP test'); });
|
||||
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: false });
|
||||
expect(window.Function).toHaveBeenCalledWith('');
|
||||
});
|
||||
@@ -1073,7 +1073,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return empty string when jq is enabled manually via [ng-jq] with empty string', function() {
|
||||
element.setAttribute('ng-jq', '');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('');
|
||||
@@ -1081,7 +1081,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return empty string when jq is enabled manually via [data-ng-jq] with empty string', function() {
|
||||
element.setAttribute('data-ng-jq', '');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[data-ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('');
|
||||
@@ -1090,7 +1090,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return empty string when jq is enabled manually via [x-ng-jq] with empty string', function() {
|
||||
element.setAttribute('x-ng-jq', '');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[x-ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('');
|
||||
@@ -1099,7 +1099,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return empty string when jq is enabled manually via [ng:jq] with empty string', function() {
|
||||
element.setAttribute('ng:jq', '');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[ng\\:jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('');
|
||||
@@ -1108,7 +1108,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return "jQuery" when jq is enabled manually via [ng-jq] with value "jQuery"', function() {
|
||||
element.setAttribute('ng-jq', 'jQuery');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('jQuery');
|
||||
@@ -1117,7 +1117,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return "jQuery" when jq is enabled manually via [data-ng-jq] with value "jQuery"', function() {
|
||||
element.setAttribute('data-ng-jq', 'jQuery');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[data-ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('jQuery');
|
||||
@@ -1126,7 +1126,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return "jQuery" when jq is enabled manually via [x-ng-jq] with value "jQuery"', function() {
|
||||
element.setAttribute('x-ng-jq', 'jQuery');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[x-ng-jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('jQuery');
|
||||
@@ -1135,7 +1135,7 @@ describe('angular', function() {
|
||||
|
||||
it('should return "jQuery" when jq is enabled manually via [ng:jq] with value "jQuery"', function() {
|
||||
element.setAttribute('ng:jq', 'jQuery');
|
||||
spyOn(document, 'querySelector').andCallFake(function(selector) {
|
||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
||||
if (selector === '[ng\\:jq]') return element;
|
||||
});
|
||||
expect(jq()).toBe('jQuery');
|
||||
@@ -1636,7 +1636,7 @@ describe('angular', function() {
|
||||
|
||||
expect(function() {
|
||||
angularInit(appElement, angular.bootstrap);
|
||||
}).toThrowMatching(
|
||||
}).toThrowError(
|
||||
new RegExp('\\[\\$injector:modulerr] Failed to instantiate module doesntexist due to:\\n' +
|
||||
'.*\\[\\$injector:nomod] Module \'doesntexist\' is not available! You either ' +
|
||||
'misspelled the module name or forgot to load it\\.')
|
||||
@@ -1650,7 +1650,7 @@ describe('angular', function() {
|
||||
|
||||
expect(function() {
|
||||
angular.bootstrap(element);
|
||||
}).toThrowMatching(
|
||||
}).toThrowError(
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
|
||||
);
|
||||
|
||||
@@ -1662,7 +1662,7 @@ describe('angular', function() {
|
||||
angular.bootstrap(document.getElementsByTagName('html')[0]);
|
||||
expect(function() {
|
||||
angular.bootstrap(document);
|
||||
}).toThrowMatching(
|
||||
}).toThrowError(
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element 'document'/i
|
||||
);
|
||||
|
||||
@@ -1671,11 +1671,11 @@ describe('angular', function() {
|
||||
|
||||
|
||||
it('should bootstrap in strict mode when ng-strict-di attribute is specified', function() {
|
||||
bootstrapSpy = spyOn(angular, 'bootstrap').andCallThrough();
|
||||
bootstrapSpy = spyOn(angular, 'bootstrap').and.callThrough();
|
||||
var appElement = jqLite('<div ng-app="" ng-strict-di></div>');
|
||||
angularInit(jqLite('<div></div>').append(appElement[0])[0], bootstrapSpy);
|
||||
expect(bootstrapSpy).toHaveBeenCalledOnce();
|
||||
expect(bootstrapSpy.mostRecentCall.args[2].strictDi).toBe(true);
|
||||
expect(bootstrapSpy.calls.mostRecent().args[2].strictDi).toBe(true);
|
||||
|
||||
var injector = appElement.injector();
|
||||
function testFactory($rootScope) {}
|
||||
@@ -1863,7 +1863,7 @@ describe('angular', function() {
|
||||
|
||||
expect(function() {
|
||||
angular.bootstrap(element, ['doesntexist']);
|
||||
}).toThrowMatching(
|
||||
}).toThrowError(
|
||||
new RegExp('\\[\\$injector:modulerr\\] Failed to instantiate module doesntexist due to:\\n' +
|
||||
'.*\\[\\$injector:nomod\\] Module \'doesntexist\' is not available! You either ' +
|
||||
'misspelled the module name or forgot to load it\\.'));
|
||||
@@ -1997,7 +1997,7 @@ describe('angular', function() {
|
||||
describe('fromJson', function() {
|
||||
|
||||
it('should delegate to JSON.parse', function() {
|
||||
var spy = spyOn(JSON, 'parse').andCallThrough();
|
||||
var spy = spyOn(JSON, 'parse').and.callThrough();
|
||||
|
||||
expect(fromJson('{}')).toEqual({});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@@ -2008,7 +2008,7 @@ describe('angular', function() {
|
||||
describe('toJson', function() {
|
||||
|
||||
it('should delegate to JSON.stringify', function() {
|
||||
var spy = spyOn(JSON, 'stringify').andCallThrough();
|
||||
var spy = spyOn(JSON, 'stringify').and.callThrough();
|
||||
|
||||
expect(toJson({})).toEqual('{}');
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ describe('Binder', function() {
|
||||
|
||||
$rootScope.error['throw'] = function() { return 'X';};
|
||||
$rootScope.$apply();
|
||||
expect(errorLogs.length).toMatch(0);
|
||||
expect(errorLogs.length).toMatch('0');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -84,14 +84,14 @@ describe('injector', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should provide the caller name if given', function(done) {
|
||||
it('should provide the caller name if given', function() {
|
||||
expect(function() {
|
||||
injector.get('idontexist', 'callerName');
|
||||
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- callerName");
|
||||
});
|
||||
|
||||
|
||||
it('should provide the caller name for controllers', function(done) {
|
||||
it('should provide the caller name for controllers', function() {
|
||||
controllerProvider.register('myCtrl', function(idontexist) {});
|
||||
var $controller = injector.get('$controller');
|
||||
expect(function() {
|
||||
@@ -241,6 +241,12 @@ describe('injector', function() {
|
||||
expect($f_n0.$inject).toEqual(['$a_']);
|
||||
});
|
||||
|
||||
it('should handle functions with overridden toString', function() {
|
||||
function fn(a) {}
|
||||
fn.toString = function() { return 'fn'; };
|
||||
expect(annotate(fn)).toEqual(['a']);
|
||||
expect(fn.$inject).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should throw on non function arg', function() {
|
||||
expect(function() {
|
||||
@@ -282,9 +288,9 @@ describe('injector', function() {
|
||||
if (support.classes) {
|
||||
it('should be possible to instantiate ES6 classes', function() {
|
||||
providers('a', function() { return 'a-value'; });
|
||||
var clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
|
||||
var instance = injector.instantiate(clazz);
|
||||
expect(instance).toEqual({a: 'a-value'});
|
||||
var Clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
|
||||
var instance = injector.instantiate(Clazz);
|
||||
expect(instance).toEqual(new Clazz('a-value'));
|
||||
expect(instance.aVal()).toEqual('a-value');
|
||||
});
|
||||
}
|
||||
@@ -294,7 +300,7 @@ describe('injector', function() {
|
||||
|
||||
it('should publish annotate API', function() {
|
||||
expect(angular.mock.$$annotate).toBe(annotate);
|
||||
spyOn(angular.mock, '$$annotate').andCallThrough();
|
||||
spyOn(angular.mock, '$$annotate').and.callThrough();
|
||||
function fn() {}
|
||||
injector.annotate(fn);
|
||||
expect(angular.mock.$$annotate).toHaveBeenCalledWith(fn);
|
||||
@@ -1012,7 +1018,7 @@ describe('injector', function() {
|
||||
createInjector([function($provide) {
|
||||
$provide.value('name', 'angular');
|
||||
}, instanceLookupInModule]);
|
||||
}).toThrowMatching(/\[\$injector:unpr] Unknown provider: name/);
|
||||
}).toThrowError(/\[\$injector:unpr] Unknown provider: name/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
|
||||
+303
-203
@@ -4,24 +4,31 @@ beforeEach(function() {
|
||||
|
||||
function cssMatcher(presentClasses, absentClasses) {
|
||||
return function() {
|
||||
var element = angular.element(this.actual);
|
||||
var present = true;
|
||||
var absent = false;
|
||||
return {
|
||||
compare: function(actual) {
|
||||
var element = angular.element(actual);
|
||||
var present = true;
|
||||
var absent = false;
|
||||
|
||||
angular.forEach(presentClasses.split(' '), function(className) {
|
||||
present = present && element.hasClass(className);
|
||||
});
|
||||
angular.forEach(presentClasses.split(' '), function(className) {
|
||||
present = present && element.hasClass(className);
|
||||
});
|
||||
|
||||
angular.forEach(absentClasses.split(' '), function(className) {
|
||||
absent = absent || element.hasClass(className);
|
||||
});
|
||||
angular.forEach(absentClasses.split(' '), function(className) {
|
||||
absent = absent || element.hasClass(className);
|
||||
});
|
||||
|
||||
this.message = function() {
|
||||
return "Expected to have " + presentClasses +
|
||||
(absentClasses ? (" and not have " + absentClasses + "") : "") +
|
||||
" but had " + element[0].className + ".";
|
||||
var message = function() {
|
||||
return "Expected to have " + presentClasses +
|
||||
(absentClasses ? (" and not have " + absentClasses + "") : "") +
|
||||
" but had " + element[0].className + ".";
|
||||
};
|
||||
return {
|
||||
pass: present && !absent,
|
||||
message: message
|
||||
};
|
||||
}
|
||||
};
|
||||
return present && !absent;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +43,7 @@ beforeEach(function() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
this.addMatchers({
|
||||
jasmine.addMatchers({
|
||||
toBeEmpty: cssMatcher('ng-empty', 'ng-not-empty'),
|
||||
toBeNotEmpty: cssMatcher('ng-not-empty', 'ng-empty'),
|
||||
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
|
||||
@@ -46,236 +53,329 @@ beforeEach(function() {
|
||||
toBeUntouched: cssMatcher('ng-untouched', 'ng-touched'),
|
||||
toBeTouched: cssMatcher('ng-touched', 'ng-untouched'),
|
||||
toBeAPromise: function() {
|
||||
this.message = valueFn(
|
||||
"Expected object " + (this.isNot ? "not " : "") + "to be a promise");
|
||||
return isPromiseLike(this.actual);
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function generateCompare(isNot) {
|
||||
return function(actual) {
|
||||
var message = valueFn(
|
||||
"Expected object " + (isNot ? "not " : "") + "to be a promise");
|
||||
return { pass: isPromiseLike(actual), message: message };
|
||||
};
|
||||
}
|
||||
},
|
||||
toBeShown: function() {
|
||||
this.message = valueFn(
|
||||
"Expected element " + (this.isNot ? "" : "not ") + "to have 'ng-hide' class");
|
||||
return !isNgElementHidden(this.actual);
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function generateCompare(isNot) {
|
||||
return function(actual) {
|
||||
var message = valueFn("Expected element " + (isNot ? "" : "not ") + "to have 'ng-hide' class");
|
||||
var pass = !isNgElementHidden(actual);
|
||||
if (isNot) {
|
||||
pass = !pass;
|
||||
}
|
||||
return { pass: pass, message: message };
|
||||
};
|
||||
}
|
||||
},
|
||||
toBeHidden: function() {
|
||||
this.message = valueFn(
|
||||
"Expected element " + (this.isNot ? "not " : "") + "to have 'ng-hide' class");
|
||||
return isNgElementHidden(this.actual);
|
||||
},
|
||||
|
||||
toEqual: function(expected) {
|
||||
if (this.actual && this.actual.$$log) {
|
||||
this.actual = (typeof expected === 'string')
|
||||
? this.actual.toString()
|
||||
: this.actual.toArray();
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function generateCompare(isNot) {
|
||||
return function(actual) {
|
||||
var message = valueFn("Expected element " + (isNot ? "not " : "") + "to have 'ng-hide' class");
|
||||
var pass = isNgElementHidden(actual);
|
||||
if (isNot) {
|
||||
pass = !pass;
|
||||
}
|
||||
return { pass: pass, message: message };
|
||||
};
|
||||
}
|
||||
return jasmine.Matchers.prototype.toEqual.call(this, expected);
|
||||
},
|
||||
|
||||
toEqualData: function(expected) {
|
||||
return angular.equals(this.actual, expected);
|
||||
},
|
||||
|
||||
toEqualError: function(message) {
|
||||
this.message = function() {
|
||||
var expected;
|
||||
if (this.actual.message && this.actual.name == 'Error') {
|
||||
expected = angular.toJson(this.actual.message);
|
||||
} else {
|
||||
expected = angular.toJson(this.actual);
|
||||
toEqual: function(util) {
|
||||
return {
|
||||
compare: function(actual, expected) {
|
||||
if (actual && actual.$$log) {
|
||||
actual = (typeof expected === 'string')
|
||||
? actual.toString()
|
||||
: actual.toArray();
|
||||
}
|
||||
return {
|
||||
pass: util.equals(actual, expected, [DOMTester])
|
||||
};
|
||||
}
|
||||
return "Expected " + expected + " to be an Error with message " + angular.toJson(message);
|
||||
};
|
||||
return this.actual.name == 'Error' && this.actual.message == message;
|
||||
|
||||
function DOMTester(a, b) {
|
||||
if (a && b && a.nodeType > 0 && b.nodeType > 0) {
|
||||
return a === b;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toMatchError: function(messageRegexp) {
|
||||
this.message = function() {
|
||||
var expected;
|
||||
if (this.actual.message && this.actual.name == 'Error') {
|
||||
expected = angular.toJson(this.actual.message);
|
||||
} else {
|
||||
expected = angular.toJson(this.actual);
|
||||
toEqualData: function() {
|
||||
return {
|
||||
compare: function(actual, expected) {
|
||||
return { pass: angular.equals(actual, expected) };
|
||||
}
|
||||
return "Expected " + expected + " to match an Error with message " + angular.toJson(messageRegexp);
|
||||
};
|
||||
return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
|
||||
},
|
||||
|
||||
toHaveBeenCalledOnce: function() {
|
||||
if (arguments.length > 0) {
|
||||
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
|
||||
}
|
||||
|
||||
if (!jasmine.isSpy(this.actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
var msg = 'Expected spy ' + this.actual.identity + ' to have been called once, but was ',
|
||||
count = this.actual.callCount;
|
||||
return [
|
||||
count === 0 ? msg + 'never called.' :
|
||||
msg + 'called ' + count + ' times.',
|
||||
msg.replace('to have', 'not to have') + 'called once.'
|
||||
];
|
||||
};
|
||||
|
||||
return this.actual.callCount == 1;
|
||||
},
|
||||
|
||||
|
||||
toHaveBeenCalledOnceWith: function() {
|
||||
var expectedArgs = jasmine.util.argsToArray(arguments);
|
||||
|
||||
if (!jasmine.isSpy(this.actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
if (this.actual.callCount != 1) {
|
||||
if (this.actual.callCount === 0) {
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was never called.',
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was.'
|
||||
];
|
||||
return {
|
||||
compare: function(actual) {
|
||||
if (arguments.length > 1) {
|
||||
throw new Error('`toHaveBeenCalledOnce` does not take arguments, ' +
|
||||
'use `toHaveBeenCalledOnceWith`');
|
||||
}
|
||||
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was called ' + this.actual.callCount + ' times.',
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was.'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall),
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall)
|
||||
];
|
||||
if (!jasmine.isSpy(actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
|
||||
}
|
||||
|
||||
var count = actual.calls.count();
|
||||
var pass = count === 1;
|
||||
|
||||
var message = function() {
|
||||
var msg = 'Expected spy ' + actual.and.identity() + (pass ? ' not ' : ' ') +
|
||||
'to have been called once, but ';
|
||||
|
||||
switch (count) {
|
||||
case 0:
|
||||
msg += 'it was never called.';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'it was called once.';
|
||||
break;
|
||||
default:
|
||||
msg += 'it was called ' + count + ' times.';
|
||||
break;
|
||||
}
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
return {
|
||||
pass: pass,
|
||||
message: message
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return this.actual.callCount === 1 && this.env.contains_(this.actual.argsForCall, expectedArgs);
|
||||
},
|
||||
|
||||
toHaveBeenCalledOnceWith: function(util, customEqualityTesters) {
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
|
||||
function generateCompare(isNot) {
|
||||
return function(actual) {
|
||||
if (!jasmine.isSpy(actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
|
||||
}
|
||||
|
||||
var expectedArgs = Array.prototype.slice.call(arguments, 1);
|
||||
var actualCount = actual.calls.count();
|
||||
var actualArgs = actualCount && actual.calls.argsFor(0);
|
||||
|
||||
var pass = (actualCount === 1) && util.equals(actualArgs, expectedArgs);
|
||||
if (isNot) pass = !pass;
|
||||
|
||||
var message = function() {
|
||||
var msg = 'Expected spy ' + actual.and.identity() + (isNot ? ' not ' : ' ') +
|
||||
'to have been called once with ' + jasmine.pp(expectedArgs) + ', but ';
|
||||
|
||||
if (isNot) {
|
||||
msg += 'it was.';
|
||||
} else {
|
||||
switch (actualCount) {
|
||||
case 0:
|
||||
msg += 'it was never called.';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'it was called with ' + jasmine.pp(actualArgs) + '.';
|
||||
break;
|
||||
default:
|
||||
msg += 'it was called ' + actualCount + ' times.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
return {
|
||||
pass: pass,
|
||||
message: message
|
||||
};
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
toBeOneOf: function() {
|
||||
return Array.prototype.indexOf.call(arguments, this.actual) !== -1;
|
||||
},
|
||||
|
||||
toHaveClass: function(clazz) {
|
||||
this.message = function() {
|
||||
return "Expected '" + angular.mock.dump(this.actual) + "'" + (this.isNot ? " not " : "") + " to have class '" + clazz + "'.";
|
||||
};
|
||||
var classes = clazz.trim().split(/\s+/);
|
||||
for (var i = 0; i < classes.length; ++i) {
|
||||
if (!jqLiteHasClass(this.actual[0], classes[i])) {
|
||||
return false;
|
||||
return {
|
||||
compare: function(actual) {
|
||||
var expectedArgs = Array.prototype.slice.call(arguments, 1);
|
||||
return { pass: expectedArgs.indexOf(actual) !== -1 };
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
toThrowMatching: function(expected) {
|
||||
return jasmine.Matchers.prototype.toThrow.call(this, expected);
|
||||
},
|
||||
|
||||
toThrowMinErr: function(namespace, code, content) {
|
||||
var result,
|
||||
exception,
|
||||
exceptionMessage = '',
|
||||
escapeRegexp = function(str) {
|
||||
// This function escapes all special regex characters.
|
||||
// We use it to create matching regex from arbitrary strings.
|
||||
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
|
||||
not = this.isNot ? "not " : "",
|
||||
regex = jasmine.isA_("RegExp", content) ? content :
|
||||
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
|
||||
|
||||
if (!angular.isFunction(this.actual)) {
|
||||
throw new Error('Actual is not a function');
|
||||
}
|
||||
|
||||
try {
|
||||
this.actual();
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (exception) {
|
||||
exceptionMessage = exception.message || exception;
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
return "Expected function " + not + "to throw " +
|
||||
namespace + "MinErr('" + code + "')" +
|
||||
(regex ? " matching " + regex.toString() : "") +
|
||||
(exception ? ", but it threw " + exceptionMessage : ".");
|
||||
};
|
||||
},
|
||||
|
||||
result = codeRegex.test(exceptionMessage);
|
||||
if (!result) {
|
||||
return result;
|
||||
toHaveClass: function() {
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function generateCompare(isNot) {
|
||||
return function(actual, clazz) {
|
||||
var message = function() {
|
||||
return "Expected '" + angular.mock.dump(actual) + "'" + (isNot ? " not " : "") + " to have class '" + clazz + "'.";
|
||||
};
|
||||
var classes = clazz.trim().split(/\s+/);
|
||||
for (var i = 0; i < classes.length; ++i) {
|
||||
if (!jqLiteHasClass(actual[0], classes[i])) {
|
||||
return { pass: isNot };
|
||||
}
|
||||
}
|
||||
return { pass: !isNot };
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
if (angular.isDefined(regex)) {
|
||||
return regex.test(exceptionMessage);
|
||||
toThrowMinErr: function() {
|
||||
return {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function generateCompare(isNot) {
|
||||
return function(actual, namespace, code, content) {
|
||||
var result,
|
||||
exception,
|
||||
exceptionMessage = '',
|
||||
escapeRegexp = function(str) {
|
||||
// This function escapes all special regex characters.
|
||||
// We use it to create matching regex from arbitrary strings.
|
||||
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
|
||||
not = isNot ? "not " : "",
|
||||
regex = jasmine.isA_("RegExp", content) ? content :
|
||||
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
|
||||
|
||||
if (!angular.isFunction(actual)) {
|
||||
throw new Error('Actual is not a function');
|
||||
}
|
||||
|
||||
try {
|
||||
actual();
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (exception) {
|
||||
exceptionMessage = exception.message || exception;
|
||||
}
|
||||
|
||||
var message = function() {
|
||||
return "Expected function " + not + "to throw " +
|
||||
namespace + "MinErr('" + code + "')" +
|
||||
(regex ? " matching " + regex.toString() : "") +
|
||||
(exception ? ", but it threw " + exceptionMessage : ".");
|
||||
};
|
||||
|
||||
result = codeRegex.test(exceptionMessage);
|
||||
if (!result) {
|
||||
if (isNot) {
|
||||
return { pass: !result, message: message };
|
||||
} else {
|
||||
return { pass: result, message: message };
|
||||
}
|
||||
}
|
||||
|
||||
if (angular.isDefined(regex)) {
|
||||
if (isNot) {
|
||||
return { pass: !regex.test(exceptionMessage), message: message };
|
||||
} else {
|
||||
return { pass: regex.test(exceptionMessage), message: message };
|
||||
}
|
||||
}
|
||||
if (isNot) {
|
||||
return { pass: !result, message: message };
|
||||
} else {
|
||||
return { pass: result, message: message };
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// TODO(vojta): remove this once Jasmine in Karma gets updated
|
||||
// https://github.com/pivotal/jasmine/blob/c40b64a24c607596fa7488f2a0ddb98d063c872a/src/core/Matchers.js#L217-L246
|
||||
// This toThrow supports RegExps.
|
||||
jasmine.Matchers.prototype.toThrow = function(expected) {
|
||||
var result = false;
|
||||
var exception, exceptionMessage;
|
||||
if (typeof this.actual != 'function') {
|
||||
throw new Error('Actual is not a function');
|
||||
}
|
||||
try {
|
||||
this.actual();
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (exception) {
|
||||
exceptionMessage = exception.message || exception;
|
||||
result = (isUndefined(expected) || this.env.equals_(exceptionMessage, expected.message || expected) || (jasmine.isA_("RegExp", expected) && expected.test(exceptionMessage)));
|
||||
}
|
||||
|
||||
var not = this.isNot ? "not " : "";
|
||||
var regexMatch = jasmine.isA_("RegExp", expected) ? " an exception matching" : "";
|
||||
|
||||
this.message = function() {
|
||||
if (exception) {
|
||||
return ["Expected function " + not + "to throw" + regexMatch, expected ? expected.message || expected : "an exception", ", but it threw", exceptionMessage].join(' ');
|
||||
} else {
|
||||
return "Expected function to throw an exception.";
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create jasmine.Spy on given method, but ignore calls without arguments
|
||||
* This is helpful when need to spy only setter methods and ignore getters
|
||||
*/
|
||||
function spyOnlyCallsWithArgs(obj, method) {
|
||||
var originalFn = obj[method];
|
||||
var spy = spyOn(obj, method);
|
||||
obj[method] = function() {
|
||||
if (arguments.length) return spy.apply(this, arguments);
|
||||
return spy.originalValue.apply(this);
|
||||
return originalFn.apply(this);
|
||||
};
|
||||
return spy;
|
||||
}
|
||||
|
||||
// Minimal implementation to mock what was removed from Jasmine 1.x
|
||||
function createAsync(doneFn) {
|
||||
function Job() {
|
||||
this.next = [];
|
||||
}
|
||||
Job.prototype.done = function() {
|
||||
return this.runs(doneFn);
|
||||
};
|
||||
Job.prototype.runs = function(fn) {
|
||||
var newJob = new Job();
|
||||
this.next.push(function() {
|
||||
fn();
|
||||
newJob.start();
|
||||
});
|
||||
return newJob;
|
||||
};
|
||||
Job.prototype.waitsFor = function(fn, error, timeout) {
|
||||
var newJob = new Job();
|
||||
timeout = timeout || 5000;
|
||||
this.next.push(function() {
|
||||
var counter = 0,
|
||||
intervalId = window.setInterval(function() {
|
||||
if (fn()) {
|
||||
window.clearInterval(intervalId);
|
||||
newJob.start();
|
||||
}
|
||||
counter += 5;
|
||||
if (counter > timeout) {
|
||||
window.clearInterval(intervalId);
|
||||
throw new Error(error);
|
||||
}
|
||||
}, 5);
|
||||
});
|
||||
return newJob;
|
||||
};
|
||||
Job.prototype.waits = function(timeout) {
|
||||
return this.waitsFor(function() { return true; }, undefined, timeout);
|
||||
};
|
||||
Job.prototype.start = function() {
|
||||
var i;
|
||||
for (i = 0; i < this.next.length; i += 1) {
|
||||
this.next[i]();
|
||||
}
|
||||
};
|
||||
return new Job();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user