Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b4d1e5e492 | |||
| 60394a9d91 | |||
| f5ddb10b56 | |||
| 6a448d3459 | |||
| 6a8c0f5f4a | |||
| 584308fc06 | |||
| 48ad2c44bf | |||
| 100998330a | |||
| b9e85c62be | |||
| 77fad099d2 | |||
| e1f8a6e82b | |||
| 132d767647 | |||
| 9cde98cbc7 | |||
| 1ddbb3ec3e | |||
| 34f40266b2 | |||
| b8c06e3f1b | |||
| a6cf648b3c | |||
| aebde27f1b | |||
| c9be327d53 | |||
| 3dc7d22d90 | |||
| c10c6cac74 | |||
| cee2c4c569 | |||
| a5160c82dc | |||
| 8853312197 | |||
| 049b24de21 | |||
| a9f987a0c9 | |||
| ad4a20d3d2 | |||
| b3972d1b65 | |||
| c3e0d58b3c | |||
| f08156ea9b | |||
| 4bacf5a5da | |||
| df88873bb7 | |||
| c4b1c5e8f1 | |||
| 9822711ad2 | |||
| 30cd764b6d | |||
| 38b75cdb2d | |||
| 6cb8b39af8 | |||
| ebaa336614 | |||
| ef5f567f91 | |||
| 64e5afc478 | |||
| 1e582e4fa4 | |||
| 7421235f24 | |||
| 09ba69078d | |||
| 3536e83d8a | |||
| 3bb1dd5d7f | |||
| 95f964b827 | |||
| c8f78a8ca9 | |||
| f34d48087b | |||
| 2c9ecd01b1 | |||
| a584fb6e15 | |||
| 1f13313f40 | |||
| 5b60303781 | |||
| 10e2552a7d | |||
| ef48b0aa55 | |||
| f57872bca0 | |||
| 2deaf2877e | |||
| 7a146c9cd5 | |||
| 2796ec172b | |||
| 6997c1bf0c | |||
| 4a030f3834 | |||
| f78d8b8ff3 | |||
| f27d19ed60 | |||
| 4a5eaf7bec | |||
| 8513674911 | |||
| 97b74ad6fb | |||
| a47ea79023 | |||
| 5ca0de6487 | |||
| 50a449f053 | |||
| d7422da7d7 | |||
| c7cbc978c6 | |||
| 27146e8a7f | |||
| 5e418b1145 | |||
| f4bb973eb7 | |||
| 848857aa5b | |||
| ee8a05d3f1 | |||
| 275ebbf0ec | |||
| 0f23df4c06 | |||
| 11f700f7bd | |||
| 5785f2a991 | |||
| 2546c29f81 | |||
| 19ea708c9d | |||
| 5cf05d67f2 | |||
| 0377c6f0e8 | |||
| 9c13866824 | |||
| 419a4813e3 | |||
| 131af8272d | |||
| c219a46f59 | |||
| 25f008f541 | |||
| 4a593db79b | |||
| ad4fef0431 | |||
| 8a15fcc1f5 | |||
| f01212ab52 | |||
| 28693a1a67 | |||
| 29fd499552 | |||
| 2f97d9d647 | |||
| 4146b38459 | |||
| 05aab660ce | |||
| 5ecb64849e | |||
| 59dfe1b5a0 | |||
| 50ebfb735c | |||
| 0bdbfe5069 | |||
| 3fc4d6028c | |||
| 2eb12a052b | |||
| bd63b2235c | |||
| f418ffd083 | |||
| 6ab5f8ce4b | |||
| becfeb5aa3 | |||
| eb968c4a68 | |||
| 7f2af3f923 | |||
| cce98ff53a | |||
| b607618342 | |||
| fa50fbaf57 | |||
| f135e2dc05 | |||
| 780351db5e | |||
| a50bb0bfec | |||
| 4d86df6f48 | |||
| bb464d16b4 | |||
| 85b2eb1472 | |||
| 7608f92c6a | |||
| c0bf8db63c | |||
| c95a6737fb | |||
| cd43d24402 | |||
| 086c5d0354 | |||
| 2a2ac5f53a | |||
| 21deaf637a | |||
| 72e15a3a83 | |||
| 174cb4a8c8 | |||
| 33f769b0a1 | |||
| c8abf20558 | |||
| 6dbb183e75 | |||
| bc4844d3b2 | |||
| 708f8b47de | |||
| 5518126d42 | |||
| 5fe73fdc3a | |||
| 394c496bf2 | |||
| 8e1aeba715 | |||
| 0e6e7eb477 | |||
| 183f636816 | |||
| 5f8ed63f2a | |||
| e4f3c94e31 | |||
| 1e5cbcbd93 | |||
| dcf3ec160f | |||
| a7beb5b6d3 | |||
| 1d3b65adc2 | |||
| d528644fe3 | |||
| 6f1bcfc14e | |||
| 996914c6b0 | |||
| fff048d099 | |||
| dcb0da8225 | |||
| b664e20d12 | |||
| 3d68b95028 | |||
| 163aca336d |
+1
-5
@@ -4,8 +4,8 @@ node_js:
|
||||
- '6'
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- bower_components
|
||||
- docs/bower_components
|
||||
|
||||
@@ -36,10 +36,6 @@ addons:
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
before_script:
|
||||
- du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true
|
||||
- ./scripts/travis/before_build.sh
|
||||
|
||||
+532
-28
@@ -1,3 +1,322 @@
|
||||
<a name="1.6.4"></a>
|
||||
# 1.6.4 phenomenal-footnote (2017-03-31)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$parse:**
|
||||
- standardize one-time literal vs non-literal and interceptors
|
||||
([60394a](https://github.com/angular/angular.js/commit/60394a9d91dad8932fa900af7c8529837f1d4557),
|
||||
[#15858](https://github.com/angular/angular.js/issues/15858))
|
||||
- fix infinite digest errors when watching objects with .valueOf in literals
|
||||
([f5ddb1](https://github.com/angular/angular.js/commit/f5ddb10b56676c2ad912ce453acb87f0a7a94e01),
|
||||
[#15867](https://github.com/angular/angular.js/issues/15867))
|
||||
- **ngModel:** prevent internal scope reference from being copied
|
||||
([e1f8a6](https://github.com/angular/angular.js/commit/e1f8a6e82bb8a70079ef3db9a891b1c08b5bae31),
|
||||
[#15833](https://github.com/angular/angular.js/issues/15833))
|
||||
- **jqLite:** make jqLite invoke jqLite.cleanData as a method
|
||||
([9cde98](https://github.com/angular/angular.js/commit/9cde98cbc770f8d33fc074ba563b7ab6e2baaf8b),
|
||||
[#15846](https://github.com/angular/angular.js/issues/15846))
|
||||
- **$http:** throw more informative error on invalid JSON response
|
||||
([df8887](https://github.com/angular/angular.js/commit/df88873bb79213057057adb47151b626a7ec0e5d),
|
||||
[#15695](https://github.com/angular/angular.js/issues/15695),
|
||||
[#15724](https://github.com/angular/angular.js/issues/15724))
|
||||
- **dateFilter:** correctly handle newlines in `format` string
|
||||
([982271](https://github.com/angular/angular.js/commit/9822711ad2a401c2449239edc13d18b301714757),
|
||||
[#15794](https://github.com/angular/angular.js/issues/15794),
|
||||
[#15792](https://github.com/angular/angular.js/issues/15792))
|
||||
|
||||
|
||||
## New Features
|
||||
- **$resource:** add `hasBody` action configuration option
|
||||
([a9f987](https://github.com/angular/angular.js/commit/a9f987a0c9653246ea471a89197907d94c0cea2a),
|
||||
[#10128](https://github.com/angular/angular.js/issues/10128),
|
||||
[#12181](https://github.com/angular/angular.js/issues/12181))
|
||||
|
||||
|
||||
<a name="1.6.3"></a>
|
||||
# 1.6.3 scriptalicious-bootstrapping (2017-03-08)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **Angular:**
|
||||
- do not auto-bootstrap if the `src` exists but is empty
|
||||
([3536e8](https://github.com/angular/angular.js/commit/3536e83d8a085b02bd6dcec8324800b7e6c734e4))
|
||||
- do not auto bootstrap if the currentScript has been clobbered
|
||||
([95f964](https://github.com/angular/angular.js/commit/95f964b827b6f5b5aab10af54f7831316c7a9935))
|
||||
- do not auto-bootstrap if the script source is bad and inside SVG
|
||||
([c8f78a](https://github.com/angular/angular.js/commit/c8f78a8ca9debc33a6deaf951f344b8d372bf210))
|
||||
- **$log:** don't parse error stacks manually outside of IE/Edge
|
||||
([64e5af](https://github.com/angular/angular.js/commit/64e5afc4786fdfd850c6bdb488a5aa2b8b077f74),
|
||||
[#15590](https://github.com/angular/angular.js/issues/15590),
|
||||
[#15767](https://github.com/angular/angular.js/issues/15767))
|
||||
- **$sanitize:** prevent clobbered elements from freezing the browser
|
||||
([3bb1dd](https://github.com/angular/angular.js/commit/3bb1dd5d7f7dcde6fea5a3148f8f10e92f451e9d),
|
||||
[#15699](https://github.com/angular/angular.js/issues/15699))
|
||||
- **$animate:**
|
||||
- reset `classNameFilter` to `null` when a disallowed RegExp is used
|
||||
([a584fb](https://github.com/angular/angular.js/commit/a584fb6e1569fc1dd85e23b251a7c126edc2dd5b),
|
||||
[#14913](https://github.com/angular/angular.js/issues/14913))
|
||||
- improve detection on `ng-animate` in `classNameFilter` RegExp
|
||||
([1f1331](https://github.com/angular/angular.js/commit/1f13313f403381581e1c31c57ebfe7a96546c6e4),
|
||||
[#14806](https://github.com/angular/angular.js/issues/14806))
|
||||
- **filterFilter:** don't throw if `key.charAt` is not a function
|
||||
([f27d19](https://github.com/angular/angular.js/commit/f27d19ed606bf05ba41698159ebbc5fbc195033e),
|
||||
[#15644](https://github.com/angular/angular.js/issues/15644),
|
||||
[#15660](https://github.com/angular/angular.js/issues/15660))
|
||||
- **select:**
|
||||
- add attribute "selected" for `select[multiple]`
|
||||
([851367](https://github.com/angular/angular.js/commit/8513674911300b27d518383a905fde9b3f25f7ae))
|
||||
- keep original selection when using shift to add options in IE/Edge
|
||||
([97b74a](https://github.com/angular/angular.js/commit/97b74ad6fbcbc4b63e37e9eb44962d6f8de83e8b),
|
||||
[#15675](https://github.com/angular/angular.js/issues/15675),
|
||||
[#15676](https://github.com/angular/angular.js/issues/15676))
|
||||
- **$jsonpCallbacks:** allow `$window` to be mocked in unit tests
|
||||
([5ca0de](https://github.com/angular/angular.js/commit/5ca0de64873c32ab2f540a3226e73c4175a15c50),
|
||||
[#15685](https://github.com/angular/angular.js/issues/15685),
|
||||
[#15686](https://github.com/angular/angular.js/issues/15686))
|
||||
|
||||
|
||||
## New Features
|
||||
- **info:** add `angularVersion` info to each module
|
||||
([1e582e](https://github.com/angular/angular.js/commit/1e582e4fa486f340150bba95927f1b26d9142de2))
|
||||
- **$injector:** add new `modules` property
|
||||
([742123](https://github.com/angular/angular.js/commit/7421235f247e5b7113345401bc5727cfbf81ddc2))
|
||||
- **Module:** add `info()` method
|
||||
([09ba69](https://github.com/angular/angular.js/commit/09ba69078de6ba52c70571b82b6205929f6facc5),
|
||||
[#15225](https://github.com/angular/angular.js/issues/15225))
|
||||
- **errorHandlingConfig:** make the depth for object stringification in errors configurable
|
||||
([4a5eaf](https://github.com/angular/angular.js/commit/4a5eaf7bec85ceca8b934ebaff4d1834a1a09f57),
|
||||
[#15402](https://github.com/angular/angular.js/issues/15402),
|
||||
[#15433](https://github.com/angular/angular.js/issues/15433))
|
||||
|
||||
|
||||
<a name="1.6.2"></a>
|
||||
# 1.6.2 llamacorn-lovehug (2017-02-07)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$compile:**
|
||||
- do not swallow thrown errors in testsg
|
||||
([0377c6](https://github.com/angular/angular.js/commit/0377c6f0e890cb4ed3eb020b96720b4b34f75df3),
|
||||
[#15629](https://github.com/angular/angular.js/issues/15629),
|
||||
[#15631](https://github.com/angular/angular.js/issues/15631))
|
||||
- allow the usage of "$" in isolate scope property alias
|
||||
([7f2af3](https://github.com/angular/angular.js/commit/7f2af3f923e7a3f85c8862d0ed57d21c72eae904),
|
||||
[#15594](https://github.com/angular/angular.js/issues/15594))
|
||||
- **$location:** correctly handle external URL change during `$digest`
|
||||
([b60761](https://github.com/angular/angular.js/commit/b607618342d6c4fab364966fe05f152be6bd4d5f),
|
||||
[#11075](https://github.com/angular/angular.js/issues/11075),
|
||||
[#12571](https://github.com/angular/angular.js/issues/12571),
|
||||
[#15556](https://github.com/angular/angular.js/issues/15556),
|
||||
[#15561](https://github.com/angular/angular.js/issues/15561))
|
||||
- **$browser:** detect external changes in `history.state`
|
||||
([fa50fb](https://github.com/angular/angular.js/commit/fa50fbaf57b3437be7a410ecaba7008dbe0ef239))
|
||||
- **$resource:**
|
||||
- do not swallow errors in `success` callback
|
||||
([27146e](https://github.com/angular/angular.js/commit/27146e8a7fad54c1342179b6d291b1b5c2ebe816),
|
||||
[#15624](https://github.com/angular/angular.js/issues/15624),
|
||||
[#15628](https://github.com/angular/angular.js/issues/15628))
|
||||
- correctly unescape `/\.` even if `\.` comes from a param value
|
||||
([419a48](https://github.com/angular/angular.js/commit/419a4813e354496bdf0df44e3f8afaa198df1ab1),
|
||||
[#15627](https://github.com/angular/angular.js/issues/15627))
|
||||
- delete `$cancelRequest()` in `toJSON()`
|
||||
([086c5d](https://github.com/angular/angular.js/commit/086c5d0354db8cb3d106b9ff966fb48d6fb46ef8),
|
||||
[#15244](https://github.com/angular/angular.js/issues/15244))
|
||||
- **$animate:** correctly animate transcluded clones with `templateUrl`
|
||||
([f01212](https://github.com/angular/angular.js/commit/f01212ab5287ac7a154da7d75037ed444e81eb34),
|
||||
[#15510](https://github.com/angular/angular.js/issues/15510),
|
||||
[#15514](https://github.com/angular/angular.js/issues/15514))
|
||||
- **$route:** make asynchronous tasks count as pending requests
|
||||
([eb968c](https://github.com/angular/angular.js/commit/eb968c4a6884838db05369a04459066424c5bba8),
|
||||
[#14159](https://github.com/angular/angular.js/issues/14159))
|
||||
- **$parse:** make sure ES6 object computed properties are watched
|
||||
([5e418b](https://github.com/angular/angular.js/commit/5e418b1145a1045da598c7863e785d647ea83850),
|
||||
[#15678](https://github.com/angular/angular.js/issues/15678))
|
||||
- **$sniffer:** allow `history` for NW.js apps
|
||||
([4a593d](https://github.com/angular/angular.js/commit/4a593db79ba1e21a6aa600a82cf6d757cad94d01),
|
||||
[#15474](https://github.com/angular/angular.js/issues/15474),
|
||||
[#15633](https://github.com/angular/angular.js/issues/15633))
|
||||
- **input:** fix `step` validation for `input[type=number/range]`
|
||||
([c95a67](https://github.com/angular/angular.js/commit/c95a6737fbd277e40c064bd9f68f383bf119505c),
|
||||
[#15504](https://github.com/angular/angular.js/issues/15504),
|
||||
[#15506](https://github.com/angular/angular.js/issues/15506))
|
||||
- **select:** keep `ngModel` when selected option is recreated by `ngRepeat`
|
||||
([131af8](https://github.com/angular/angular.js/commit/131af8272d269a541d04cb522c264a91e0ec8b6a),
|
||||
[#15630](https://github.com/angular/angular.js/issues/15630),
|
||||
[#15632](https://github.com/angular/angular.js/issues/15632))
|
||||
- **ngValue:** correctly update the `value` property when `value` is undefined
|
||||
([05aab6](https://github.com/angular/angular.js/commit/05aab660ce74f526f2110d3b5faf9a5b4f4e664b)
|
||||
[#15603](https://github.com/angular/angular.js/issues/15603),
|
||||
[#15605](https://github.com/angular/angular.js/issues/15605))
|
||||
- **angularInit:** allow auto-bootstrapping from inline script
|
||||
([bb464d](https://github.com/angular/angular.js/commit/bb464d16b434b9e2de2fecf80c192d4741cba879),
|
||||
[#15567](https://github.com/angular/angular.js/issues/15567),
|
||||
[#15571](https://github.com/angular/angular.js/issues/15571))
|
||||
- **ngMockE2E:** ensure that mocked `$httpBackend` uses correct `$browser`
|
||||
([bd63b2](https://github.com/angular/angular.js/commit/bd63b2235cd410251cb83eebd9a47d3102830b6b),
|
||||
[#15593](https://github.com/angular/angular.js/issues/15593))
|
||||
|
||||
|
||||
## New Features
|
||||
- **ngModel:** add `$overrideModelOptions` support
|
||||
([2546c2](https://github.com/angular/angular.js/commit/2546c29f811b68eea4d68be7fa1c8f7bb562dc11),
|
||||
[#15415](https://github.com/angular/angular.js/issues/15415))
|
||||
- **$parse:** allow watching array/object literals with non-primitive values
|
||||
([25f008](https://github.com/angular/angular.js/commit/25f008f541d68b09efd7b428b648c6d4899e6972),
|
||||
[#15301](https://github.com/angular/angular.js/issues/15301))
|
||||
|
||||
|
||||
|
||||
<a name="1.5.11"></a>
|
||||
# 1.5.11 princely-quest (2017-01-13)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$compile:** allow the usage of "$" in isolate scope property alias
|
||||
([e75fbc](https://github.com/angular/angular.js/commit/e75fbc494e6a0da6a9231b40bb0382431b62be07),
|
||||
[#15586](https://github.com/angular/angular.js/issues/15586),
|
||||
[#15594](https://github.com/angular/angular.js/issues/15594))
|
||||
- **angularInit:** allow auto-bootstraping from inline script
|
||||
([41aa91](https://github.com/angular/angular.js/commit/41aa9125b9aaf771addb250642f524a4e6f9d8d3),
|
||||
[#15567](https://github.com/angular/angular.js/issues/15567),
|
||||
[#15571](https://github.com/angular/angular.js/issues/15571))
|
||||
- **$resource:** delete `$cancelRequest()` in `toJSON()`
|
||||
([4f3858](https://github.com/angular/angular.js/commit/4f3858e7c371f87534397f45b9d002add33b00cc),
|
||||
[#15244](https://github.com/angular/angular.js/issues/15244))
|
||||
- **$$cookieReader:** correctly handle forbidden access to `document.cookie`
|
||||
([6933cf](https://github.com/angular/angular.js/commit/6933cf64fe51f54b10d1639f2b95bab3c1178df9),
|
||||
[#15523](https://github.com/angular/angular.js/issues/15523),
|
||||
[#15532](https://github.com/angular/angular.js/issues/15532))
|
||||
|
||||
|
||||
|
||||
<a name="1.6.1"></a>
|
||||
# 1.6.1 promise-rectification (2016-12-23)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$q:** Add traceback to unhandled promise rejections
|
||||
([174cb4](https://github.com/angular/angular.js/commit/174cb4a8c81e25581da5b452c2bb43b0fa377a9b),
|
||||
[#14631](https://github.com/angular/angular.js/issues/14631))
|
||||
- **$$cookieReader:** correctly handle forbidden access to `document.cookie`
|
||||
([33f769](https://github.com/angular/angular.js/commit/33f769b0a1214055c16fb59adad4897bf53d62bf),
|
||||
[#15523](https://github.com/angular/angular.js/issues/15523))
|
||||
- **ngOptions:** do not unset the `selected` property unless necessary
|
||||
([bc4844](https://github.com/angular/angular.js/commit/bc4844d3b297d80aecef89aa1b32615024decedc),
|
||||
[#15477](https://github.com/angular/angular.js/issues/15477))
|
||||
- **ngModelOptions:** work correctly when on the template of `replace` directives
|
||||
([5f8ed6](https://github.com/angular/angular.js/commit/5f8ed63f2ab02ffb9c21bf9c29d27c851d162e26),
|
||||
[#15492](https://github.com/angular/angular.js/issues/15492))
|
||||
- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously
|
||||
([d52864](https://github.com/angular/angular.js/commit/d528644fe3e9ffd43999e7fc67806059f9e1083e))
|
||||
- **jqLite:** silently ignore `after()` if element has no parent
|
||||
([3d68b9](https://github.com/angular/angular.js/commit/3d68b9502848ff6714ef89bfb95b8e70ae34eff6),
|
||||
[#15331](https://github.com/angular/angular.js/issues/15331),
|
||||
[#15475](https://github.com/angular/angular.js/issues/15475))
|
||||
- **$rootScope:** when adding/removing watchers during $digest
|
||||
([163aca](https://github.com/angular/angular.js/commit/163aca336d7586a45255787af41b14b2a12361dd),
|
||||
[#15422](https://github.com/angular/angular.js/issues/15422))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
- **ngClass:** avoid unnecessary `.data()` accesses, deep-watching and copies
|
||||
([1d3b65](https://github.com/angular/angular.js/commit/1d3b65adc2c22ff662159ef910089cf10d1edb7b),
|
||||
[#14404](https://github.com/angular/angular.js/issues/14404))
|
||||
|
||||
|
||||
|
||||
<a name="1.5.10"></a>
|
||||
# 1.5.10 asynchronous-synchronization (2016-12-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$compile:**
|
||||
- don't throw tplrt error when there is whitespace around a top-level comment
|
||||
([12752f](https://github.com/angular/angular.js/commit/12752f66ac425ab38a5ee574a4bfbf3516adc42c),
|
||||
[#15108](https://github.com/angular/angular.js/issues/15108))
|
||||
- clean up `@`-binding observers when re-assigning bindings
|
||||
([f3cb6e](https://github.com/angular/angular.js/commit/f3cb6e309aa1f676e5951ac745fa886d3581c2f4),
|
||||
[#15268](https://github.com/angular/angular.js/issues/15268))
|
||||
- set attribute value even if `ngAttr*` contains no interpolation
|
||||
([229799](https://github.com/angular/angular.js/commit/22979904fb754c59e9f6ee5d8763e3b8de0e18c2),
|
||||
[#15133](https://github.com/angular/angular.js/issues/15133))
|
||||
- `bindToController` should work without `controllerAs`
|
||||
([944989](https://github.com/angular/angular.js/commit/9449893763a4fd95ee8ff78b53c6966a874ec9ae),
|
||||
[#15088](https://github.com/angular/angular.js/issues/15088))
|
||||
- do not overwrite values set in `$onInit()` for `<`-bound literals
|
||||
([07e1ba](https://github.com/angular/angular.js/commit/07e1ba365fb5e8a049be732bd7b62f71e0aa1672),
|
||||
[#15118](https://github.com/angular/angular.js/issues/15118))
|
||||
- avoid calling `$onChanges()` twice for `NaN` initial values
|
||||
([0cf5be](https://github.com/angular/angular.js/commit/0cf5be52642f7e9d81a708b3005042eac6492572))
|
||||
- **$location:** prevent infinite digest with IDN urls in Edge
|
||||
([4bf892](https://github.com/angular/angular.js/commit/4bf89218130d434771089fdfe643490b8d2ee259),
|
||||
[#15217](https://github.com/angular/angular.js/issues/15217))
|
||||
- **$rootScope:** correctly handle adding/removing watchers during `$digest`
|
||||
([a9708d](https://github.com/angular/angular.js/commit/a9708de84b50f06eacda33834d5bbdfc97c97f37),
|
||||
[#15422](https://github.com/angular/angular.js/issues/15422))
|
||||
- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**`
|
||||
([78eecb](https://github.com/angular/angular.js/commit/78eecb43dbb0500358d333aea8955bd0646a7790))
|
||||
- **jqLite:** silently ignore `after()` if element has no parent
|
||||
([77ed85](https://github.com/angular/angular.js/commit/77ed85bcd3be057a5a79231565ac7accc6d644c6),
|
||||
[#15331](https://github.com/angular/angular.js/issues/15331))
|
||||
- **input[radio]:** use non-strict comparison for checkedness
|
||||
([593a50](https://github.com/angular/angular.js/commit/593a5034841b3b7661d3bcbdd06b7a9d0876fd34))
|
||||
- **select, ngOptions:**
|
||||
- let `ngValue` take precedence over option text with multiple interpolations
|
||||
([5b7ec8](https://github.com/angular/angular.js/commit/5b7ec8c84e88ee08aacaf9404853eda0016093f5),
|
||||
[#15413](https://github.com/angular/angular.js/issues/15413))
|
||||
- don't add comment nodes as empty options
|
||||
([1d29c9](https://github.com/angular/angular.js/commit/1d29c91c3429de96e4103533752700d1266741be),
|
||||
[#15454](https://github.com/angular/angular.js/issues/15454))
|
||||
- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously
|
||||
([e3d020](https://github.com/angular/angular.js/commit/e3d02070ab8a02c818dcc5114db6fba9d3f385d6))
|
||||
- **$sanitize:** reduce stack height in IE <= 11
|
||||
([862dc2](https://github.com/angular/angular.js/commit/862dc2532f8126a4a71fd3d957884ba6f11f591c),
|
||||
[#14928](https://github.com/angular/angular.js/issues/14928))
|
||||
- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()`
|
||||
([75c83f](https://github.com/angular/angular.js/commit/75c83ff3195931859a099f7a95bf81d32abf2eb3))
|
||||
|
||||
|
||||
## New Features
|
||||
- **bootstrap:** do not bootstrap from unknown schemes with a different origin
|
||||
([bdeb33](https://github.com/angular/angular.js/commit/bdeb3392a8719131ab2b993f2a881c43a2860f92),
|
||||
[#15428](https://github.com/angular/angular.js/issues/15428))
|
||||
- **$anchorScroll:** convert numeric hash targets to string
|
||||
([a52640](https://github.com/angular/angular.js/commit/a5264090b66ad0cf9a93de84bb7b307868c0edef),
|
||||
[#14680](https://github.com/angular/angular.js/issues/14680))
|
||||
- **$compile:**
|
||||
- add `preAssignBindingsEnabled` option
|
||||
([f86576](https://github.com/angular/angular.js/commit/f86576def44005f180a66e3aa12d6cc73c1ac72c))
|
||||
- throw error when directive name or factory function is invalid
|
||||
([5c9399](https://github.com/angular/angular.js/commit/5c9399d18ae5cd79e6cf6fc4377d66df00f6fcc7),
|
||||
[#15056](https://github.com/angular/angular.js/issues/15056))
|
||||
- **$controller:** throw when requested controller is not registered
|
||||
([9ae793](https://github.com/angular/angular.js/commit/9ae793d8a69afe84370b601e07fc375fc18a576a),
|
||||
[#14980](https://github.com/angular/angular.js/issues/14980))
|
||||
- **$location:** add support for selectively rewriting links based on attribute
|
||||
([a4a222](https://github.com/angular/angular.js/commit/a4a22266f127d3b9a6818e6f4754f048e253f693))
|
||||
- **$resource:** pass `status`/`statusText` to success callbacks
|
||||
([a8da25](https://github.com/angular/angular.js/commit/a8da25c74d2c1f6265f0fafd95bf72c981d9d678),
|
||||
[#8341](https://github.com/angular/angular.js/issues/8841),
|
||||
[#8841](https://github.com/angular/angular.js/issues/8841))
|
||||
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
|
||||
([0e1651](https://github.com/angular/angular.js/commit/0e1651bfd28ba73ebd0e4943d85af48c4506e02c),
|
||||
[#3410](https://github.com/angular/angular.js/issues/3410),
|
||||
[#3516](https://github.com/angular/angular.js/issues/3516))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
- **all:** don't trigger digests after enter/leave of structural directives
|
||||
([c57779](https://github.com/angular/angular.js/commit/c57779d8725493c5853dceda0105dafd5c0e3a7c),
|
||||
[#15322](https://github.com/angular/angular.js/issues/15322))
|
||||
- **$compile:** validate `directive.restrict` property on directive init
|
||||
([31d464](https://github.com/angular/angular.js/commit/31d464feef38b1cc950da6c8dccd0f194ebfc68b))
|
||||
- **ngOptions:** avoid calls to `element.value`
|
||||
([e269ad](https://github.com/angular/angular.js/commit/e269ad1244bc50fee9218f7c18fab3e9ab063aab))
|
||||
- **jqLite:** move bind/unbind definitions out of the loop
|
||||
([7717b9](https://github.com/angular/angular.js/commit/7717b96e950a5916a5f12fd611c73d3b06a8d717))
|
||||
|
||||
|
||||
<a name="1.6.0"></a>
|
||||
# 1.6.0 rainbow-tsunami (2016-12-08)
|
||||
|
||||
@@ -8,14 +327,23 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions`
|
||||
([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa),
|
||||
[#10922](https://github.com/angular/angular.js/issues/10922))
|
||||
- **$compile:** set `preAssignBindingsEnabled` to false by default
|
||||
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858),
|
||||
[#15352](https://github.com/angular/angular.js/issues/15352))
|
||||
- **$compile:**
|
||||
- add `preAssignBindingsEnabled` option
|
||||
([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef))
|
||||
- set `preAssignBindingsEnabled` to false by default
|
||||
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858),
|
||||
[#15352](https://github.com/angular/angular.js/issues/15352))
|
||||
- throw error when directive name or factory function is invalid
|
||||
([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec)
|
||||
[#15056](https://github.com/angular/angular.js/issues/15056))
|
||||
- **jqLite:**
|
||||
- implement `jqLite(f)` as an alias to `jqLite(document).ready(f)`
|
||||
([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e))
|
||||
- don't throw for elements with missing `getAttribute`
|
||||
([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417))
|
||||
- don't get/set properties when getting/setting boolean attributes
|
||||
([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304),
|
||||
[#14126](https://github.com/angular/angular.js/issues/14126))
|
||||
- don't remove a boolean attribute for `.attr(attrName, '')`
|
||||
([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a))
|
||||
- remove the attribute for `.attr(attribute, null)`
|
||||
@@ -38,6 +366,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
- JSONP requests now require a trusted resource URL
|
||||
([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4),
|
||||
[#11352](https://github.com/angular/angular.js/issues/11352))
|
||||
- **$anchorScroll:** convert numeric hash targets to string
|
||||
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
|
||||
[#14680](https://github.com/angular/angular.js/issues/14680))
|
||||
- **select:** support values of any type added with `ngValue`
|
||||
([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622),
|
||||
[#9842](https://github.com/angular/angular.js/issues/9842))
|
||||
@@ -51,6 +382,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
[#10597](https://github.com/angular/angular.js/issues/10597))
|
||||
- allow `ngTrim` to work for `input[type=radio]`
|
||||
([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
|
||||
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
|
||||
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
|
||||
[#3410](https://github.com/angular/angular.js/issues/3410)
|
||||
[#3516](https://github.com/angular/angular.js/issues/3516))
|
||||
- **$interpolate:** use custom `toString()` function if present
|
||||
([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe),
|
||||
[#7317](https://github.com/angular/angular.js/issues/7317),
|
||||
@@ -66,18 +401,30 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0),
|
||||
[#13653](https://github.com/angular/angular.js/issues/13653),
|
||||
[#7992](https://github.com/angular/angular.js/issues/7992))
|
||||
- **$location:** default hashPrefix to `'!'`
|
||||
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52),
|
||||
[#13812](https://github.com/angular/angular.js/issues/13812))
|
||||
- **$resource:** pass `status`/`statusText` to success callbacks
|
||||
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
|
||||
[#8341](https://github.com/angular/angular.js/issues/8841)
|
||||
[#8841](https://github.com/angular/angular.js/issues/8841))
|
||||
- **$location:**
|
||||
- default hashPrefix to `'!'`
|
||||
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
|
||||
[#13812](https://github.com/angular/angular.js/issues/13812))
|
||||
- add support for selectively rewriting links based on attribute
|
||||
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
|
||||
- **$controller:** throw when requested controller is not registered
|
||||
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
|
||||
[#14980](https://github.com/angular/angular.js/issues/14980))
|
||||
|
||||
|
||||
|
||||
## Security Related
|
||||
- Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/09/angular-16-expression-sandbox-removal.html).
|
||||
- **bootstrap:** explicitly whitelist URL schemes for bootstrap. (#15427)
|
||||
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
|
||||
- **bootstrap:**
|
||||
- explicitly whitelist URL schemes for bootstrap.
|
||||
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
|
||||
- do not bootstrap from unknown schemes with a different origin
|
||||
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1))
|
||||
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1)
|
||||
[#15428](https://github.com/angular/angular.js/issues/15428))
|
||||
- **$compile:**
|
||||
- secure `link[href]` as a `RESOURCE_URL`s in `$sce`
|
||||
([04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa),
|
||||
@@ -87,14 +434,18 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$sce:** fix `adjustMatcher` to replace multiple '*' and '**' (#7897)
|
||||
- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**`
|
||||
([991a2b](https://github.com/angular/angular.js/commit/991a2b30e00aed1d312e29555e356a795f9e3d62))
|
||||
- **ngModelOptions:** handle debounce of `updateOn` triggers that are not in debounce list
|
||||
([789790](https://github.com/angular/angular.js/commit/789790feee4d6c5b1f5d5b18ecb0ccf6edd36fb3))
|
||||
- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()`
|
||||
([7d9a79](https://github.com/angular/angular.js/commit/7d9a791c6a8c80d29d6c84afa287c81f2a307439))
|
||||
- **$location:** throw if the path starts with double (back)slashes
|
||||
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
|
||||
- **$location:**
|
||||
- prevent infinite digest with IDN URLs in Edge
|
||||
([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7)
|
||||
[#15217](https://github.com/angular/angular.js/issues/15217))
|
||||
- throw if the path starts with double (back)slashes
|
||||
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
|
||||
- **core:** do not auto-bootstrap when loaded from an extension.
|
||||
([0ff10e](https://github.com/angular/angular.js/commit/0ff10e1b56c6b7c4ac465e35c96a5886e294bac5))
|
||||
- **input[radio]:** use strict comparison when evaluating checked-ness
|
||||
@@ -124,6 +475,20 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
- don't throw tplrt error when there is a whitespace around a top-level comment
|
||||
([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284),
|
||||
[#15108](https://github.com/angular/angular.js/issues/15108))
|
||||
- clean up `@`-binding observers when re-assigning bindings
|
||||
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
|
||||
[#15268](https://github.com/angular/angular.js/issues/15268))
|
||||
- set attribute value even if `ngAttr*` contains no interpolation
|
||||
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
|
||||
[#15133](https://github.com/angular/angular.js/issues/15133))
|
||||
- `bindToController` should work without `controllerAs`
|
||||
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
|
||||
[#15088](https://github.com/angular/angular.js/issues/15088))
|
||||
- do not overwrite values set in `$onInit()` for `<`-bound literals
|
||||
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
|
||||
[#15118](https://github.com/angular/angular.js/issues/15118))
|
||||
- avoid calling `$onChanges()` twice for `NaN` initial values
|
||||
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
|
||||
- disallow linking the same element more than once
|
||||
([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
|
||||
- correctly merge consecutive text nodes on IE11
|
||||
@@ -136,14 +501,14 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
[#5513](https://github.com/angular/angular.js/issues/5513),
|
||||
[#5597](https://github.com/angular/angular.js/issues/5597))
|
||||
- move check for interpolation of on-event attributes to compile time
|
||||
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
|
||||
[#13267](https://github.com/angular/angular.js/issues/13267))
|
||||
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
|
||||
[#13267](https://github.com/angular/angular.js/issues/13267))
|
||||
- **select, ngOptions, ngValue:**
|
||||
- don't add comment nodes as empty options
|
||||
([245b27](https://github.com/angular/angular.js/commit/245b27101aad129061585252b73652054319ca82),
|
||||
[#15454](https://github.com/angular/angular.js/issues/15454))
|
||||
- do not throw when removing the element (e.g. via `ngIf`)
|
||||
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
|
||||
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
|
||||
- add/remove selected attribute for selected/unselected options
|
||||
([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
|
||||
- don't register options when select has no ngModel
|
||||
@@ -158,7 +523,7 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
([e6afca](https://github.com/angular/angular.js/commit/e6afca00c9061a3e13b570796ca3ab428c1723a1),
|
||||
[#14031](https://github.com/angular/angular.js/issues/14031))
|
||||
- **$resource:**
|
||||
- **$resource:** allow params in `hostname` (except for IPv6 addresses)
|
||||
- allow params in `hostname` (except for IPv6 addresses)
|
||||
([752b1e](https://github.com/angular/angular.js/commit/752b1e69b7a8e9c0b908f1980e9c738888f3647c),
|
||||
[#14542](https://github.com/angular/angular.js/issues/14542))
|
||||
- fulfill promise with the correct value on error
|
||||
@@ -202,6 +567,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
- **loader:** `module.decorator` order of operations is now irrelevant
|
||||
([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e),
|
||||
[#12382](https://github.com/angular/angular.js/issues/12382))
|
||||
- **$sanitize:** reduce stack height in IE <= 11
|
||||
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
|
||||
[#14928](https://github.com/angular/angular.js/issues/14928))
|
||||
- **ngAnimate:** make svg elements work with `classNameFilter`
|
||||
([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
|
||||
|
||||
@@ -221,8 +589,11 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
|
||||
- **injector:** cache the results of the native class detection check
|
||||
([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
|
||||
- **$compile:** use strict comparison for `controller === '@'`
|
||||
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
|
||||
- **$compile:**
|
||||
- use strict comparison for `controller === '@'`
|
||||
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
|
||||
- validate `directive.restrict` property on directive init
|
||||
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
|
||||
- **$parse:**
|
||||
- Inline constants
|
||||
([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
|
||||
@@ -452,6 +823,48 @@ var bgColor = elem.css('background-color');
|
||||
var bgColor = elem.css('backgroundColor');
|
||||
```
|
||||
|
||||
- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes
|
||||
|
||||
Previously, all boolean attributes were reflected into the corresponding property when calling a
|
||||
setter and from the corresponding property when calling a getter, even on elements that don't treat
|
||||
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
|
||||
know when to reflect the property. Note that this browser-level conversion differs between browsers;
|
||||
if you need to dynamically change the state of an element, you should modify the property, not the
|
||||
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
|
||||
description about a related change in jQuery 1.9.
|
||||
|
||||
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
CSS:
|
||||
|
||||
```css
|
||||
input[checked="checked"] { ... }
|
||||
```
|
||||
|
||||
JS:
|
||||
|
||||
```js
|
||||
elem1.attr('checked', 'checked');
|
||||
elem2.attr('checked', false);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
CSS:
|
||||
|
||||
```css
|
||||
input:checked { ... }
|
||||
```
|
||||
|
||||
JS:
|
||||
|
||||
```js
|
||||
elem1.prop('checked', true);
|
||||
elem2.prop('checked', false);
|
||||
```
|
||||
|
||||
- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**:
|
||||
don't remove a boolean attribute for `.attr(attrName, '')`
|
||||
|
||||
@@ -778,7 +1191,7 @@ You configure this list in a module configuration block:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
@@ -875,7 +1288,7 @@ previous behaviour simply add a comment:
|
||||
**Note:** Everything described below affects **IE11 only**.
|
||||
|
||||
Previously, consecutive text nodes would not get merged if they had no parent. They will now, which
|
||||
might have unexpectd side effects in the following cases:
|
||||
might have unexpected side effects in the following cases:
|
||||
|
||||
1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly:
|
||||
|
||||
@@ -1029,7 +1442,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
|
||||
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
|
||||
will - by default - be instantiated early on.
|
||||
|
||||
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
|
||||
Although this is not expected to have unwanted side-effects in normal application behavior, it may
|
||||
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
|
||||
request will be made for the default route's template. If not properly "trained", `$httpBackend`
|
||||
will complain about this unexpected request.
|
||||
@@ -1595,6 +2008,7 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
|
||||
- **ngModel:** treat synchronous validators as boolean always ([7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b) [#14734](https://github.com/angular/angular.js/issues/14734))
|
||||
- **$q:** treat thrown errors as regular rejections ([e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9) [#3174](https://github.com/angular/angular.js/issues/3174) [#15213](https://github.com/angular/angular.js/issues/15213))
|
||||
- **ngTransclude:** use fallback content if only whitespace is provided ([32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301) [#15077](https://github.com/angular/angular.js/issues/15077))
|
||||
- **$location:** prevent infinite digest with IDN URLs in Edge ([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7) [#15217](https://github.com/angular/angular.js/issues/15217))
|
||||
- **$compile:**
|
||||
- don't throw tplrt error when there is a whitespace around a top-level comment ([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284) [#15108](https://github.com/angular/angular.js/issues/15108))
|
||||
- disallow linking the same element more than once ([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
|
||||
@@ -1604,6 +2018,20 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
|
||||
- don't add leading white-space in attributes for a specific merge case ([305ba1](https://github.com/angular/angular.js/commit/305ba1a3fb3529cb3fdf04c12ac03fbb4f634456))
|
||||
- don't trim white-space in attributes ([97bbf8](https://github.com/angular/angular.js/commit/97bbf86a1979d099802f0d631c17c54b87563b40) [#5513](https://github.com/angular/angular.js/issues/5513) [#5597](https://github.com/angular/angular.js/issues/5597))
|
||||
- move check for interpolation of on-event attributes to compile time ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4) [#13267](https://github.com/angular/angular.js/issues/13267))
|
||||
- clean up `@`-binding observers when re-assigning bindings
|
||||
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
|
||||
[#15268](https://github.com/angular/angular.js/issues/15268))
|
||||
- set attribute value even if `ngAttr*` contains no interpolation
|
||||
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
|
||||
[#15133](https://github.com/angular/angular.js/issues/15133))
|
||||
- `bindToController` should work without `controllerAs`
|
||||
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
|
||||
[#15088](https://github.com/angular/angular.js/issues/15088))
|
||||
- do not overwrite values set in `$onInit()` for `<`-bound literals
|
||||
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
|
||||
[#15118](https://github.com/angular/angular.js/issues/15118))
|
||||
- avoid calling `$onChanges()` twice for `NaN` initial values
|
||||
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
|
||||
- **select:**
|
||||
- add/remove selected attribute for selected/unselected options ([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
|
||||
- don't register options when select has no ngModel ([e8c2e1](https://github.com/angular/angular.js/commit/e8c2e119758e58e18fe43932d09a8ff9f506aa9d))
|
||||
@@ -1627,6 +2055,9 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
|
||||
- **ngMock/$httpBackend:** fail if a url is provided but is `undefined` ([7551b8](https://github.com/angular/angular.js/commit/7551b8975a91ee286cc2cf4af5e78f924533575e) [#8442](https://github.com/angular/angular.js/issues/8442) [#10934](https://github.com/angular/angular.js/issues/10934))
|
||||
- **$route:** don't process route change controllers and templates for `redirectTo` routes ([7f4b35](https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1) [#3332](https://github.com/angular/angular.js/issues/3332))
|
||||
- **loader:** `module.decorator` order of operations is now irrelevant ([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e) [#12382](https://github.com/angular/angular.js/issues/12382))
|
||||
- **$sanitize:** reduce stack height in IE <= 11
|
||||
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
|
||||
[#14928](https://github.com/angular/angular.js/issues/14928))
|
||||
- **ngAnimate:** make svg elements work with `classNameFilter` ([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
|
||||
|
||||
|
||||
@@ -1634,24 +2065,50 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
|
||||
- **jqLite:**
|
||||
- implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e))
|
||||
- don't throw for elements with missing `getAttribute` ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417))
|
||||
- don't get/set properties when getting/setting boolean attributes ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), [#14126](https://github.com/angular/angular.js/issues/14126))
|
||||
- don't remove a boolean attribute for `.attr(attrName, '')` ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a))
|
||||
- remove the attribute for `.attr(attribute, null)` ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa))
|
||||
- return `[]` for `.val()` on `<select multiple>` with no selection ([d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8))
|
||||
- **$compile:**
|
||||
- add `preAssignBindingsEnabled` option
|
||||
([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef))
|
||||
- throw error when directive name or factory function is invalid
|
||||
([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec)
|
||||
[#15056](https://github.com/angular/angular.js/issues/15056))
|
||||
- **$http:**
|
||||
- remove deprecated callback methods: `success()/error()` ([b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014))
|
||||
- JSONP callback must be specified by `jsonpCallbackParam` config ([fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb) [#15161](https://github.com/angular/angular.js/issues/15161) [#11352](https://github.com/angular/angular.js/issues/11352))
|
||||
- JSONP requests now require a trusted resource URL ([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4) [#11352](https://github.com/angular/angular.js/issues/11352))
|
||||
- **$anchorScroll:** convert numeric hash targets to string
|
||||
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
|
||||
[#14680](https://github.com/angular/angular.js/issues/14680))
|
||||
- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` ([87a2ff](https://github.com/angular/angular.js/commit/87a2ff76af5d0a9268d8eb84db5755077d27c84c) [#10922](https://github.com/angular/angular.js/issues/10922))
|
||||
- **input:**
|
||||
- add support for binding to `input[type=range]` ([913016](https://github.com/angular/angular.js/commit/9130166767c4792c5d32d08a918fc7becf32c9a6) [#5892](https://github.com/angular/angular.js/issues/5892) [#14870](https://github.com/angular/angular.js/issues/14870))
|
||||
- add support for `step` to `input[type=number]` ([e1da4be](https://github.com/angular/angular.js/commit/e1da4bed8e291003d485a8ad346ab80bed8ae2e3) [#10597](https://github.com/angular/angular.js/issues/10597))
|
||||
- allow `ngTrim` to work for `input[type=radio]` ([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
|
||||
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
|
||||
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
|
||||
[#3410](https://github.com/angular/angular.js/issues/3410)
|
||||
[#3516](https://github.com/angular/angular.js/issues/3516))
|
||||
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template ([c13c66](https://github.com/angular/angular.js/commit/c13c666728c1a1485ef18e92d7cb35118ce39609) [#1213](https://github.com/angular/angular.js/issues/1213))
|
||||
- **select:** support values of any type added with `ngValue` ([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622) [#9842](https://github.com/angular/angular.js/issues/9842))
|
||||
- **$interpolate:** use custom `toString()` function if present ([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe) [#7317](https://github.com/angular/angular.js/issues/7317) [#11406](https://github.com/angular/angular.js/issues/11406))
|
||||
- **$route:** implement `resolveRedirectTo` ([e98656](https://github.com/angular/angular.js/commit/e9865654b39c71be71034c38581a8c7bd16bc716) [#5150](https://github.com/angular/angular.js/issues/5150))
|
||||
- **$q:** report promises with non rejection callback ([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0) [#13653](https://github.com/angular/angular.js/issues/13653) [#7992](https://github.com/angular/angular.js/issues/7992))
|
||||
- **$location:** default hashPrefix to `'!'` ([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52) [#13812](https://github.com/angular/angular.js/issues/13812))
|
||||
- **$resource:** pass `status`/`statusText` to success callbacks
|
||||
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
|
||||
[#8341](https://github.com/angular/angular.js/issues/8841)
|
||||
[#8841](https://github.com/angular/angular.js/issues/8841))
|
||||
- **$location:**
|
||||
- default hashPrefix to `'!'`
|
||||
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
|
||||
[#13812](https://github.com/angular/angular.js/issues/13812))
|
||||
- add support for selectively rewriting links based on attribute
|
||||
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
|
||||
- **$controller:** throw when requested controller is not registered
|
||||
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
|
||||
[#14980](https://github.com/angular/angular.js/issues/14980))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
@@ -1660,7 +2117,11 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
|
||||
- **$animate:** listen for document visibility changes ([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
|
||||
- **injector:** cache the results of the native class detection check ([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
|
||||
- **$parse:** Inline constants ([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
|
||||
- **$compile:** use strict comparison for `controller === '@'` ([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
|
||||
- **$compile:**
|
||||
- use strict comparison for `controller === '@'`
|
||||
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
|
||||
- validate `directive.restrict` property on directive init
|
||||
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
|
||||
- **$parse:** remove Angular expression sandbox ([1547c7](https://github.com/angular/angular.js/commit/1547c751aa48efe7dbefef701c3df5983b04aa2e) [#15094](https://github.com/angular/angular.js/issues/15094))
|
||||
|
||||
|
||||
@@ -1782,6 +2243,48 @@ var bgColor = elem.css('background-color');
|
||||
var bgColor = elem.css('backgroundColor');
|
||||
```
|
||||
|
||||
- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes
|
||||
|
||||
Previously, all boolean attributes were reflected into the corresponding property when calling a
|
||||
setter and from the corresponding property when calling a getter, even on elements that don't treat
|
||||
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
|
||||
know when to reflect the property. Note that this browser-level conversion differs between browsers;
|
||||
if you need to dynamically change the state of an element, you should modify the property, not the
|
||||
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
|
||||
description about a related change in jQuery 1.9.
|
||||
|
||||
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
CSS:
|
||||
|
||||
```css
|
||||
input[checked="checked"] { ... }
|
||||
```
|
||||
|
||||
JS:
|
||||
|
||||
```js
|
||||
elem1.attr('checked', 'checked');
|
||||
elem2.attr('checked', false);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
CSS:
|
||||
|
||||
```css
|
||||
input:checked { ... }
|
||||
```
|
||||
|
||||
JS:
|
||||
|
||||
```js
|
||||
elem1.prop('checked', true);
|
||||
elem2.prop('checked', false);
|
||||
```
|
||||
|
||||
- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: don't remove a boolean attribute for `.attr(attrName, '')`
|
||||
|
||||
Before, using the `attr` method with an empty string as a value
|
||||
@@ -2099,7 +2602,7 @@ You configure this list in a module configuration block:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
@@ -2215,7 +2718,7 @@ affects custom directives that might have been reading options for their own pur
|
||||
**Note:** Everything described below affects **IE11 only**.
|
||||
|
||||
Previously, consecutive text nodes would not get merged if they had no parent. They will now, which
|
||||
might have unexpectd side effects in the following cases:
|
||||
might have unexpected side effects in the following cases:
|
||||
|
||||
1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly:
|
||||
|
||||
@@ -2365,7 +2868,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
|
||||
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
|
||||
will - by default - be instantiated early on.
|
||||
|
||||
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
|
||||
Although this is not expected to have unwanted side-effects in normal application behavior, it may
|
||||
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
|
||||
request will be made for the default route's template. If not properly "trained", `$httpBackend`
|
||||
will complain about this unexpected request.
|
||||
@@ -4273,6 +4776,7 @@ support asynchronous loading of resources.)
|
||||
|
||||
- **$compile:** Lazily compile the `transclude` function
|
||||
([652b83eb](https://github.com/angular/angular.js/commit/652b83eb226131d131a44453520a569202aa4aac))
|
||||
See https://github.com/angular/angular.js/issues/14343#issuecomment-229037252 for more information.
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
@@ -12079,7 +12583,7 @@ Contains only these fixes cherry-picked from [v1.2.0rc1](#1.2.0rc1).
|
||||
- due to [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56),
|
||||
Interpolations inside DOM event handlers are disallowed.
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
|
||||
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
|
||||
|
||||
To migrate the code follow the example below:
|
||||
|
||||
@@ -14710,7 +15214,7 @@ with the `$route` service
|
||||
|
||||
### Docs
|
||||
- rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs
|
||||
- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
|
||||
- added support for [sitemap](http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
|
||||
search crawlers
|
||||
- transition of Developer Guide docs from the wiki into docs.angularjs.org
|
||||
- lots of improvements related to formatting of the content of docs.anguarjs.org
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2017 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('animationBenchmark', ['ngAnimate'], config)
|
||||
.controller('BenchmarkController', BenchmarkController);
|
||||
|
||||
// Functions - Definitions
|
||||
function config($compileProvider) {
|
||||
$compileProvider
|
||||
.commentDirectivesEnabled(false)
|
||||
.cssClassDirectivesEnabled(false)
|
||||
.debugInfoEnabled(false);
|
||||
}
|
||||
|
||||
function BenchmarkController($scope) {
|
||||
var self = this;
|
||||
var itemCount = 1000;
|
||||
var items = (new Array(itemCount + 1)).join('.').split('');
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'create',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
self.items = items;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: '$digest',
|
||||
fn: function() {
|
||||
$scope.$root.$digest();
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'destroy',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
self.items = [];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* eslint-env node */
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [
|
||||
{
|
||||
id: 'jquery',
|
||||
src: 'jquery-noop.js'
|
||||
}, {
|
||||
id: 'angular',
|
||||
src: '/build/angular.js'
|
||||
}, {
|
||||
id: 'angular-animate',
|
||||
src: '/build/angular-animate.js'
|
||||
}, {
|
||||
src: 'app.js'
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
// Override me with ?jquery=/bower_components/jquery/dist/jquery.js
|
||||
@@ -0,0 +1,28 @@
|
||||
<style>
|
||||
[ng-cloak] { display: none !important; }
|
||||
.animation-container .ng-enter,
|
||||
.animation-container .ng-leave {
|
||||
transition: all 0.1s;
|
||||
}
|
||||
|
||||
.animation-container .ng-enter,
|
||||
.animation-container .ng-leave.ng-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animation-container .ng-enter.ng-enter-active,
|
||||
.animation-container .ng-leave {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
<div ng-app="animationBenchmark" ng-cloak ng-controller="BenchmarkController as bm">
|
||||
<div class="container-fluid">
|
||||
<h2>Large collection of elements animated in and out with ngAnimate</h2>
|
||||
|
||||
<div class="animation-container">
|
||||
<div ng-repeat="i in bm.items track by $index">
|
||||
Just a plain ol' element
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,108 @@
|
||||
'use strict';
|
||||
|
||||
var app = angular.module('ngClassBenchmark', []);
|
||||
|
||||
app.controller('DataController', function DataController($scope) {
|
||||
|
||||
this.init = function() {
|
||||
this.numberOfTodos = 1000;
|
||||
this.implementation = 'tableOptimized';
|
||||
this.completedPeriodicity = 3;
|
||||
this.importantPeriodicity = 13;
|
||||
this.urgentPeriodicity = 29;
|
||||
|
||||
this.createTodos(100);
|
||||
this.setTodosValuesWithSeed(0);
|
||||
};
|
||||
|
||||
this.clearTodos = function() {
|
||||
this.todos = null;
|
||||
};
|
||||
|
||||
this.createTodos = function(count) {
|
||||
var i;
|
||||
this.todos = [];
|
||||
for (i = 0; i < count; i++) {
|
||||
this.todos.push({
|
||||
id: i + 1,
|
||||
completed: false,
|
||||
important: false,
|
||||
urgent: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.setTodosValuesWithSeed = function(offset) {
|
||||
var i, todo;
|
||||
for (i = 0; i < this.todos.length; i++) {
|
||||
todo = this.todos[i];
|
||||
todo.completed = 0 === (i + offset) % this.completedPeriodicity;
|
||||
todo.important = 0 === (i + offset) % this.importantPeriodicity;
|
||||
todo.urgent = 0 === (i + offset) % this.urgentPeriodicity;
|
||||
}
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'setup',
|
||||
fn: function() {
|
||||
$scope.$apply();
|
||||
this.clearTodos();
|
||||
this.createTodos(this.numberOfTodos);
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'create',
|
||||
fn: function() {
|
||||
// initialize data for first time that will construct the DOM
|
||||
this.setTodosValuesWithSeed(0);
|
||||
$scope.$apply();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: '$apply',
|
||||
fn: function() {
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'update',
|
||||
fn: function() {
|
||||
// move everything but completed
|
||||
this.setTodosValuesWithSeed(3);
|
||||
$scope.$apply();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'unclass',
|
||||
fn: function() {
|
||||
// remove all classes
|
||||
this.setTodosValuesWithSeed(NaN);
|
||||
$scope.$apply();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'class',
|
||||
fn: function() {
|
||||
// add all classes as the initial state
|
||||
this.setTodosValuesWithSeed(0);
|
||||
$scope.$apply();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'destroy',
|
||||
fn: function() {
|
||||
this.clearTodos();
|
||||
$scope.$apply();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [{
|
||||
id: 'angular',
|
||||
src: '/build/angular.js'
|
||||
},
|
||||
{
|
||||
src: 'app.js'
|
||||
}]
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
<style>
|
||||
.gold {
|
||||
background: gold;
|
||||
}
|
||||
.silver {
|
||||
background: silver;
|
||||
}
|
||||
.table tbody tr > td.success {
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
|
||||
.table tbody tr > td.error {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
|
||||
.table tbody tr > td.warning {
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
.table tbody tr > td.info {
|
||||
background-color: #d9edf7;
|
||||
}
|
||||
.completed {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.important {
|
||||
font-weight: bold;
|
||||
}
|
||||
.urgent {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
<div ng-app="ngClassBenchmark" ng-cloak class="container-fluid">
|
||||
<div ng-controller="DataController as benchmark" class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<div class="well">
|
||||
<h3>Parameters</h3>
|
||||
|
||||
<br>
|
||||
<p>
|
||||
<label>Number of todos</label><br>
|
||||
<input type="number" ng-model="benchmark.numberOfTodos">
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<p>
|
||||
<label>Implementation</label><br>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="benchmark.implementation" value="tableOptimized"
|
||||
type="radio" name="implementation">
|
||||
Table optimized <br>
|
||||
<code>ng-class="todo.completed && 'success'"</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="benchmark.implementation" value="table"
|
||||
type="radio" name="implementation">
|
||||
Table <br>
|
||||
<code>ng-class="{success: todo.completed}"</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="benchmark.implementation" value="list"
|
||||
type="radio" name="implementation">
|
||||
List <br>
|
||||
<code>ng-class="{completed: todo.completed, urgent: todo.urgent, important: todo.important"}</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="benchmark.implementation" value="singleOptimized"
|
||||
type="radio" name="implementation">
|
||||
Single ngClass optimized <br>
|
||||
<code>
|
||||
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}"
|
||||
</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="benchmark.implementation" value="single"
|
||||
type="radio" name="implementation">
|
||||
Single ngClass <br>
|
||||
<code>
|
||||
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}"
|
||||
</code>
|
||||
</label>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<h3>Example</h3>
|
||||
<div ng-switch="benchmark.implementation">
|
||||
|
||||
<table ng-switch-when="tableOptimized" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>todo #id</th>
|
||||
<th>completed?</th>
|
||||
<th>urgent?</th>
|
||||
<th>important?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="todo in benchmark.todos track by todo.id"
|
||||
ng-class="todo.completed && 'active'"
|
||||
ng-class-even="todo.completed && todo.important && 'gold'"
|
||||
ng-class-odd="todo.completed && todo.important && 'silver'"
|
||||
>
|
||||
<td>#{{todo.id}}</td>
|
||||
<td>{{todo.completed}}</td>
|
||||
<td ng-class="todo.urgent && 'danger'">{{todo.urgent}}</td>
|
||||
<td ng-class="todo.important && 'success'">{{todo.important}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table ng-switch-when="table" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>todo #id</th>
|
||||
<th>completed?</th>
|
||||
<th>urgent?</th>
|
||||
<th>important?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="todo in benchmark.todos track by todo.id"
|
||||
ng-class="{active: todo.completed}"
|
||||
ng-class-even="{gold: todo.completed && todo.important}"
|
||||
ng-class-odd="{silver: todo.completed && todo.important}"
|
||||
>
|
||||
<td>#{{todo.id}}</td>
|
||||
<td>{{todo.completed}}</td>
|
||||
<td ng-class="{danger: todo.urgent}">{{todo.urgent}}</td>
|
||||
<td ng-class="{success: todo.important}">{{todo.important}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul ng-switch-when="list">
|
||||
<li ng-repeat="todo in benchmark.todos track by todo.id"
|
||||
ng-class="{
|
||||
completed: todo.completed,
|
||||
urgent: todo.urgent,
|
||||
important: todo.important
|
||||
}">#{{todo.id}}</li>
|
||||
</ul>
|
||||
|
||||
<div ng-switch-when="singleOptimized"
|
||||
class="panel"
|
||||
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Information</h3>
|
||||
</div>
|
||||
<div class="panel-body"> The title is green because there are todos... </div>
|
||||
</div>
|
||||
|
||||
<div ng-switch-when="single"
|
||||
class="panel"
|
||||
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Information</h3>
|
||||
</div>
|
||||
<div class="panel-body"> The title is green because there are todos... </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br><br><br>
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "3.1.0",
|
||||
"jquery": "3.2.1",
|
||||
"jquery-2.2": "jquery#2.2.4",
|
||||
"jquery-2.1": "jquery#2.1.4",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.visible-phone{display:none}.visible-desktop{display:block}.navbar{display:block}.navbar .container{padding:0 16px;width:auto}.navbar .brand{float:left;margin:8px 80px 0 8px;padding:0}.navbar .brand a{display:block;height:30px;margin:6px 0 5px 0;overflow:hidden;padding:0;width:117px}.navbar .nav{float:right}.navbar .nav .dropdown-toggle{color:rgba(255,255,255,0.87);font-size:16px;font-weight:300;line-height:56px;padding:0 24px;text-transform:uppercase;transition:all .3s}.navbar .nav .dropdown-toggle:hover,.navbar .nav .dropdown-toggle:active,.navbar .nav .dropdown-toggle:focus{background:#37474F;color:#fff}.navbar .nav .dropdown-menu{background:#37474F;border:none;border-radius:0;box-shadow:0 0 16px rgba(0,0,0,0.12),0 16px 16px rgba(0,0,0,0.24);color:#fff;left:auto;margin:0;padding:0;right:0}.navbar .nav .dropdown-menu:after,.navbar .nav .dropdown-menu:before{display:none}.navbar .nav .dropdown-menu li{border-bottom:1px solid rgba(38,50,56,0.56);box-sizing:border-box;line-height:48px}.navbar .nav .dropdown-menu li:last-child{border:none}.navbar .nav .dropdown-menu a{background:#37474F;color:#fff;font-weight:300;line-height:48px;padding:0 16px;transition:all .2s}.navbar .nav .dropdown-menu a:hover,.navbar .nav .dropdown-menu a:focus{background:#455A64}.navbar .navbar-search{left:200px;margin:0;position:absolute;right:440px;top:8px;width:auto}.navbar .navbar-search i{color:#546E7A;font-size:16px;left:12px;position:absolute;top:11px}.navbar .navbar-search .search-query{background:#37474F;border:none;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#546E7A;font-size:14px;height:40px;width:100%;padding:0 16px 0 32px;text-shadow:none;transition:all .3s}.navbar .navbar-search .search-query:-webkit-autofill,.navbar .navbar-search .search-query:-webkit-autofill:hover,.navbar .navbar-search .search-query:-webkit-autofill:focus{background-color:#fff;transition:background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#455A64}.navbar .navbar-search .search-query:hover,.navbar .navbar-search .search-query:active,.navbar .navbar-search .search-query:focus{background:#fff;box-shadow:inset 0 2px 4px rgba(0,0,0,0.24);color:#2196F3}.navbar .navbar-search .search-query::-webkit-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query::-moz-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-ms-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-moz-placeholder{color:#546E7A}#navbar-main .navbar-inner{background:#263238;height:56px}#navbar-notice{z-index:1029;top:56px}#navbar-notice .navbar-inner{background:#ECEFF1;box-shadow:0 0 3px rgba(0,0,0,0.12),0 3px 3px rgba(0,0,0,0.24);height:auto}.site-notice{padding:4px 0;text-align:center;font-size:13px;margin:0}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.visible-phone{display:block}.visible-desktop{display:none}}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.homepage .container{padding:16px;width:auto}.homepage .span1{width:auto}.homepage .span2{width:auto}.homepage .span3{width:auto}.homepage .span4{width:auto}.homepage .span5{width:auto}.homepage .span6{width:auto}.homepage .span7{width:auto}.homepage .span8{width:auto}.homepage .span9{width:auto}.homepage .span10{width:auto}.homepage .navbar .container{padding:0 8px}.homepage #navbar-main .navbar-inner{height:40px}.homepage #navbar-main .brand{margin:6px 0 0 0}.homepage #navbar-main .brand a{margin:0}.homepage #navbar-main .nav{margin:0}.homepage #navbar-main .nav .dropdown-toggle{font-size:12px;line-height:40px;padding:0 8px}.homepage #navbar-main .dropdown-menu a{padding:0 8px}.homepage #navbar-main .navbar-search{background:#263238;border-bottom:1px solid #263238;left:0;right:0;top:100%}.homepage #navbar-main .navbar-search i{left:12px;top:7px}.homepage #navbar-main .navbar-search .search-query{border-radius:0;height:32px}.homepage #navbar-notice{top:40px}.homepage #navbar-notice .site-notice{font-size:11px}.homepage .hero{padding:80px 32px 32px 32px}.homepage .hero h2{background-size:230px 60px;height:60px;width:230px}}
|
||||
@@ -53,13 +53,13 @@ h1,h2,h3,h4,h5,h6 {
|
||||
}
|
||||
|
||||
.header .brand {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.header .brand img {
|
||||
margin-top: 5px;
|
||||
height: 30px;
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.docs-search {
|
||||
@@ -82,6 +82,11 @@ h1,h2,h3,h4,h5,h6 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.navbar .navbar-search i {
|
||||
top: 13px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.docs-search > .search-query:focus {
|
||||
outline: 0;
|
||||
}
|
||||
@@ -297,6 +302,7 @@ iframe.example {
|
||||
}
|
||||
|
||||
.search-results-container {
|
||||
position: relative;
|
||||
padding-bottom: 1em;
|
||||
border-top: 1px solid #111;
|
||||
background: #181818;
|
||||
@@ -435,15 +441,17 @@ iframe.example {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.sup-header {
|
||||
#navbar-sub {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 5px;
|
||||
background: rgba(245,245,245,0.88);
|
||||
box-shadow: 0 0 2px #999;
|
||||
z-index: 1028;
|
||||
top: 83px;
|
||||
}
|
||||
|
||||
.main-body-grid {
|
||||
margin-top: 120px;
|
||||
margin-top: 144px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -454,7 +462,7 @@ iframe.example {
|
||||
|
||||
.main-body-grid > .grid-left {
|
||||
position: fixed;
|
||||
top: 120px;
|
||||
top: 144px;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -827,3 +835,86 @@ ul.events > li {
|
||||
iframe[name="example-anchoringExample"] {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
/*
|
||||
angular-topnav.css and bootstrap overrides
|
||||
*/
|
||||
|
||||
.navbar .navbar-inner .container {
|
||||
padding: 0 16px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.navbar .nav > li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu {
|
||||
position: absolute;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu > li > a {
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
#navbar-main .navbar-inner, #navbar-notice .navbar-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#navbar-sub .container {
|
||||
max-width: 970px;
|
||||
}
|
||||
|
||||
.nav .open > a, .nav .open > a:hover, .nav .open > a:focus {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
@media handheld and (max-width:800px), screen and (max-device-width:800px), screen and (max-width:800px) {
|
||||
.navbar {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.search-results-container {
|
||||
top: 32px;
|
||||
overflow: auto;
|
||||
max-height: 85vh;
|
||||
padding-bottom: 0;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.search-close {
|
||||
right: 1px;
|
||||
margin-left: 0;
|
||||
top: 41px;
|
||||
padding: 5px 10px;
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
box-shadow: none;
|
||||
width: auto;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.homepage #navbar-notice {
|
||||
top: 72px;
|
||||
}
|
||||
|
||||
#navbar-notice .navbar-inner {
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 3px 3px rgba(0, 0, 0, .24)
|
||||
}
|
||||
|
||||
#navbar-sub {
|
||||
position: relative;
|
||||
top: 17px;
|
||||
margin-top: 80px;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/* global importScripts, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
|
||||
importScripts('../components/lunr-0.7.2/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
|
||||
@@ -18,8 +18,8 @@ describe('doc.angularjs.org', function() {
|
||||
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
|
||||
ngBindLink.click();
|
||||
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('ngClick');
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('ngClick');
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -44,30 +44,31 @@ describe('docs.angularjs.org', function() {
|
||||
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
|
||||
ngBindLink.click();
|
||||
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('ngClick');
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('ngClick');
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should be resilient to trailing slashes', function() {
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
it('should be resilient to trailing "index"', function() {
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
it('should be resilient to trailing "index/"', function() {
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
@@ -78,7 +79,8 @@ describe('docs.angularjs.org', function() {
|
||||
|
||||
it('should display an error if the page does not exist', function() {
|
||||
browser.get('build/docs/index-production.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
var mainHeader = element(by.css('.main-body h1 '));
|
||||
expect(mainHeader.getText()).toEqual('Oops!');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -159,10 +159,11 @@ angular.module('examples', [])
|
||||
|
||||
};
|
||||
|
||||
// Initialize the example data, so it's ready when clicking the open button.
|
||||
// Otherwise pop-up blockers will prevent a new window from opening
|
||||
ctrl.prepareExampleData(ctrl.example.path);
|
||||
|
||||
ctrl.$onInit = function() {
|
||||
// Initialize the example data, so it's ready when clicking the open button.
|
||||
// Otherwise pop-up blockers will prevent a new window from opening
|
||||
ctrl.prepareExampleData(ctrl.example.path);
|
||||
};
|
||||
}]
|
||||
};
|
||||
}])
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.2.3",
|
||||
"lunr.js": "0.5.12",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'../angular-sanitize.js',
|
||||
'../angular-touch.js',
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/current-version-data.js',
|
||||
@@ -32,6 +32,7 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/angular-topnav.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
|
||||
@@ -17,9 +17,9 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/current-version-data.js',
|
||||
@@ -32,6 +32,7 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/angular-topnav.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
|
||||
+3
-2
@@ -21,9 +21,9 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/current-version-data.js',
|
||||
@@ -36,6 +36,7 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/angular-topnav.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
|
||||
@@ -34,9 +34,9 @@ module.exports = function productionDeployment(getVersion) {
|
||||
cdnUrl + '/angular-sanitize.min.js',
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/current-version-data.js',
|
||||
@@ -49,6 +49,7 @@ module.exports = function productionDeployment(getVersion) {
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/angular-topnav.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
|
||||
@@ -10,8 +10,8 @@ module.exports = function getVersion(readFilesProcessor) {
|
||||
var basePath = readFilesProcessor.basePath;
|
||||
|
||||
return function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components');
|
||||
packageFile = packageFile || 'bower.json';
|
||||
sourceFolder = path.resolve(basePath, sourceFolder || 'node_modules');
|
||||
packageFile = packageFile || 'package.json';
|
||||
return require(path.join(sourceFolder,component,packageFile)).version;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -68,101 +68,95 @@
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body class="homepage">
|
||||
<div id="wrapper">
|
||||
<header scroll-y-offset-element class="header header-fixed">
|
||||
<section class="navbar navbar-inverse docs-navbar-primary" ng-controller="DocsSearchCtrl">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
<li><a href="http://angularjs.org"><i class="icon-home icon-white"></i> Home</a></li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-eye-open icon-white"></i> Learn <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="disabled"><a href="http://angularjs.org/">Why AngularJS?</a></li>
|
||||
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
|
||||
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown active">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="guide">Developer Guide</a></li>
|
||||
<li><a href="api">API Reference</a></li>
|
||||
<li><a href="error">Error Reference</a></li>
|
||||
<li><a href="misc/contribute">Contribute</a></li>
|
||||
<li><a href="http://code.angularjs.org/">Download</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-comment icon-white"></i> Discuss <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="http://blog.angularjs.org">Blog</a></li>
|
||||
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
|
||||
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">Chat Room</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://twitter.com/#!/angularjs">Twitter</a></li>
|
||||
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
|
||||
<li><a href="https://github.com/angular/angular.js/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form ng-class="{focus:focus}" class="navbar-search col-md-3 docs-search" ng-submit="submit()">
|
||||
<span class="glyphicon glyphicon-search search-icon"></span>
|
||||
<input type="text"
|
||||
name="as_q"
|
||||
class="search-query"
|
||||
placeholder="Click or press / to search"
|
||||
ng-focus="focus=true"
|
||||
ng-blur="focus=false"
|
||||
ng-change="search(q)"
|
||||
ng-model="q"
|
||||
docs-search-input
|
||||
autocomplete="off" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-results-container" ng-show="hasResults">
|
||||
<header class="header" scroll-y-offset-element>
|
||||
<nav id="navbar-main" class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner" ng-controller="DocsSearchCtrl">
|
||||
<div class="container">
|
||||
<div class="search-results-frame">
|
||||
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
|
||||
<h4 class="search-results-group-heading">{{ key }}</h4>
|
||||
<ul class="search-results">
|
||||
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
|
||||
TODO: use a html minifier instead -->
|
||||
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
|
||||
<h1 class="brand"><a href="http://angularjs.org"><img width="117" height="30" src="img/angularjs-for-header-only.svg" alt="AngularJS"></a></h1>
|
||||
|
||||
<form class="navbar-search" ng-submit="submit()">
|
||||
<i class="glyphicon glyphicon-search search-icon"></i>
|
||||
<input type="text" name="as_q" class="search-query" placeholder="SEARCH"
|
||||
ng-focus="focus=true"
|
||||
ng-blur="focus=false"
|
||||
ng-change="search(q)"
|
||||
ng-model="q"
|
||||
ng-model-options="{debounce: 150}"
|
||||
docs-search-input
|
||||
autocomplete="off">
|
||||
</form>
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown" uib-dropdown>
|
||||
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Learn</a>
|
||||
<ul class="dropdown-menu" uib-dropdown-menu>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
<li><a href="https://www.youtube.com/user/angularjs">Videos</a></li>
|
||||
<li><a href="http://angular.codeschool.com/">Free Course</a></li>
|
||||
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown" uib-dropdown>
|
||||
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Develop</a>
|
||||
<ul class="dropdown-menu" uib-dropdown-menu>
|
||||
<li><a href="guide">Developer Guide</a></li>
|
||||
<li><a href="api">API Reference</a></li>
|
||||
<li><a href="error">Error Reference</a></li>
|
||||
<li><a href="misc/contribute">Contribute</a></li>
|
||||
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
|
||||
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
|
||||
<li><a href="http://code.angularjs.org/">Download</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown" uib-dropdown>
|
||||
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Discuss</a>
|
||||
<ul class="dropdown-menu" uib-dropdown-menu>
|
||||
<li><a href="http://blog.angularjs.org">Blog</a></li>
|
||||
<li><a href="https://twitter.com/angular">Twitter</a></li>
|
||||
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
|
||||
<li><a href="https://github.com/angular/angular.js/issues">Feature & Bug Tracker</a></li>
|
||||
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
|
||||
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">IRC</a></li>
|
||||
<li><a href="https://gitter.im/angular/angular.js">Gitter</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div class="search-results-container" ng-show="hasResults">
|
||||
<div class="container">
|
||||
<div class="search-results-frame">
|
||||
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
|
||||
<h4 class="search-results-group-heading">{{ key }}</h4>
|
||||
<ul class="search-results">
|
||||
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<a href="" ng-click="hideResults()" class="search-close">
|
||||
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
|
||||
</a>
|
||||
</div>
|
||||
<a href="" ng-click="hideResults()" class="search-close">
|
||||
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="sup-header">
|
||||
</nav>
|
||||
<nav id="navbar-notice" class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<p class="site-notice visible-phone">
|
||||
This site refers to AngularJS (v1.x). <a href="https://angular.io/">Go to the latest Angular</a>.
|
||||
</p>
|
||||
<p class="site-notice visible-desktop">
|
||||
This site and all of its contents are referring to AngularJS (version 1.x),
|
||||
if you are looking for the latest Angular, please visit <a href="https://angular.io/">angular.io</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top">
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<version-picker></version-picker>
|
||||
@@ -176,7 +170,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<section role="main" class="container main-body">
|
||||
@@ -214,7 +208,7 @@
|
||||
<p class="pull-right"><a back-to-top>Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2016
|
||||
Super-powered by Google ©2010-2017
|
||||
(<a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
|
||||
|
||||
@@ -61,12 +61,12 @@
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
{% include "lib/params.template.html" %}
|
||||
{% include "lib/events.template.html" %}
|
||||
|
||||
{%- if doc.animations %}
|
||||
<h2 id="animations">Animations</h2>
|
||||
{$ doc.animations | marked $}
|
||||
{$ 'module:ngAnimate.$animate' | link('Click here', doc) $} to learn more about the steps involved in the animation.
|
||||
{%- endif -%}
|
||||
|
||||
{% include "lib/params.template.html" %}
|
||||
{% include "lib/events.template.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $animate:nocb
|
||||
@fullName Do not pass a callback to animate methods
|
||||
@description
|
||||
|
||||
Since Angular 1.3, the methods of {@link ng.$animate} do not accept a callback as the last parameter.
|
||||
Instead, they return a promise to which you can attach `then` handlers to be run when the animation completes.
|
||||
|
||||
If you are getting this error then you need to update your code to use the promise-based API.
|
||||
|
||||
See https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9 for information about
|
||||
the change to the animation API and the changes you need to make.
|
||||
@@ -0,0 +1,8 @@
|
||||
@ngdoc error
|
||||
@name $animate:nongcls
|
||||
@fullName `ng-animate` class not allowed
|
||||
@description
|
||||
|
||||
This error occurs, when trying to set `$animateProvider.classNameFilter()` to a RegExp containing
|
||||
the reserved `ng-animate` class. Since `.ng-animate` will be added/removed by `$animate` itself,
|
||||
using it as part of the `classNameFilter` RegExp is not allowed.
|
||||
@@ -0,0 +1,38 @@
|
||||
@ngdoc error
|
||||
@name $compile:noslot
|
||||
@fullName No matching slot in parent directive
|
||||
@description
|
||||
|
||||
This error occurs when declaring a specific slot in a {@link ng.ngTransclude `ngTransclude`}
|
||||
which does not map to a specific slot defined in the transclude property of the directive.
|
||||
|
||||
In this example the template has declared a slot missing from the transclude definition.
|
||||
This example will generate a noslot error.
|
||||
```js
|
||||
var componentConfig = {
|
||||
template: '<div>' +
|
||||
'<div ng-transclude="slotProvided"></div>' +
|
||||
'<div ng-transclude="noSlotProvided"></div>' +
|
||||
'</div>',
|
||||
transclude: {
|
||||
// The key value pairs here are considered "slots" that are provided for components to slot into.
|
||||
slotProvided: 'slottedComponent', // mandatory transclusion
|
||||
// There is no slot provided here for the transclude 'noSlotProvided' declared in the above template.
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If we make the following change we will no longer get the noslot error.
|
||||
```js
|
||||
var componentConfig = {
|
||||
template: '<div>' +
|
||||
'<div ng-transclude="slotProvided"></div>' +
|
||||
'<div ng-transclude="noSlotProvided"></div>' +
|
||||
'</div>',
|
||||
transclude: {
|
||||
slotProvided: 'slottedComponent',
|
||||
noSlotProvided: 'otherComponent' // now it is declared and the error should cease
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
@ngdoc error
|
||||
@name $http:baddata
|
||||
@fullName Bad JSON Data
|
||||
@description
|
||||
|
||||
The default @{link ng.$http#default-transformations `transformResponse`} will try to parse the
|
||||
response as JSON if the `Content-Type` header is `application/json` or the response looks like a
|
||||
valid JSON-stringified object or array.
|
||||
This error occurs when that data is not a valid JSON object.
|
||||
|
||||
The error message should provide additional context such as the actual response.
|
||||
|
||||
To resolve this error, make sure you pass valid JSON data to `transformResponse` or use an
|
||||
appropriate `Content-Type` header for non-JSON data.
|
||||
@@ -0,0 +1,11 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:elclob
|
||||
@fullName Failed to sanitize html because the element is clobbered
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer is unable to traverse the HTML because one or more of the elements in the
|
||||
HTML have been "clobbered". This could be a sign that the payload contains code attempting to cause a DoS attack on the
|
||||
browser.
|
||||
|
||||
Typically clobbering breaks the `nextSibling` property on an element so that it points to one of its child nodes. This
|
||||
makes it impossible to walk the HTML tree without getting stuck in an infinite loop, which causes the browser to freeze.
|
||||
@@ -0,0 +1,7 @@
|
||||
@ngdoc error
|
||||
@name ng:aobj
|
||||
@fullName Invalid Argument
|
||||
@description
|
||||
|
||||
The argument passed should be an object. Check the value that was passed to the function where
|
||||
this error was thrown.
|
||||
@@ -232,27 +232,27 @@ than the hash fragment was changed.
|
||||
### Example
|
||||
|
||||
```js
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
it('should show example', function() {
|
||||
module(function($locationProvider) {
|
||||
$locationProvider.html5Mode(false);
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
});
|
||||
inject(function($location) {
|
||||
// open http://example.com/base/index.html#!/a
|
||||
$location.absUrl() === 'http://example.com/base/index.html#!/a'
|
||||
$location.path() === '/a'
|
||||
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/a');
|
||||
expect($location.path()).toBe('/a');
|
||||
|
||||
$location.path('/foo')
|
||||
$location.absUrl() === 'http://example.com/base/index.html#!/foo'
|
||||
$location.path('/foo');
|
||||
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo');
|
||||
|
||||
$location.search() === {}
|
||||
expect($location.search()).toEqual({});
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() === 'http://example.com/base/index.html#!/foo?a=b&c'
|
||||
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo?a=b&c');
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.absUrl() === 'http://example.com/base/index.html#!/new?x=y'
|
||||
}
|
||||
));
|
||||
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/new?x=y');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## HTML5 mode
|
||||
@@ -274,40 +274,50 @@ and updates the url in a way that never performs a full page reload.
|
||||
### Example
|
||||
|
||||
```js
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
it('should show example', function() {
|
||||
module(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
});
|
||||
inject(function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
// open http://example.com/#!/a -> rewrite to http://example.com/a
|
||||
// (replacing the http://example.com/#!/a history record)
|
||||
$location.path() === '/a'
|
||||
expect($location.path()).toBe('/a');
|
||||
|
||||
$location.path('/foo');
|
||||
$location.absUrl() === 'http://example.com/foo'
|
||||
expect($location.absUrl()).toBe('http://example.com/foo');
|
||||
|
||||
$location.search() === {}
|
||||
expect($location.search()).toEqual({});
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() === 'http://example.com/foo?a=b&c'
|
||||
expect($location.absUrl()).toBe('http://example.com/foo?a=b&c');
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.url() === 'new?x=y'
|
||||
$location.absUrl() === 'http://example.com/new?x=y'
|
||||
expect($location.url()).toBe('/new?x=y');
|
||||
expect($location.absUrl()).toBe('http://example.com/new?x=y');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show example (when browser doesn\'t support HTML5 mode', function() {
|
||||
module(function($provide, $locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix('!');
|
||||
$provide.value('$sniffer', {history: false});
|
||||
});
|
||||
inject(initBrowser({ url: 'http://example.com/new?x=y', basePath: '/' }),
|
||||
function($location) {
|
||||
// in browser without html5 history support:
|
||||
// open http://example.com/new?x=y -> redirect to http://example.com/#!/new?x=y
|
||||
// (again replacing the http://example.com/new?x=y history item)
|
||||
$location.path() === '/new'
|
||||
$location.search() === {x: 'y'}
|
||||
expect($location.path()).toBe('/new');
|
||||
expect($location.search()).toEqual({x: 'y'});
|
||||
|
||||
$location.path('/foo/bar');
|
||||
$location.path() === '/foo/bar'
|
||||
$location.url() === '/foo/bar?x=y'
|
||||
$location.absUrl() === 'http://example.com/#!/foo/bar?x=y'
|
||||
}
|
||||
));
|
||||
expect($location.path()).toBe('/foo/bar');
|
||||
expect($location.url()).toBe('/foo/bar?x=y');
|
||||
expect($location.absUrl()).toBe('http://example.com/#!/foo/bar?x=y');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Fallback for legacy browsers
|
||||
@@ -555,7 +565,7 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
|
||||
|
||||
</example>
|
||||
|
||||
####Browser in HTML5 Fallback mode (Hashbang mode)
|
||||
#### Browser in HTML5 Fallback mode (Hashbang mode)
|
||||
<example module="hashbang-mode" name="location-hashbang-mode">
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
|
||||
@@ -122,7 +122,7 @@ The same approach to animation can be used using JavaScript code (**jQuery is us
|
||||
```js
|
||||
myModule.animation('.repeated-item', function() {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
enter: function(element, done) {
|
||||
element.css('opacity',0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
@@ -137,7 +137,7 @@ myModule.animation('.repeated-item', function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
leave : function(element, done) {
|
||||
leave: function(element, done) {
|
||||
element.css('opacity', 1);
|
||||
jQuery(element).animate({
|
||||
opacity: 0
|
||||
@@ -152,7 +152,7 @@ myModule.animation('.repeated-item', function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
move : function(element, done) {
|
||||
move: function(element, done) {
|
||||
element.css('opacity', 0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
@@ -169,8 +169,8 @@ myModule.animation('.repeated-item', function() {
|
||||
},
|
||||
|
||||
// you can also capture these animation events
|
||||
addClass : function(element, className, done) {},
|
||||
removeClass : function(element, className, done) {}
|
||||
addClass: function(element, className, done) {},
|
||||
removeClass: function(element, className, done) {}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -5,19 +5,18 @@
|
||||
|
||||
# Component Router
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
<div class="alert alert-danger">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in the new Angular, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the new Angular Router to AngularJS, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
</div>
|
||||
|
||||
This guide describes the new Component Router for AngularJS 1.5.
|
||||
|
||||
<div class="alert alert-info">
|
||||
If you are looking for information about the old router for AngularJS 1.4 and
|
||||
earlier have a look at the {@link ngRoute} module.
|
||||
If you are looking for information about the default router for AngularJS have a look at the {@link ngRoute} module.
|
||||
|
||||
If you are looking for information about the Component Router for Angular 2 then
|
||||
check out the [Angular 2 Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
|
||||
If you are looking for information about the Component Router for the new Angular then
|
||||
check out the [Angular Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -78,6 +78,7 @@ It's also possible to add components via {@link $compileProvider#component} in a
|
||||
| link functions | Yes | No |
|
||||
| multiElement | Yes | No |
|
||||
| priority | Yes | No |
|
||||
| replace | Yes (deprecated) | No |
|
||||
| require | Yes | Yes |
|
||||
| restrict | Yes | No (restricted to elements only) |
|
||||
| scope | Yes (default: false) | No (scope is always isolate) |
|
||||
|
||||
@@ -120,11 +120,13 @@ The other forms shown above are accepted for legacy reasons but we advise you to
|
||||
|
||||
### Directive types
|
||||
|
||||
`$compile` can match directives based on element names, attributes, class names, as well as comments.
|
||||
`$compile` can match directives based on element names (E), attributes (A), class names (C),
|
||||
and comments (M).
|
||||
|
||||
All of the Angular-provided directives match attribute name, tag name, comments, or class name.
|
||||
The following demonstrates the various ways a directive (`myDir` in this case) can be referenced
|
||||
from within a template:
|
||||
The built-in the AngularJS directives show in their documentation page which type of matching they support.
|
||||
|
||||
The following demonstrates the various ways a directive that matches all 4 types
|
||||
(`myDir` in this case) can be referenced from within a template.
|
||||
|
||||
```html
|
||||
<my-dir></my-dir>
|
||||
@@ -133,6 +135,10 @@ from within a template:
|
||||
<span class="my-dir: exp;"></span>
|
||||
```
|
||||
|
||||
A directive can specify which of the 4 matching types it supports in the
|
||||
{@link ng.$compile#-restrict- `restrict`} property of the directive definition object.
|
||||
The default is `EA`.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** Prefer using directives via tag name and attributes over comment and class names.
|
||||
Doing so generally makes it easier to determine what directives a given element matches.
|
||||
|
||||
@@ -241,7 +241,7 @@ An expression that starts with `::` is considered a one-time expression. One-tim
|
||||
will stop recalculating once they are stable, which happens after the first digest if the expression
|
||||
result is a non-undefined value (see value stabilization algorithm below).
|
||||
|
||||
<example module="oneTimeBidingExampleApp" name="expression-one-time">
|
||||
<example module="oneTimeBindingExampleApp" name="expression-one-time">
|
||||
<file name="index.html">
|
||||
<div ng-controller="EventController">
|
||||
<button ng-click="clickMe($event)">Click Me</button>
|
||||
@@ -250,7 +250,7 @@ result is a non-undefined value (see value stabilization algorithm below).
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('oneTimeBidingExampleApp', []).
|
||||
angular.module('oneTimeBindingExampleApp', []).
|
||||
controller('EventController', ['$scope', function($scope) {
|
||||
var counter = 0;
|
||||
var names = ['Igor', 'Misko', 'Chirayu', 'Lucas'];
|
||||
@@ -265,24 +265,24 @@ result is a non-undefined value (see value stabilization algorithm below).
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should freeze binding after its value has stabilized', function() {
|
||||
var oneTimeBiding = element(by.id('one-time-binding-example'));
|
||||
var oneTimeBinding = element(by.id('one-time-binding-example'));
|
||||
var normalBinding = element(by.id('normal-binding-example'));
|
||||
|
||||
expect(oneTimeBiding.getText()).toEqual('One time binding:');
|
||||
expect(oneTimeBinding.getText()).toEqual('One time binding:');
|
||||
expect(normalBinding.getText()).toEqual('Normal binding:');
|
||||
element(by.buttonText('Click Me')).click();
|
||||
|
||||
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
|
||||
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
|
||||
expect(normalBinding.getText()).toEqual('Normal binding: Igor');
|
||||
element(by.buttonText('Click Me')).click();
|
||||
|
||||
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
|
||||
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
|
||||
expect(normalBinding.getText()).toEqual('Normal binding: Misko');
|
||||
|
||||
element(by.buttonText('Click Me')).click();
|
||||
element(by.buttonText('Click Me')).click();
|
||||
|
||||
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
|
||||
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
|
||||
expect(normalBinding.getText()).toEqual('Normal binding: Lucas');
|
||||
});
|
||||
</file>
|
||||
|
||||
@@ -119,7 +119,8 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http
|
||||
### Books
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
* [AngularJS Essentials (Free eBook)](https://www.packtpub.com/packt/free-ebook/angularjs-essentials) by Rodrigo Branas
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
* [AngularJS in Action](https://www.manning.com/books/angularjs-in-action) by Lukas Ruebbelke
|
||||
* [AngularJS: Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
|
||||
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
|
||||
@@ -28,8 +28,8 @@ for other directives to augment its behavior.
|
||||
<form novalidate class="simple-form">
|
||||
<label>Name: <input type="text" ng-model="user.name" /></label><br />
|
||||
<label>E-mail: <input type="email" ng-model="user.email" /></label><br />
|
||||
Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
|
||||
<label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
|
||||
Best Editor: <label><input type="radio" ng-model="user.preference" value="vi" />vi</label>
|
||||
<label><input type="radio" ng-model="user.preference" value="emacs" />emacs</label><br />
|
||||
<input type="button" ng-click="reset()" value="Reset" />
|
||||
<input type="submit" ng-click="update(user)" value="Save" />
|
||||
</form>
|
||||
|
||||
@@ -26,7 +26,7 @@ directive}. Additionally, you can use {@link guide/i18n#messageformat-extension
|
||||
All localizable Angular components depend on locale-specific rule sets managed by the {@link
|
||||
ng.$locale `$locale` service}.
|
||||
|
||||
There a few examples that showcase how to use Angular filters with various locale rule sets in the
|
||||
There are a few examples that showcase how to use Angular filters with various locale rule sets in the
|
||||
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
|
||||
source code.
|
||||
|
||||
@@ -85,7 +85,7 @@ requires German locale, you would serve index_de-de.html which will look somethi
|
||||
|
||||
Both approaches described above require you to prepare different `index.html` pages or JavaScript
|
||||
files for each locale that your app may use. You also need to configure your server to serve
|
||||
the correct file that correspond to the desired locale.
|
||||
the correct file that corresponds to the desired locale.
|
||||
|
||||
The second approach (including the locale JavaScript file in `index.html`) may be slower because
|
||||
an extra script needs to be loaded.
|
||||
|
||||
@@ -88,7 +88,9 @@ commits for more info.
|
||||
|
||||
- **jqLite** is more aligned to jQuery 3, which required the following changes
|
||||
(see [details](guide/migration#migrate1.5to1.6-ng-misc-jqLite) below):
|
||||
- Keys passed to `.data()` and `.css()` are now camelCased in the same as jQuery does it.
|
||||
- Keys passed to `.data()` and `.css()` are now camelCased in the same way as the jQuery methods
|
||||
do.
|
||||
- Getting/setting boolean attributes no longer takes the corresponding properties into account.
|
||||
- Setting boolean attributes to empty string no longer removes the attribute.
|
||||
- Calling `.val()` on a multiple select will always return an array, even if no option is
|
||||
selected.
|
||||
@@ -416,7 +418,7 @@ will be removed in a future version, so we strongly recommend migrating your app
|
||||
rely on it as soon as possible.
|
||||
|
||||
Initialization logic that relies on bindings being present should be put in the controller's
|
||||
`$onInit()` method, which is guarranteed to always be called _after_ the bindings have been
|
||||
`$onInit()` method, which is guaranteed to always be called _after_ the bindings have been
|
||||
assigned.
|
||||
|
||||
Before:
|
||||
@@ -501,7 +503,7 @@ running at `https://docs.angularjs.org` then the following will fail:
|
||||
<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet" />
|
||||
```
|
||||
|
||||
By default, only URLs with the same domain and prototocl as the application document are considered
|
||||
By default, only URLs with the same domain and protocol as the application document are considered
|
||||
safe in the `RESOURCE_URL` context. To use URLs from other domains and/or protocols, you may either
|
||||
whitelist them or wrap them into a trusted value by calling `$sce.trustAsResourceUrl(url)`.
|
||||
|
||||
@@ -586,7 +588,7 @@ trust a URL:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
@@ -880,6 +882,48 @@ var bgColor = elem.css('background-color');
|
||||
var bgColor = elem.css('backgroundColor');
|
||||
```
|
||||
|
||||
<hr />
|
||||
<major />
|
||||
**Due to [7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**,
|
||||
getting/setting boolean attributes will no longer take the corresponding properties into account.
|
||||
Previously, all boolean attributes were reflected into the corresponding property when calling a
|
||||
setter and from the corresponding property when calling a getter, even on elements that don't treat
|
||||
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
|
||||
know when to reflect the property. Note that this browser-level conversion differs between browsers;
|
||||
if you need to dynamically change the state of an element, you should modify the property, not the
|
||||
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
|
||||
description about a related change in jQuery 1.9.
|
||||
|
||||
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
```css
|
||||
/* CSS */
|
||||
|
||||
input[checked="checked"] { ... }
|
||||
```
|
||||
```js
|
||||
// JS
|
||||
|
||||
elem1.attr('checked', 'checked');
|
||||
elem2.attr('checked', false);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```css
|
||||
/* CSS */
|
||||
|
||||
input:checked { ... }
|
||||
```
|
||||
```js
|
||||
// JS
|
||||
|
||||
elem1.prop('checked', true);
|
||||
elem2.prop('checked', false);
|
||||
```
|
||||
|
||||
<hr />
|
||||
<major />
|
||||
**Due to [3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**,
|
||||
@@ -1169,7 +1213,7 @@ with an unencoded `;` character.
|
||||
Previously, in cases where `ngView` was loaded asynchronously, `$route` (and its dependencies) might
|
||||
also have been instantiated asynchronously.
|
||||
|
||||
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
|
||||
Although this is not expected to have unwanted side-effects in normal application behavior, it may
|
||||
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
|
||||
request will be made for the default route's template. If not properly "trained", `$httpBackend`
|
||||
will complain about this unexpected request. You can restore the previous behavior (and avoid
|
||||
@@ -1410,9 +1454,8 @@ For more info on the topic, you can take a look at this
|
||||
|
||||
## Migrating from 1.3 to 1.4
|
||||
|
||||
Angular 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
|
||||
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions `and some fixes to core filters:
|
||||
`limitTo` and `filter`.
|
||||
AngularJS 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
|
||||
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern` and some fixes to core filters: `limitTo` and `filter`.
|
||||
|
||||
The reason for the ngAnimate refactor was to fix timing issues and to expose new APIs to allow
|
||||
for developers to construct more versatile animations. We now have access to `$animateCss`
|
||||
@@ -1425,9 +1468,9 @@ to render error messages with ngMessages that are listed with a directive such a
|
||||
involves pulling error message data from a server and then displaying that data via the mechanics of ngMessages. Be
|
||||
sure to read the breaking change involved with `ngMessagesInclude` to upgrade your template code.
|
||||
|
||||
Other changes, such as the ordering of elements with ngRepeat and ngOptions, may also affect the behavior of your
|
||||
application. And be sure to also read up on the changes to `$cookies`. The migration jump from 1.3 to 1.4 should be
|
||||
relatively straightforward otherwise.
|
||||
Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives
|
||||
validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`.
|
||||
The migration jump from 1.3 to 1.4 should be relatively straightforward otherwise.
|
||||
|
||||
|
||||
|
||||
@@ -1531,7 +1574,7 @@ class based animations (animations triggered via ngClass) in order to ensure tha
|
||||
|
||||
|
||||
|
||||
### Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
### Forms (`ngMessages`, `ngOptions`, `select`, `ngPattern` and `pattern`)
|
||||
|
||||
#### ngMessages
|
||||
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
|
||||
@@ -1639,6 +1682,79 @@ ngModelCtrl.$formatters.push(function(value) {
|
||||
});
|
||||
```
|
||||
|
||||
#### ngPattern and pattern
|
||||
|
||||
Due to [0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
|
||||
The `ngPattern` and `pattern` directives will validate the regex
|
||||
against the `$viewValue` of `ngModel`, i.e. the value of the model
|
||||
before the $parsers are applied. Previously, the `$modelValue`
|
||||
(the result of the $parsers) was validated.
|
||||
|
||||
This fixes issues where `input[date]` and `input[number]` cannot
|
||||
be validated because the `$viewValue` string is parsed into
|
||||
`Date` and `Number` respectively (starting with Angular 1.3).
|
||||
It also brings the directives in line with HTML5 constraint
|
||||
validation, which validates against the input value.
|
||||
|
||||
This change is unlikely to cause applications to fail, because even
|
||||
in Angular 1.2, the value that was validated by pattern could have
|
||||
been manipulated by the $parsers, as all validation was done
|
||||
inside this pipeline.
|
||||
|
||||
If you rely on the pattern being validated against the `$modelValue`,
|
||||
you must create your own validator directive that overwrites
|
||||
the built-in pattern validator:
|
||||
|
||||
```
|
||||
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
priority: 1,
|
||||
compile: function() {
|
||||
var regexp, patternExp;
|
||||
|
||||
return {
|
||||
pre: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
|
||||
attr.$observe('pattern', function(regex) {
|
||||
/**
|
||||
* The built-in directive will call our overwritten validator
|
||||
* (see below). We just need to update the regex.
|
||||
* The preLink fn guaranetees our observer is called first.
|
||||
*/
|
||||
if (isString(regex) && regex.length > 0) {
|
||||
regex = new RegExp('^' + regex + '$');
|
||||
}
|
||||
|
||||
if (regex && !regex.test) {
|
||||
//The built-in validator will throw at this point
|
||||
return;
|
||||
}
|
||||
|
||||
regexp = regex || undefined;
|
||||
});
|
||||
|
||||
},
|
||||
post: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
|
||||
regexp, patternExp = attr.ngPattern || attr.pattern;
|
||||
|
||||
//The postLink fn guarantees we overwrite the built-in pattern validator
|
||||
ctrl.$validators.pattern = function(value) {
|
||||
return ctrl.$isEmpty(value) ||
|
||||
isUndefined(regexp) ||
|
||||
regexp.test(value);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### form
|
||||
|
||||
@@ -2605,7 +2721,7 @@ See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262
|
||||
|
||||
### Interpolations inside DOM event handlers are now disallowed
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
|
||||
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers
|
||||
means that the interpolated value is a JS string that is evaluated. Storing or generating such
|
||||
strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other
|
||||
Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which
|
||||
|
||||
@@ -221,8 +221,8 @@ it('should clear messages after alert', function() {
|
||||
notify('two');
|
||||
notify('third');
|
||||
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
expect(mock.alert.calls.count()).toEqual(2);
|
||||
expect(mock.alert.calls.mostRecent().args).toEqual(["more\ntwo\nthird"]);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -140,13 +140,13 @@ tests once on Chrome run:
|
||||
grunt test:unit
|
||||
```
|
||||
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox and Safari are pre-configured) use:
|
||||
|
||||
```shell
|
||||
grunt test:unit --browsers=Opera,Firefox
|
||||
grunt test:unit --browsers=Chrome,Firefox
|
||||
```
|
||||
|
||||
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
|
||||
Note there should be _no spaces between browsers_. `Chrome, Firefox` is INVALID.
|
||||
|
||||
During development, however, it's more productive to continuously run unit tests every time the source or test files
|
||||
change. To execute tests in this mode run:
|
||||
|
||||
+93
-43
@@ -22,39 +22,89 @@ So it's definitely not a plugin or some other native browser extension.
|
||||
|
||||
### What is the AngularJS versioning strategy?
|
||||
|
||||
In Angular 1 we do not allow intentional breaking changes to appear in versions where only the "patch"
|
||||
In AngularJS we do not allow intentional breaking changes to appear in versions where only the "patch"
|
||||
number changes. For example between 1.3.12 and 1.3.13 there can be no breaking changes. We do allow
|
||||
breaking changes happen between "minor" number changes. For example between 1.3.15 and 1.4.0 there
|
||||
will be a number of breaking changes. We also allow breaking changes between beta releases of Angular.
|
||||
are a number of breaking changes. That means AngularJS does not follow
|
||||
[semantic versioning (semver)](http://semver.org/) where breaking changes are only
|
||||
allowed when the "major" version changes.
|
||||
|
||||
We also allow breaking changes between beta releases of AngularJS.
|
||||
For example between 1.4.0-beta.4 and 1.4.0-beta.5 there may be breaking changes. We try hard to minimize
|
||||
these kinds of change only to those where there is a strong use case such as a strongly requested feature
|
||||
improvement, a considerable simplification of the code or a measurable performance improvement.
|
||||
improvement, a considerable simplification of the code, a measurable performance improvement, or a better
|
||||
developer experience (especially with regard to upgrading to Angular).
|
||||
|
||||
When adding new code to branches of Angular, have a very stringent commit policy:
|
||||
When we are making a release we generate updates to the changelog directly from the commits. This
|
||||
generated update contains a highlighted section that contains all the breaking changes that have been
|
||||
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
|
||||
changes and so can application developers when they are deciding whether to update to a new version of
|
||||
Angular.
|
||||
|
||||
- Every commit must contain tests and documentation updates alongside the code changes and that all the
|
||||
tests must pass;
|
||||
Features with non-breaking changes can also appear in the "patch" version, e.g. in version 1.6.3 there might
|
||||
be a feature that is not available in 1.6.2.
|
||||
|
||||
Finally, deprecation of features might also appear in "minor" version updates. That means the features
|
||||
will still work in this version, but sometimes must be activated specifically.
|
||||
|
||||
#### When are deprecated features removed from the library?
|
||||
|
||||
Most of the time we remove a deprecated feature in a next minor version bump. For example, the
|
||||
`preAssignBindingsEnabled` `$compileProvider` method was defined in AngularJS `1.5.10`, deprecated in `1.6` and
|
||||
will be removed in `1.7`.
|
||||
|
||||
In case of jqLite we apply a different strategy - we deprecate features that have an equivalent in jQuery that
|
||||
is also deprecated but we only remove the feature once it's removed from jQuery to improve compatibility between
|
||||
jqLite and jQuery. One such example is the `bind` method, deprecated in favor of `on` but unlikely to be removed
|
||||
from jqLite any time soon.
|
||||
|
||||
#### What is the version compatibility between AngularJS main and optional modules?
|
||||
|
||||
AngularJS code is separated into a main module ("angular"), and a few different optional modules
|
||||
("angular-animate", "angular-route" etc) that are dependant on the main module.
|
||||
When a new AngularJS version is released, all modules are updated to the new version.
|
||||
This means that the main module and the optional modules must always have the exact same version,
|
||||
down to the patch number, otherwise your application might break.
|
||||
|
||||
Therefore you must always explicitly lock down your dependencies, for example in the package.json,
|
||||
the following means that "angular" and "angular-animate" are always updated to the same version:
|
||||
|
||||
```
|
||||
{
|
||||
"angular": "~1.6.0",
|
||||
"angular-animate": "~1.6.0"
|
||||
}
|
||||
```
|
||||
|
||||
If you define exact versions, make sure core and optional modules are the same:
|
||||
|
||||
```
|
||||
{
|
||||
"angular": "1.6.3",
|
||||
"angular-animate": "1.6.3"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### How does AngularJS ensure code quality and guard against regressions?
|
||||
|
||||
When adding new code to AngularJS, we have a very stringent commit policy:
|
||||
|
||||
- Every commit must pass all existing tests, contain tests for code changes, and update the documentation
|
||||
- Commit messages must be written in a specific manner that allows us to parse them and extract the changes
|
||||
for release notes.
|
||||
for release notes ([see the contributing guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md))
|
||||
|
||||
The Angular code base has a very large set of unit tests (over 4000) and end to end tests, which are pretty
|
||||
comprehensive. This means that a breaking change will require one or more tests to be changed to allow the
|
||||
The AngularJS code base has a very large set of unit tests and end-to-end tests. This means that a breaking change will require one or more tests to be changed to allow the
|
||||
tests to pass. So when a commit includes tests that are being removed or modified, this is a flag that the
|
||||
code might include a breaking change. When reviewing the commit we can then decide whether there really is
|
||||
a breaking change and if it is appropriate for the branch to which it is being merged. If so, then we
|
||||
require that the commit message contains an appropriate breaking change message.
|
||||
|
||||
Additionally, when a commit lands in our master repository it is synced to Google where we test it against
|
||||
over 2000 applications using the test suites of these applications. This allows us to catch regressions
|
||||
Additionally, commits are periodically synced to Google where we test it against applications using
|
||||
the test suites of these applications. This allows us to catch regressions
|
||||
quickly before a release. We've had a pretty good experience with this setup. Only bugs that affect features
|
||||
not used at Google or without sufficient test coverage, have a chance of making it through.
|
||||
|
||||
Lastly, when we are making a release we generate updates to the changelog directly from the commits. This
|
||||
generated update contains a highlighted section that contains all the breaking changes that have been
|
||||
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
|
||||
changes and so can application developers when they are deciding whether to update to a new version of
|
||||
Angular.
|
||||
|
||||
|
||||
### Is AngularJS a templating system?
|
||||
|
||||
@@ -86,11 +136,11 @@ Yes. See instructions in {@link downloading}.
|
||||
|
||||
|
||||
|
||||
### What browsers does Angular work with?
|
||||
### What browsers does AngularJS work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: the latest versions of Chrome,
|
||||
Firefox, Safari, and Safari for iOs, as well as Internet Explorer versions 9-11. See {@link guide/ie
|
||||
Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
|
||||
Firefox, Safari, and Safari for iOS, as well as Internet Explorer versions 9-11. See
|
||||
{@link guide/ie Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
|
||||
|
||||
If a browser is untested, it doesn't mean it won't work; for example, older Android (2.3.x)
|
||||
is supported in the sense that we avoid the dot notation for reserved words as property names,
|
||||
@@ -99,7 +149,7 @@ a large part of their codebase with a browser we test, such as Opera > version 1
|
||||
(uses the Blink engine), or the various Firefox derivatives.
|
||||
|
||||
|
||||
### What's Angular's performance like?
|
||||
### What's AngularJS's performance like?
|
||||
|
||||
The startup time heavily depends on your network connection, state of the cache, browser used and
|
||||
available hardware, but typically we measure bootstrap time in tens or hundreds of milliseconds.
|
||||
@@ -114,40 +164,40 @@ illustration, we typically build snappy apps with hundreds or thousands of activ
|
||||
The size of the file is ~50KB compressed and minified.
|
||||
|
||||
|
||||
### Can I use the open-source Closure Library with Angular?
|
||||
### Can I use the open-source Closure Library with AngularJS?
|
||||
|
||||
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
|
||||
in Angular.
|
||||
in AngularJS.
|
||||
|
||||
|
||||
### Does Angular use the jQuery library?
|
||||
### Does AngularJS use the jQuery library?
|
||||
|
||||
Yes, Angular can use [jQuery](http://jquery.com/) if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
|
||||
Yes, AngularJS can use [jQuery](http://jquery.com/) if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, AngularJS falls back
|
||||
to its own implementation of the subset of jQuery that we call {@link angular.element jQLite}.
|
||||
|
||||
Angular 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with Angular
|
||||
AngularJS 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with AngularJS
|
||||
but we don't guarantee that.
|
||||
|
||||
|
||||
### What is testability like in Angular?
|
||||
### What is testability like in AngularJS?
|
||||
|
||||
Very testable and designed this way from the ground up. It has an integrated dependency injection
|
||||
framework, provides mocks for many heavy dependencies (server-side communication). See
|
||||
{@link ngMock} for details.
|
||||
|
||||
|
||||
### How can I learn more about Angular?
|
||||
### How can I learn more about AngularJS?
|
||||
|
||||
Watch the July 17, 2012 talk
|
||||
"[AngularJS Intro + Dependency Injection](http://www.youtube.com/watch?v=1CpiB3Wk25U)".
|
||||
|
||||
|
||||
### How is Angular licensed?
|
||||
### How is AngularJS licensed?
|
||||
|
||||
The [MIT License](https://github.com/angular/angular.js/blob/master/LICENSE).
|
||||
|
||||
### Can I download and use the Angular logo artwork?
|
||||
### Can I download and use the AngularJS logo artwork?
|
||||
|
||||
Yes! You can find design files in our github repository, under "[angular.js/images/logo](https://github.com/angular/angular.js/tree/master/images/logo)"
|
||||
The logo design is licensed under a "[Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)". If you have some other use in mind, contact us.
|
||||
@@ -168,7 +218,7 @@ For a smaller order, or for other countries, we suggest downloading the logo art
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
The Angular support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of Angular fall into.
|
||||
The AngularJS support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of AngularJS fall into.
|
||||
This document aims to point them out before you discover them the hard way.
|
||||
|
||||
### DOM Manipulation
|
||||
@@ -179,13 +229,13 @@ Use built-in directives, or write your own where necessary, to do your DOM manip
|
||||
See below about duplicating functionality.
|
||||
|
||||
If you're struggling to break the habit, consider removing jQuery from your app.
|
||||
Really. Angular has the $http service and powerful directives that make it almost always unnecessary.
|
||||
Angular's bundled jQLite has a handful of the features most commonly used in writing Angular directives, especially binding to events.
|
||||
Really. AngularJS has the $http service and powerful directives that make it almost always unnecessary.
|
||||
AngularJS's bundled jQLite has a handful of the features most commonly used in writing AngularJS directives, especially binding to events.
|
||||
|
||||
### Trying to duplicate functionality that already exists
|
||||
|
||||
There's a good chance that your app isn't the first to require certain functionality.
|
||||
There are a few pieces of Angular that are particularly likely to be reimplemented out of old habits.
|
||||
There are a few pieces of AngularJS that are particularly likely to be reimplemented out of old habits.
|
||||
|
||||
**ng-repeat**
|
||||
|
||||
@@ -198,7 +248,7 @@ Store the data from the server in an array on your `$scope`, and bind it to the
|
||||
**ng-show**
|
||||
|
||||
`ng-show` gets this frequently too.
|
||||
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but Angular has a better way.
|
||||
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but AngularJS has a better way.
|
||||
`ng-show` (and `ng-hide`) conditionally show and hide elements based on boolean expressions.
|
||||
Describe the conditions for showing and hiding an element in terms of `$scope` variables:
|
||||
|
||||
@@ -211,7 +261,7 @@ Note especially the powerful `ng-switch` that should be used instead of several
|
||||
|
||||
`ng-class` is the last of the big three.
|
||||
Conditionally applying classes to elements is another thing commonly done manually using jQuery.
|
||||
Angular, of course, has a better way.
|
||||
AngularJS, of course, has a better way.
|
||||
You can give `ng-class` a whitespace-separated set of class names, and then it's identical to ordinary `class`.
|
||||
That's not very exciting, so there's a second syntax:
|
||||
|
||||
@@ -225,22 +275,22 @@ Note also the handy `ng-class-even` and `ng-class-odd`, and the related though s
|
||||
|
||||
### `$watch` and `$apply`
|
||||
|
||||
Angular's two-way data binding is the root of all awesome in Angular.
|
||||
AngularJS's two-way data binding is the root of all awesome in AngularJS.
|
||||
However, it's not magic, and there are some situations where you need to give it a nudge in the right direction.
|
||||
|
||||
When you bind a value to an element in Angular using `ng-model`, `ng-repeat`, etc., Angular creates a `$watch` on that value.
|
||||
When you bind a value to an element in AngularJS using `ng-model`, `ng-repeat`, etc., AngularJS creates a `$watch` on that value.
|
||||
Then whenever a value on a scope changes, all `$watch`es observing that element are executed, and everything updates.
|
||||
|
||||
Sometimes, usually when you're writing a custom directive, you will have to define your own `$watch` on a scope value to make the directive react to changes.
|
||||
|
||||
On the flip side, sometimes you change a scope value in some code, but the app doesn't react to it.
|
||||
Angular checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, Angular will check for changes and react.
|
||||
However, some code is outside of Angular and you'll have to call `scope.$apply()` yourself to trigger the update.
|
||||
AngularJS checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, AngularJS will check for changes and react.
|
||||
However, some code is outside of AngularJS and you'll have to call `scope.$apply()` yourself to trigger the update.
|
||||
This is most commonly seen in event handlers in custom directives.
|
||||
|
||||
### Combining `ng-repeat` with other directives
|
||||
|
||||
`ng-repeat` is extremely useful, one of the most powerful directives in Angular.
|
||||
`ng-repeat` is extremely useful, one of the most powerful directives in AngularJS.
|
||||
However the transformation it applies to the DOM is substantial.
|
||||
Therefore applying other directives (such as `ng-show`, `ng-controller` and others) to the same element as `ng-repeat` generally leads to problems.
|
||||
|
||||
@@ -249,7 +299,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
|
||||
|
||||
### `$rootScope` exists, but it can be used for evil
|
||||
|
||||
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Scopes in AngularJS form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
|
||||
|
||||
Occasionally there are pieces of data that you want to make global to the whole app.
|
||||
|
||||
@@ -32,7 +32,7 @@ tutorials.
|
||||
## 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)
|
||||
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F®ion=follow_link&screen_name=angular&source=followbutton&variant=2.0)
|
||||
* Add us to your circles on [Google+](https://plus.google.com/110323587230527980117/posts)
|
||||
|
||||
## Read more
|
||||
|
||||
@@ -41,7 +41,7 @@ maintain. As we add more and more features, our files will get bigger and bigger
|
||||
difficult to navigate and find the code we are looking for.
|
||||
|
||||
Instead we should put each feature/entity in its own file. Each stand-alone controller will be
|
||||
defined in its own file, each component will be defined in each own file, etc.
|
||||
defined in its own file, each component will be defined in its own file, etc.
|
||||
|
||||
Luckily, we don't need to change anything with respect to that guideline in our code, since we have
|
||||
already defined our `phoneList` component in its own `phone-list.component.js` file. Good job!
|
||||
@@ -54,7 +54,7 @@ We will keep this in mind though, as we add more features.
|
||||
So, now that we learned we should put everything in its own file, our `app/` directory will soon be
|
||||
full with dozens of files and specs (remember we keep our unit test files next to the corresponding
|
||||
source code files). What's more important, logically related files will not be grouped together; it
|
||||
will be really difficult of locate all files related to a specific section of the application and
|
||||
will be really difficult to locate all files related to a specific section of the application and
|
||||
make a change or fix a bug.
|
||||
|
||||
So, what shall we do?
|
||||
|
||||
@@ -194,7 +194,7 @@ angular.module('phonecatApp', [
|
||||
```
|
||||
|
||||
Now, in addition to the core services and directives, we can also configure the `$route` service
|
||||
(using it's provider) for our application. In order to be able to quickly locate the configuration
|
||||
(using its provider) for our application. In order to be able to quickly locate the configuration
|
||||
code, we put it into a separate file and used the `.config` suffix.
|
||||
|
||||
<br />
|
||||
@@ -402,7 +402,7 @@ You can now rerun `npm run protractor` to see the tests run (and hopefully pass)
|
||||
|
||||
<div></div>
|
||||
|
||||
* Try to add a `{{$ctrl.phoneId}` binding in the template string for the phone details view:
|
||||
* Try to add a `{{$ctrl.phoneId}}` binding in the template string for the phone details view:
|
||||
|
||||
```js
|
||||
when('/phones/:phoneId', {
|
||||
|
||||
@@ -173,7 +173,10 @@ angular.module('phoneList', ['core.phone']);
|
||||
**`app/phone-detail/phone-detail.module.js`:**
|
||||
|
||||
```js
|
||||
angular.module('phoneDetail', ['core.phone']);
|
||||
angular.module('phoneDetail', [
|
||||
'ngRoute',
|
||||
'core.phone'
|
||||
]);
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
@@ -46,14 +46,14 @@ Since we are using [Bower][bower] to install client-side dependencies, this step
|
||||
"angular-resource": "1.5.x",
|
||||
"angular-route": "1.5.x",
|
||||
"bootstrap": "3.3.x",
|
||||
"jquery": "2.2.x"
|
||||
"jquery": "3.2.x"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `"angular-animate": "1.5.x"` tells bower to install a version of the angular-animate module that
|
||||
is compatible with version 1.5.x of Angular.
|
||||
* `"jquery": "2.2.x"` tells bower to install the latest patch release of the 2.2 version of jQuery.
|
||||
* `"jquery": "3.2.x"` tells bower to install the latest patch release of the 3.2 version of jQuery.
|
||||
Note that this is not an Angular library; it is the standard jQuery library. We can use bower to
|
||||
install a wide range of 3rd party libraries.
|
||||
|
||||
|
||||
+6
-21
@@ -1,10 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var log = require('gulp-util').log;
|
||||
var concat = require('gulp-concat');
|
||||
var eslint = require('gulp-eslint');
|
||||
var bower = require('bower');
|
||||
var Dgeni = require('dgeni');
|
||||
var merge = require('event-stream').merge;
|
||||
var path = require('canonical-path');
|
||||
@@ -18,7 +16,6 @@ var rename = require('gulp-rename');
|
||||
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
|
||||
|
||||
var outputFolder = '../build/docs';
|
||||
var bowerFolder = 'bower_components';
|
||||
|
||||
var src = 'app/src/**/*.js';
|
||||
var ignoredFiles = '!src/angular.bind.js';
|
||||
@@ -57,8 +54,8 @@ var getMergedEslintConfig = function(filepath) {
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
pattern = pattern || '/**/*';
|
||||
sourceFolder = sourceFolder || bowerFolder;
|
||||
packageFile = packageFile || 'bower.json';
|
||||
sourceFolder = sourceFolder || '../node_modules';
|
||||
packageFile = packageFile || 'package.json';
|
||||
var version = require(path.resolve(sourceFolder, component, packageFile)).version;
|
||||
return gulp
|
||||
.src(sourceFolder + '/' + component + pattern)
|
||||
@@ -66,18 +63,6 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
};
|
||||
|
||||
|
||||
gulp.task('bower', function() {
|
||||
var bowerTask = bower.commands.install();
|
||||
bowerTask.on('log', function(result) {
|
||||
log('bower:', result.id, result.data.endpoint.name);
|
||||
});
|
||||
bowerTask.on('error', function(error) {
|
||||
log(error);
|
||||
});
|
||||
return bowerTask;
|
||||
});
|
||||
|
||||
|
||||
gulp.task('build-app', function() {
|
||||
var file = 'docs.js';
|
||||
var minFile = 'docs.min.js';
|
||||
@@ -94,7 +79,7 @@ gulp.task('build-app', function() {
|
||||
});
|
||||
|
||||
|
||||
gulp.task('assets', ['bower'], function() {
|
||||
gulp.task('assets', function() {
|
||||
var JS_EXT = /\.js$/;
|
||||
return merge(
|
||||
gulp.src(['img/**/*']).pipe(gulp.dest(outputFolder + '/img')),
|
||||
@@ -113,15 +98,15 @@ gulp.task('assets', ['bower'], function() {
|
||||
})),
|
||||
copyComponent('bootstrap', '/dist/**/*'),
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr.js', '/*.js'),
|
||||
copyComponent('lunr', '/*.js'),
|
||||
copyComponent('google-code-prettify'),
|
||||
copyComponent('jquery', '/dist/*.js'),
|
||||
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
|
||||
copyComponent('marked', '/**/*.js')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('doc-gen', ['bower'], function() {
|
||||
gulp.task('doc-gen', function() {
|
||||
var dgeni = new Dgeni([require('./config')]);
|
||||
return dgeni.generate().catch(function() {
|
||||
process.exit(1);
|
||||
|
||||
+9
-4
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "^1.5.8",
|
||||
"branchVersion": "^1.6.0",
|
||||
"branchPattern": "1.6.*",
|
||||
"distTag": "latest",
|
||||
"repository": {
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"yarn": "^0.17.9",
|
||||
"yarn": ">=0.21.3",
|
||||
"grunt": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -22,19 +22,21 @@
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
"benchmark": "1.x.x",
|
||||
"bootstrap": "3.1.1",
|
||||
"bower": "~1.3.9",
|
||||
"browserstacktunnel-wrapper": "^1.4.2",
|
||||
"canonical-path": "0.0.2",
|
||||
"changez": "^2.1.1",
|
||||
"changez-angular": "^2.1.0",
|
||||
"changez-angular": "^2.1.3",
|
||||
"cheerio": "^0.17.0",
|
||||
"commitizen": "^2.3.0",
|
||||
"cross-spawn": "^4.0.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.16.0",
|
||||
"dgeni-packages": "^0.16.4",
|
||||
"event-stream": "~3.1.0",
|
||||
"glob": "^6.0.1",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-bump": "^0.8.0",
|
||||
"grunt-cli": "^1.2.0",
|
||||
@@ -57,6 +59,7 @@
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "^2.2.0",
|
||||
"jquery": "^3.2.1",
|
||||
"karma": "^1.1.2",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
@@ -69,9 +72,11 @@
|
||||
"load-grunt-tasks": "^3.5.0",
|
||||
"lodash": "~2.4.1",
|
||||
"log4js": "^0.6.27",
|
||||
"lunr": "^0.7.2",
|
||||
"marked": "~0.3.0",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"npm-run": "^4.1.0",
|
||||
"open-sans-fontface": "^1.4.0",
|
||||
"promises-aplus-tests": "~2.1.0",
|
||||
"protractor": "^4.0.10",
|
||||
"q": "~1.0.0",
|
||||
|
||||
@@ -12,7 +12,7 @@ set -xe
|
||||
# This is the default set of browsers to use on the CI server unless overridden via env variable
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
|
||||
BROWSERS="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
|
||||
fi
|
||||
|
||||
# CLEAN #
|
||||
|
||||
@@ -8,7 +8,7 @@ nvm install
|
||||
|
||||
# clean out and install yarn
|
||||
rm -rf ~/.yarn
|
||||
curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.21.3
|
||||
export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
# Ensure that we have the local dependencies installed
|
||||
|
||||
+5
-1
@@ -15,6 +15,9 @@
|
||||
"splice": false,
|
||||
"push": false,
|
||||
"toString": false,
|
||||
"minErrConfig": false,
|
||||
"errorHandlingConfig": false,
|
||||
"isValidObjectMaxDepth": false,
|
||||
"ngMinErr": false,
|
||||
"_angular": false,
|
||||
"angularModule": false,
|
||||
@@ -66,6 +69,7 @@
|
||||
"arrayRemove": false,
|
||||
"copy": false,
|
||||
"shallowCopy": false,
|
||||
"simpleCompare": false,
|
||||
"equals": false,
|
||||
"csp": false,
|
||||
"concat": false,
|
||||
@@ -150,7 +154,7 @@
|
||||
|
||||
/* apis.js */
|
||||
"hashKey": false,
|
||||
"HashMap": false,
|
||||
"NgMap": false,
|
||||
|
||||
/* urlUtils.js */
|
||||
"urlResolve": false,
|
||||
|
||||
+109
-31
@@ -10,6 +10,9 @@
|
||||
splice,
|
||||
push,
|
||||
toString,
|
||||
minErrConfig,
|
||||
errorHandlingConfig,
|
||||
isValidObjectMaxDepth,
|
||||
ngMinErr,
|
||||
angularModule,
|
||||
uid,
|
||||
@@ -59,6 +62,7 @@
|
||||
includes,
|
||||
arrayRemove,
|
||||
copy,
|
||||
simpleCompare,
|
||||
equals,
|
||||
csp,
|
||||
jq,
|
||||
@@ -125,6 +129,50 @@ var VALIDITY_STATE_PROPERTY = 'validity';
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
var minErrConfig = {
|
||||
objectMaxDepth: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.errorHandlingConfig
|
||||
* @module ng
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Configure several aspects of error handling in AngularJS if used as a setter or return the
|
||||
* current configuration if used as a getter. The following options are supported:
|
||||
*
|
||||
* - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
|
||||
*
|
||||
* Omitted or undefined options will leave the corresponding configuration values unchanged.
|
||||
*
|
||||
* @param {Object=} config - The configuration object. May only contain the options that need to be
|
||||
* updated. Supported keys:
|
||||
*
|
||||
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
|
||||
* non-positive or non-numeric value, removes the max depth limit.
|
||||
* Default: 5
|
||||
*/
|
||||
function errorHandlingConfig(config) {
|
||||
if (isObject(config)) {
|
||||
if (isDefined(config.objectMaxDepth)) {
|
||||
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
|
||||
}
|
||||
} else {
|
||||
return minErrConfig;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Number} maxDepth
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isValidObjectMaxDepth(maxDepth) {
|
||||
return isNumber(maxDepth) && maxDepth > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.lowercase
|
||||
@@ -847,9 +895,10 @@ function arrayRemove(array, value) {
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function copy(source, destination) {
|
||||
function copy(source, destination, maxDepth) {
|
||||
var stackSource = [];
|
||||
var stackDest = [];
|
||||
maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
|
||||
|
||||
if (destination) {
|
||||
if (isTypedArray(destination) || isArrayBuffer(destination)) {
|
||||
@@ -872,35 +921,39 @@ function copy(source, destination) {
|
||||
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
return copyRecurse(source, destination);
|
||||
return copyRecurse(source, destination, maxDepth);
|
||||
}
|
||||
|
||||
return copyElement(source);
|
||||
return copyElement(source, maxDepth);
|
||||
|
||||
function copyRecurse(source, destination) {
|
||||
function copyRecurse(source, destination, maxDepth) {
|
||||
maxDepth--;
|
||||
if (maxDepth < 0) {
|
||||
return '...';
|
||||
}
|
||||
var h = destination.$$hashKey;
|
||||
var key;
|
||||
if (isArray(source)) {
|
||||
for (var i = 0, ii = source.length; i < ii; i++) {
|
||||
destination.push(copyElement(source[i]));
|
||||
destination.push(copyElement(source[i], maxDepth));
|
||||
}
|
||||
} else if (isBlankObject(source)) {
|
||||
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
|
||||
for (key in source) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
destination[key] = copyElement(source[key], maxDepth);
|
||||
}
|
||||
} else if (source && typeof source.hasOwnProperty === 'function') {
|
||||
// Slow path, which must rely on hasOwnProperty
|
||||
for (key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
destination[key] = copyElement(source[key], maxDepth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Slowest path --- hasOwnProperty can't be called as a method
|
||||
for (key in source) {
|
||||
if (hasOwnProperty.call(source, key)) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
destination[key] = copyElement(source[key], maxDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -908,7 +961,7 @@ function copy(source, destination) {
|
||||
return destination;
|
||||
}
|
||||
|
||||
function copyElement(source) {
|
||||
function copyElement(source, maxDepth) {
|
||||
// Simple values
|
||||
if (!isObject(source)) {
|
||||
return source;
|
||||
@@ -937,7 +990,7 @@ function copy(source, destination) {
|
||||
stackDest.push(destination);
|
||||
|
||||
return needsRecurse
|
||||
? copyRecurse(source, destination)
|
||||
? copyRecurse(source, destination, maxDepth)
|
||||
: destination;
|
||||
}
|
||||
|
||||
@@ -988,6 +1041,10 @@ function copy(source, destination) {
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line no-self-compare
|
||||
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.equals
|
||||
@@ -1068,7 +1125,7 @@ function equals(o1, o2) {
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
return simpleCompare(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
if (!isRegExp(o2)) return false;
|
||||
return o1.toString() === o2.toString();
|
||||
@@ -1479,30 +1536,51 @@ function getNgAttribute(element, ngAttr) {
|
||||
}
|
||||
|
||||
function allowAutoBootstrap(document) {
|
||||
if (!document.currentScript) {
|
||||
var script = document.currentScript;
|
||||
|
||||
if (!script) {
|
||||
// IE does not have `document.currentScript`
|
||||
return true;
|
||||
}
|
||||
var src = document.currentScript.getAttribute('src');
|
||||
var link = document.createElement('a');
|
||||
link.href = src;
|
||||
if (document.location.origin === link.origin) {
|
||||
// Same-origin resources are always allowed, even for non-whitelisted schemes.
|
||||
return true;
|
||||
|
||||
// If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
|
||||
if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
|
||||
return false;
|
||||
}
|
||||
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
|
||||
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
|
||||
// content security policy in web pages and other browser extensions.
|
||||
switch (link.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
case 'ftp:':
|
||||
case 'blob:':
|
||||
case 'file:':
|
||||
case 'data:':
|
||||
|
||||
var attributes = script.attributes;
|
||||
var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
|
||||
|
||||
return srcs.every(function(src) {
|
||||
if (!src) {
|
||||
return true;
|
||||
default:
|
||||
}
|
||||
if (!src.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var link = document.createElement('a');
|
||||
link.href = src.value;
|
||||
|
||||
if (document.location.origin === link.origin) {
|
||||
// Same-origin resources are always allowed, even for non-whitelisted schemes.
|
||||
return true;
|
||||
}
|
||||
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
|
||||
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
|
||||
// content security policy in web pages and other browser extensions.
|
||||
switch (link.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
case 'ftp:':
|
||||
case 'blob:':
|
||||
case 'file:':
|
||||
case 'data:':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cached as it has to run during loading so that document.currentScript is available.
|
||||
@@ -1866,7 +1944,7 @@ function bindJQuery() {
|
||||
extend(jQuery.fn, {
|
||||
scope: JQLitePrototype.scope,
|
||||
isolateScope: JQLitePrototype.isolateScope,
|
||||
controller: JQLitePrototype.controller,
|
||||
controller: /** @type {?} */ (JQLitePrototype).controller,
|
||||
injector: JQLitePrototype.injector,
|
||||
inheritedData: JQLitePrototype.inheritedData
|
||||
});
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
$$ForceReflowProvider,
|
||||
$InterpolateProvider,
|
||||
$IntervalProvider,
|
||||
$$HashMapProvider,
|
||||
$HttpProvider,
|
||||
$HttpParamSerializerProvider,
|
||||
$HttpParamSerializerJQLikeProvider,
|
||||
@@ -79,6 +78,7 @@
|
||||
$jsonpCallbacksProvider,
|
||||
$LocationProvider,
|
||||
$LogProvider,
|
||||
$$MapProvider,
|
||||
$ParseProvider,
|
||||
$RootScopeProvider,
|
||||
$QProvider,
|
||||
@@ -126,6 +126,7 @@ var version = {
|
||||
|
||||
function publishExternalAPI(angular) {
|
||||
extend(angular, {
|
||||
'errorHandlingConfig': errorHandlingConfig,
|
||||
'bootstrap': bootstrap,
|
||||
'copy': copy,
|
||||
'extend': extend,
|
||||
@@ -260,9 +261,10 @@ function publishExternalAPI(angular) {
|
||||
$window: $WindowProvider,
|
||||
$$rAF: $$RAFProvider,
|
||||
$$jqLite: $$jqLiteProvider,
|
||||
$$HashMap: $$HashMapProvider,
|
||||
$$Map: $$MapProvider,
|
||||
$$cookieReader: $$CookieReaderProvider
|
||||
});
|
||||
}
|
||||
]);
|
||||
])
|
||||
.info({ angularVersion: '"NG_VERSION_FULL"' });
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2017 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window) {
|
||||
|
||||
+55
-36
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* Computes a hash of an 'obj'.
|
||||
* Hash of a:
|
||||
@@ -33,49 +32,69 @@ function hashKey(obj, nextUidFn) {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* HashMap which can use objects as keys
|
||||
*/
|
||||
function HashMap(array, isolatedUid) {
|
||||
if (isolatedUid) {
|
||||
var uid = 0;
|
||||
this.nextUid = function() {
|
||||
return ++uid;
|
||||
};
|
||||
}
|
||||
forEach(array, this.put, this);
|
||||
// A minimal ES2015 Map implementation.
|
||||
// Should be bug/feature equivalent to the native implementations of supported browsers
|
||||
// (for the features required in Angular).
|
||||
// See https://kangax.github.io/compat-table/es6/#test-Map
|
||||
var nanKey = Object.create(null);
|
||||
function NgMapShim() {
|
||||
this._keys = [];
|
||||
this._values = [];
|
||||
this._lastKey = NaN;
|
||||
this._lastIndex = -1;
|
||||
}
|
||||
HashMap.prototype = {
|
||||
/**
|
||||
* Store key value pair
|
||||
* @param key key to store can be any type
|
||||
* @param value value to store can be any type
|
||||
*/
|
||||
put: function(key, value) {
|
||||
this[hashKey(key, this.nextUid)] = value;
|
||||
NgMapShim.prototype = {
|
||||
_idx: function(key) {
|
||||
if (key === this._lastKey) {
|
||||
return this._lastIndex;
|
||||
}
|
||||
this._lastKey = key;
|
||||
this._lastIndex = this._keys.indexOf(key);
|
||||
return this._lastIndex;
|
||||
},
|
||||
_transformKey: function(key) {
|
||||
return isNumberNaN(key) ? nanKey : key;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @returns {Object} the value for the key
|
||||
*/
|
||||
get: function(key) {
|
||||
return this[hashKey(key, this.nextUid)];
|
||||
key = this._transformKey(key);
|
||||
var idx = this._idx(key);
|
||||
if (idx !== -1) {
|
||||
return this._values[idx];
|
||||
}
|
||||
},
|
||||
set: function(key, value) {
|
||||
key = this._transformKey(key);
|
||||
var idx = this._idx(key);
|
||||
if (idx === -1) {
|
||||
idx = this._lastIndex = this._keys.length;
|
||||
}
|
||||
this._keys[idx] = key;
|
||||
this._values[idx] = value;
|
||||
|
||||
/**
|
||||
* Remove the key/value pair
|
||||
* @param key
|
||||
*/
|
||||
remove: function(key) {
|
||||
var value = this[key = hashKey(key, this.nextUid)];
|
||||
delete this[key];
|
||||
return value;
|
||||
// Support: IE11
|
||||
// Do not `return this` to simulate the partial IE11 implementation
|
||||
},
|
||||
delete: function(key) {
|
||||
key = this._transformKey(key);
|
||||
var idx = this._idx(key);
|
||||
if (idx === -1) {
|
||||
return false;
|
||||
}
|
||||
this._keys.splice(idx, 1);
|
||||
this._values.splice(idx, 1);
|
||||
this._lastKey = NaN;
|
||||
this._lastIndex = -1;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var $$HashMapProvider = [/** @this */function() {
|
||||
// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
|
||||
// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
|
||||
// implementations get more stable, we can reconsider switching to `window.Map` (when available).
|
||||
var NgMap = NgMapShim;
|
||||
|
||||
var $$MapProvider = [/** @this */function() {
|
||||
this.$get = [function() {
|
||||
return HashMap;
|
||||
return NgMap;
|
||||
}];
|
||||
}];
|
||||
|
||||
+27
-7
@@ -71,11 +71,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
var $injectorMinErr = minErr('$injector');
|
||||
|
||||
function stringifyFn(fn) {
|
||||
// Support: Chrome 50-51 only
|
||||
// Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
|
||||
// (See https://github.com/angular/angular.js/issues/14487.)
|
||||
// TODO (gkalpak): Remove workaround when Chrome v52 is released
|
||||
return Function.prototype.toString.call(fn) + ' ';
|
||||
return Function.prototype.toString.call(fn);
|
||||
}
|
||||
|
||||
function extractArgs(fn) {
|
||||
@@ -184,6 +180,28 @@ function annotate(fn, strictDi, name) {
|
||||
* As an array of injection names, where the last item in the array is the function to call.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $injector#modules
|
||||
* @type {Object}
|
||||
* @description
|
||||
* A hash containing all the modules that have been loaded into the
|
||||
* $injector.
|
||||
*
|
||||
* You can use this property to find out information about a module via the
|
||||
* {@link angular.Module#info `myModule.info(...)`} method.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* var info = $injector.modules['ngAnimate'].info();
|
||||
* ```
|
||||
*
|
||||
* **Do not use this property to attempt to modify the modules after the application
|
||||
* has been bootstrapped.**
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $injector#get
|
||||
@@ -649,7 +667,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
var INSTANTIATING = {},
|
||||
providerSuffix = 'Provider',
|
||||
path = [],
|
||||
loadedModules = new HashMap([], true),
|
||||
loadedModules = new NgMap(),
|
||||
providerCache = {
|
||||
$provide: {
|
||||
provider: supportObject(provider),
|
||||
@@ -677,6 +695,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
instanceInjector = protoInstanceInjector;
|
||||
|
||||
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
|
||||
instanceInjector.modules = providerInjector.modules = createMap();
|
||||
var runBlocks = loadModules(modulesToLoad);
|
||||
instanceInjector = protoInstanceInjector.get('$injector');
|
||||
instanceInjector.strictDi = strictDi;
|
||||
@@ -757,7 +776,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
var runBlocks = [], moduleFn;
|
||||
forEach(modulesToLoad, function(module) {
|
||||
if (loadedModules.get(module)) return;
|
||||
loadedModules.put(module, true);
|
||||
loadedModules.set(module, true);
|
||||
|
||||
function runInvokeQueue(queue) {
|
||||
var i, ii;
|
||||
@@ -772,6 +791,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
try {
|
||||
if (isString(module)) {
|
||||
moduleFn = angularModule(module);
|
||||
instanceInjector.modules[module] = moduleFn;
|
||||
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
|
||||
runInvokeQueue(moduleFn._invokeQueue);
|
||||
runInvokeQueue(moduleFn._configBlocks);
|
||||
|
||||
+15
-17
@@ -201,12 +201,6 @@ function jqLiteHasData(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
@@ -309,13 +303,10 @@ function jqLiteClone(element) {
|
||||
}
|
||||
|
||||
function jqLiteDealoc(element, onlyDescendants) {
|
||||
if (!onlyDescendants) jqLiteRemoveData(element);
|
||||
if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]);
|
||||
|
||||
if (element.querySelectorAll) {
|
||||
var descendants = element.querySelectorAll('*');
|
||||
for (var i = 0, l = descendants.length; i < l; i++) {
|
||||
jqLiteRemoveData(descendants[i]);
|
||||
}
|
||||
jqLite.cleanData(element.querySelectorAll('*'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +604,11 @@ forEach({
|
||||
data: jqLiteData,
|
||||
removeData: jqLiteRemoveData,
|
||||
hasData: jqLiteHasData,
|
||||
cleanData: jqLiteCleanData
|
||||
cleanData: function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
}, function(fn, name) {
|
||||
JQLite[name] = fn;
|
||||
});
|
||||
@@ -979,12 +974,15 @@ forEach({
|
||||
|
||||
after: function(element, newElement) {
|
||||
var index = element, parent = element.parentNode;
|
||||
newElement = new JQLite(newElement);
|
||||
|
||||
for (var i = 0, ii = newElement.length; i < ii; i++) {
|
||||
var node = newElement[i];
|
||||
parent.insertBefore(node, index.nextSibling);
|
||||
index = node;
|
||||
if (parent) {
|
||||
newElement = new JQLite(newElement);
|
||||
|
||||
for (var i = 0, ii = newElement.length; i < ii; i++) {
|
||||
var node = newElement[i];
|
||||
parent.insertBefore(node, index.nextSibling);
|
||||
index = node;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -79,6 +79,9 @@ function setupModuleLoader(window) {
|
||||
* @returns {angular.Module} new module with the {@link angular.Module} api.
|
||||
*/
|
||||
return function module(name, requires, configFn) {
|
||||
|
||||
var info = {};
|
||||
|
||||
var assertNotHasOwnProperty = function(name, context) {
|
||||
if (name === 'hasOwnProperty') {
|
||||
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
|
||||
@@ -114,6 +117,45 @@ function setupModuleLoader(window) {
|
||||
_configBlocks: configBlocks,
|
||||
_runBlocks: runBlocks,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#info
|
||||
* @module ng
|
||||
*
|
||||
* @param {Object=} info Information about the module
|
||||
* @returns {Object|Module} The current info object for this module if called as a getter,
|
||||
* or `this` if called as a setter.
|
||||
*
|
||||
* @description
|
||||
* Read and write custom information about this module.
|
||||
* For example you could put the version of the module in here.
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myModule', []).info({ version: '1.0.0' });
|
||||
* ```
|
||||
*
|
||||
* The version could then be read back out by accessing the module elsewhere:
|
||||
*
|
||||
* ```
|
||||
* var version = angular.module('myModule').info().version;
|
||||
* ```
|
||||
*
|
||||
* You can also retrieve this information during runtime via the
|
||||
* {@link $injector#modules `$injector.modules`} property:
|
||||
*
|
||||
* ```js
|
||||
* var version = $injector.modules['myModule'].info().version;
|
||||
* ```
|
||||
*/
|
||||
info: function(value) {
|
||||
if (isDefined(value)) {
|
||||
if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
|
||||
info = value;
|
||||
return this;
|
||||
}
|
||||
return info;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2017 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'use strict';
|
||||
(function() {
|
||||
function isFunction(value) {return typeof value === 'function';};
|
||||
function isFunction(value) {return typeof value === 'function';}
|
||||
function isDefined(value) {return typeof value !== 'undefined';}
|
||||
function isObject(value) {return value !== null && typeof value === 'object';}
|
||||
|
||||
|
||||
+10
-12
@@ -33,20 +33,19 @@
|
||||
function minErr(module, ErrorConstructor) {
|
||||
ErrorConstructor = ErrorConstructor || Error;
|
||||
return function() {
|
||||
var SKIP_INDEXES = 2;
|
||||
|
||||
var templateArgs = arguments,
|
||||
code = templateArgs[0],
|
||||
var code = arguments[0],
|
||||
template = arguments[1],
|
||||
message = '[' + (module ? module + ':' : '') + code + '] ',
|
||||
template = templateArgs[1],
|
||||
templateArgs = sliceArgs(arguments, 2).map(function(arg) {
|
||||
return toDebugString(arg, minErrConfig.objectMaxDepth);
|
||||
}),
|
||||
paramPrefix, i;
|
||||
|
||||
message += template.replace(/\{\d+\}/g, function(match) {
|
||||
var index = +match.slice(1, -1),
|
||||
shiftedIndex = index + SKIP_INDEXES;
|
||||
var index = +match.slice(1, -1);
|
||||
|
||||
if (shiftedIndex < templateArgs.length) {
|
||||
return toDebugString(templateArgs[shiftedIndex]);
|
||||
if (index < templateArgs.length) {
|
||||
return templateArgs[index];
|
||||
}
|
||||
|
||||
return match;
|
||||
@@ -55,9 +54,8 @@ function minErr(module, ErrorConstructor) {
|
||||
message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
|
||||
(module ? module + '/' : '') + code;
|
||||
|
||||
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
|
||||
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
|
||||
encodeURIComponent(toDebugString(templateArgs[i]));
|
||||
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
|
||||
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
|
||||
}
|
||||
|
||||
return new ErrorConstructor(message);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2017 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {
|
||||
|
||||
+11
-9
@@ -60,7 +60,7 @@ var $$CoreAnimateJsProvider = /** @this */ function() {
|
||||
// this is prefixed with Core since it conflicts with
|
||||
// the animateQueueProvider defined in ngAnimate/animateQueue.js
|
||||
var $$CoreAnimateQueueProvider = /** @this */ function() {
|
||||
var postDigestQueue = new HashMap();
|
||||
var postDigestQueue = new NgMap();
|
||||
var postDigestElements = [];
|
||||
|
||||
this.$get = ['$$AnimateRunner', '$rootScope',
|
||||
@@ -139,7 +139,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
|
||||
jqLiteRemoveClass(elm, toRemove);
|
||||
}
|
||||
});
|
||||
postDigestQueue.remove(element);
|
||||
postDigestQueue.delete(element);
|
||||
}
|
||||
});
|
||||
postDigestElements.length = 0;
|
||||
@@ -154,7 +154,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
|
||||
|
||||
if (classesAdded || classesRemoved) {
|
||||
|
||||
postDigestQueue.put(element, data);
|
||||
postDigestQueue.set(element, data);
|
||||
postDigestElements.push(element);
|
||||
|
||||
if (postDigestElements.length === 1) {
|
||||
@@ -179,6 +179,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
|
||||
*/
|
||||
var $AnimateProvider = ['$provide', /** @this */ function($provide) {
|
||||
var provider = this;
|
||||
var classNameFilter = null;
|
||||
|
||||
this.$$registeredAnimations = Object.create(null);
|
||||
|
||||
@@ -247,15 +248,16 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
|
||||
*/
|
||||
this.classNameFilter = function(expression) {
|
||||
if (arguments.length === 1) {
|
||||
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
|
||||
if (this.$$classNameFilter) {
|
||||
var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)');
|
||||
if (reservedRegex.test(this.$$classNameFilter.toString())) {
|
||||
throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
|
||||
classNameFilter = (expression instanceof RegExp) ? expression : null;
|
||||
if (classNameFilter) {
|
||||
var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
|
||||
if (reservedRegex.test(classNameFilter.toString())) {
|
||||
classNameFilter = null;
|
||||
throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.$$classNameFilter;
|
||||
return classNameFilter;
|
||||
};
|
||||
|
||||
this.$get = ['$$animateQueue', function($$animateQueue) {
|
||||
|
||||
+11
-10
@@ -96,7 +96,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
};
|
||||
|
||||
cacheState();
|
||||
lastHistoryState = cachedState;
|
||||
|
||||
/**
|
||||
* @name $browser#url
|
||||
@@ -150,8 +149,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
if ($sniffer.history && (!sameBase || !sameState)) {
|
||||
history[replace ? 'replaceState' : 'pushState'](state, '', url);
|
||||
cacheState();
|
||||
// Do the assignment again so that those two variables are referentially identical.
|
||||
lastHistoryState = cachedState;
|
||||
} else {
|
||||
if (!sameBase) {
|
||||
pendingLocation = url;
|
||||
@@ -200,8 +197,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
|
||||
function cacheStateAndFireUrlChange() {
|
||||
pendingLocation = null;
|
||||
cacheState();
|
||||
fireUrlChange();
|
||||
fireStateOrUrlChange();
|
||||
}
|
||||
|
||||
// This variable should be used *only* inside the cacheState function.
|
||||
@@ -215,11 +211,16 @@ function Browser(window, document, $log, $sniffer) {
|
||||
if (equals(cachedState, lastCachedState)) {
|
||||
cachedState = lastCachedState;
|
||||
}
|
||||
|
||||
lastCachedState = cachedState;
|
||||
lastHistoryState = cachedState;
|
||||
}
|
||||
|
||||
function fireUrlChange() {
|
||||
if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
|
||||
function fireStateOrUrlChange() {
|
||||
var prevLastHistoryState = lastHistoryState;
|
||||
cacheState();
|
||||
|
||||
if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -254,8 +255,8 @@ function Browser(window, document, $log, $sniffer) {
|
||||
self.onUrlChange = function(callback) {
|
||||
// TODO(vojta): refactor to use node's syntax for events
|
||||
if (!urlChangeInit) {
|
||||
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
|
||||
// don't fire popstate when user change the address bar and don't fire hashchange when url
|
||||
// We listen on both (hashchange/popstate) when available, as some browsers don't
|
||||
// fire popstate when user changes the address bar and don't fire hashchange when url
|
||||
// changed by push/replaceState
|
||||
|
||||
// html5 history api - popstate event
|
||||
@@ -285,7 +286,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* Needs to be exported to be able to check for changes that have been done in sync,
|
||||
* as hashchange/popstate events fire in async.
|
||||
*/
|
||||
self.$$checkUrlChange = fireUrlChange;
|
||||
self.$$checkUrlChange = fireStateOrUrlChange;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Misc API
|
||||
|
||||
+23
-16
@@ -128,7 +128,8 @@
|
||||
* * `$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.
|
||||
* component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
|
||||
* also be called when your bindings are initialized.
|
||||
* * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
|
||||
* changes. Any actions that you wish to take in response to the changes that you detect must be
|
||||
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
|
||||
@@ -276,10 +277,12 @@
|
||||
* the directive's element. If multiple directives on the same element request a new scope,
|
||||
* only one new scope is created.
|
||||
*
|
||||
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
|
||||
* 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
|
||||
* scope. This is useful when creating reusable components, which should not accidentally read or modify
|
||||
* data in the parent scope.
|
||||
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
|
||||
* The 'isolate' scope differs from normal scope in that it does not prototypically
|
||||
* inherit from its parent scope. This is useful when creating reusable components, which should not
|
||||
* accidentally read or modify data in the parent scope. Note that an isolate scope
|
||||
* directive without a `template` or `templateUrl` will not apply the isolate scope
|
||||
* to its children elements.
|
||||
*
|
||||
* The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
|
||||
* directive's element. These local properties are useful for aliasing values for templates. The keys in
|
||||
@@ -372,9 +375,9 @@
|
||||
* initialized.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Deprecation warning:** although bindings for non-ES6 class controllers are currently
|
||||
* bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
|
||||
* code that relies upon bindings inside a `$onInit` method on the controller, instead.
|
||||
* **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class
|
||||
* controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please
|
||||
* place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead.
|
||||
* </div>
|
||||
*
|
||||
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
|
||||
@@ -983,7 +986,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var bindingCache = createMap();
|
||||
|
||||
function parseIsolateBindings(scope, directiveName, isController) {
|
||||
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
|
||||
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
|
||||
|
||||
var bindings = createMap();
|
||||
|
||||
@@ -1387,7 +1390,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
*
|
||||
* If disabled (false), the compiler calls the constructor first before assigning bindings.
|
||||
*
|
||||
* The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
|
||||
* The default value is false.
|
||||
*
|
||||
* @deprecated
|
||||
* sinceVersion="1.6.0"
|
||||
* removeVersion="1.7.0"
|
||||
*
|
||||
* This method and the option to assign the bindings before calling the controller's constructor
|
||||
* will be removed in v1.7.0.
|
||||
*/
|
||||
var preAssignBindingsEnabled = false;
|
||||
this.preAssignBindingsEnabled = function(enabled) {
|
||||
@@ -3155,7 +3165,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (error instanceof Error) {
|
||||
$exceptionHandler(error);
|
||||
}
|
||||
}).catch(noop);
|
||||
});
|
||||
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
||||
var childBoundTranscludeFn = boundTranscludeFn;
|
||||
@@ -3477,8 +3487,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
// eslint-disable-next-line no-self-compare
|
||||
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
|
||||
compare = simpleCompare;
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -3553,9 +3562,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
});
|
||||
|
||||
function recordChanges(key, currentValue, previousValue) {
|
||||
if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
|
||||
// eslint-disable-next-line no-self-compare
|
||||
(currentValue === currentValue || previousValue === previousValue)) {
|
||||
if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) {
|
||||
// If we have not already scheduled the top level onChangesQueue handler then do so now
|
||||
if (!onChangesQueue) {
|
||||
scope.$$postDigest(flushOnChangesQueue);
|
||||
|
||||
@@ -14,6 +14,14 @@ function $$CookieReader($document) {
|
||||
var lastCookies = {};
|
||||
var lastCookieString = '';
|
||||
|
||||
function safeGetCookie(rawDocument) {
|
||||
try {
|
||||
return rawDocument.cookie || '';
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function safeDecodeURIComponent(str) {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
@@ -24,7 +32,7 @@ function $$CookieReader($document) {
|
||||
|
||||
return function() {
|
||||
var cookieArray, cookie, i, index, name;
|
||||
var currentCookieString = rawDocument.cookie || '';
|
||||
var currentCookieString = safeGetCookie(rawDocument);
|
||||
|
||||
if (currentCookieString !== lastCookieString) {
|
||||
lastCookieString = currentCookieString;
|
||||
|
||||
@@ -159,7 +159,8 @@
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* This directive sets the `disabled` attribute on the element if the
|
||||
* This directive sets the `disabled` attribute on the element (typically a form control,
|
||||
* e.g. `input`, `button`, `select` etc.) if the
|
||||
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
|
||||
*
|
||||
* A special directive is necessary because we cannot use interpolation inside the `disabled`
|
||||
|
||||
@@ -1565,15 +1565,27 @@ function isValidForStep(viewValue, stepBase, step) {
|
||||
// and `viewValue` is expected to be a valid stringified number.
|
||||
var value = Number(viewValue);
|
||||
|
||||
var isNonIntegerValue = !isNumberInteger(value);
|
||||
var isNonIntegerStepBase = !isNumberInteger(stepBase);
|
||||
var isNonIntegerStep = !isNumberInteger(step);
|
||||
|
||||
// Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
|
||||
// `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
|
||||
if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) {
|
||||
var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step));
|
||||
if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
|
||||
var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
|
||||
var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
|
||||
var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
|
||||
|
||||
var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
|
||||
var multiplier = Math.pow(10, decimalCount);
|
||||
|
||||
value = value * multiplier;
|
||||
stepBase = stepBase * multiplier;
|
||||
step = step * multiplier;
|
||||
|
||||
if (isNonIntegerValue) value = Math.round(value);
|
||||
if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
|
||||
if (isNonIntegerStep) step = Math.round(step);
|
||||
}
|
||||
|
||||
return (value - stepBase) % step === 0;
|
||||
@@ -2130,7 +2142,10 @@ var ngValueDirective = function() {
|
||||
* makes it possible to use ngValue as a sort of one-way bind.
|
||||
*/
|
||||
function updateElementValue(element, attr, value) {
|
||||
element.prop('value', value);
|
||||
// Support: IE9 only
|
||||
// In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
|
||||
var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
|
||||
element.prop('value', propValue);
|
||||
attr.$set('value', value);
|
||||
}
|
||||
|
||||
|
||||
+109
-97
@@ -8,51 +8,64 @@
|
||||
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return ['$animate', function($animate) {
|
||||
var indexWatchExpression;
|
||||
|
||||
return ['$parse', function($parse) {
|
||||
return {
|
||||
restrict: 'AC',
|
||||
link: function(scope, element, attr) {
|
||||
var oldVal;
|
||||
var classCounts = element.data('$classCounts');
|
||||
var oldModulo = true;
|
||||
var oldClassString;
|
||||
|
||||
scope.$watch(attr[name], ngClassWatchAction, true);
|
||||
|
||||
attr.$observe('class', function(value) {
|
||||
ngClassWatchAction(scope.$eval(attr[name]));
|
||||
});
|
||||
|
||||
|
||||
if (name !== 'ngClass') {
|
||||
scope.$watch('$index', function($index, old$index) {
|
||||
/* eslint-disable no-bitwise */
|
||||
var mod = $index & 1;
|
||||
if (mod !== (old$index & 1)) {
|
||||
var classes = arrayClasses(scope.$eval(attr[name]));
|
||||
if (mod === selector) {
|
||||
addClasses(classes);
|
||||
} else {
|
||||
removeClasses(classes);
|
||||
}
|
||||
}
|
||||
/* eslint-enable */
|
||||
});
|
||||
}
|
||||
|
||||
function addClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, 1);
|
||||
attr.$addClass(newClasses);
|
||||
}
|
||||
|
||||
function removeClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, -1);
|
||||
attr.$removeClass(newClasses);
|
||||
}
|
||||
|
||||
function digestClassCounts(classes, count) {
|
||||
if (!classCounts) {
|
||||
// Use createMap() to prevent class assumptions involving property
|
||||
// names in Object.prototype
|
||||
var classCounts = element.data('$classCounts') || createMap();
|
||||
classCounts = createMap();
|
||||
element.data('$classCounts', classCounts);
|
||||
}
|
||||
|
||||
if (name !== 'ngClass') {
|
||||
if (!indexWatchExpression) {
|
||||
indexWatchExpression = $parse('$index', function moduloTwo($index) {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return $index & 1;
|
||||
});
|
||||
}
|
||||
|
||||
scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
|
||||
}
|
||||
|
||||
scope.$watch($parse(attr[name], toClassString), ngClassWatchAction);
|
||||
|
||||
function addClasses(classString) {
|
||||
classString = digestClassCounts(split(classString), 1);
|
||||
attr.$addClass(classString);
|
||||
}
|
||||
|
||||
function removeClasses(classString) {
|
||||
classString = digestClassCounts(split(classString), -1);
|
||||
attr.$removeClass(classString);
|
||||
}
|
||||
|
||||
function updateClasses(oldClassString, newClassString) {
|
||||
var oldClassArray = split(oldClassString);
|
||||
var newClassArray = split(newClassString);
|
||||
|
||||
var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
|
||||
var toAddArray = arrayDifference(newClassArray, oldClassArray);
|
||||
|
||||
var toRemoveString = digestClassCounts(toRemoveArray, -1);
|
||||
var toAddString = digestClassCounts(toAddArray, 1);
|
||||
|
||||
attr.$addClass(toAddString);
|
||||
attr.$removeClass(toRemoveString);
|
||||
}
|
||||
|
||||
function digestClassCounts(classArray, count) {
|
||||
var classesToUpdate = [];
|
||||
forEach(classes, function(className) {
|
||||
|
||||
forEach(classArray, function(className) {
|
||||
if (count > 0 || classCounts[className]) {
|
||||
classCounts[className] = (classCounts[className] || 0) + count;
|
||||
if (classCounts[className] === +(count > 0)) {
|
||||
@@ -60,77 +73,76 @@ function classDirective(name, selector) {
|
||||
}
|
||||
}
|
||||
});
|
||||
element.data('$classCounts', classCounts);
|
||||
|
||||
return classesToUpdate.join(' ');
|
||||
}
|
||||
|
||||
function updateClasses(oldClasses, newClasses) {
|
||||
var toAdd = arrayDifference(newClasses, oldClasses);
|
||||
var toRemove = arrayDifference(oldClasses, newClasses);
|
||||
toAdd = digestClassCounts(toAdd, 1);
|
||||
toRemove = digestClassCounts(toRemove, -1);
|
||||
if (toAdd && toAdd.length) {
|
||||
$animate.addClass(element, toAdd);
|
||||
}
|
||||
if (toRemove && toRemove.length) {
|
||||
$animate.removeClass(element, toRemove);
|
||||
function ngClassIndexWatchAction(newModulo) {
|
||||
// This watch-action should run before the `ngClassWatchAction()`, thus it
|
||||
// adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
|
||||
// `ngClassWatchAction()` will update the classes.
|
||||
if (newModulo === selector) {
|
||||
addClasses(oldClassString);
|
||||
} else {
|
||||
removeClasses(oldClassString);
|
||||
}
|
||||
|
||||
oldModulo = newModulo;
|
||||
}
|
||||
|
||||
function ngClassWatchAction(newVal) {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if (selector === true || (scope.$index & 1) === selector) {
|
||||
var newClasses = arrayClasses(newVal || []);
|
||||
if (!oldVal) {
|
||||
addClasses(newClasses);
|
||||
} else if (!equals(newVal,oldVal)) {
|
||||
var oldClasses = arrayClasses(oldVal);
|
||||
updateClasses(oldClasses, newClasses);
|
||||
}
|
||||
function ngClassWatchAction(newClassString) {
|
||||
// When using a one-time binding the newClassString will return
|
||||
// the pre-interceptor value until the one-time is complete
|
||||
if (!isString(newClassString)) {
|
||||
newClassString = toClassString(newClassString);
|
||||
}
|
||||
if (isArray(newVal)) {
|
||||
oldVal = newVal.map(function(v) { return shallowCopy(v); });
|
||||
} else {
|
||||
oldVal = shallowCopy(newVal);
|
||||
|
||||
if (oldModulo === selector) {
|
||||
updateClasses(oldClassString, newClassString);
|
||||
}
|
||||
|
||||
oldClassString = newClassString;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function arrayDifference(tokens1, tokens2) {
|
||||
var values = [];
|
||||
|
||||
outer:
|
||||
for (var i = 0; i < tokens1.length; i++) {
|
||||
var token = tokens1[i];
|
||||
for (var j = 0; j < tokens2.length; j++) {
|
||||
if (token === tokens2[j]) continue outer;
|
||||
}
|
||||
values.push(token);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function arrayClasses(classVal) {
|
||||
var classes = [];
|
||||
if (isArray(classVal)) {
|
||||
forEach(classVal, function(v) {
|
||||
classes = classes.concat(arrayClasses(v));
|
||||
});
|
||||
return classes;
|
||||
} else if (isString(classVal)) {
|
||||
return classVal.split(' ');
|
||||
} else if (isObject(classVal)) {
|
||||
forEach(classVal, function(v, k) {
|
||||
if (v) {
|
||||
classes = classes.concat(k.split(' '));
|
||||
}
|
||||
});
|
||||
return classes;
|
||||
}
|
||||
return classVal;
|
||||
}
|
||||
}];
|
||||
|
||||
// Helpers
|
||||
function arrayDifference(tokens1, tokens2) {
|
||||
if (!tokens1 || !tokens1.length) return [];
|
||||
if (!tokens2 || !tokens2.length) return tokens1;
|
||||
|
||||
var values = [];
|
||||
|
||||
outer:
|
||||
for (var i = 0; i < tokens1.length; i++) {
|
||||
var token = tokens1[i];
|
||||
for (var j = 0; j < tokens2.length; j++) {
|
||||
if (token === tokens2[j]) continue outer;
|
||||
}
|
||||
values.push(token);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
function split(classString) {
|
||||
return classString && classString.split(' ');
|
||||
}
|
||||
|
||||
function toClassString(classValue) {
|
||||
var classString = classValue;
|
||||
|
||||
if (isArray(classValue)) {
|
||||
classString = classValue.map(toClassString).join(' ');
|
||||
} else if (isObject(classValue)) {
|
||||
classString = Object.keys(classValue).
|
||||
filter(function(key) { return classValue[key]; }).
|
||||
join(' ');
|
||||
}
|
||||
|
||||
return classString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,15 +53,15 @@ forEach(
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function($element, attr) {
|
||||
// We expose the powerful $event object on the scope that provides access to the Window,
|
||||
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
|
||||
// checks at the cost of speed since event handler expressions are not executed as
|
||||
// frequently as regular change detection.
|
||||
var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
|
||||
// NOTE:
|
||||
// We expose the powerful `$event` object on the scope that provides access to the Window,
|
||||
// etc. This is OK, because expressions are not sandboxed any more (and the expression
|
||||
// sandbox was never meant to be a security feature anyway).
|
||||
var fn = $parse(attr[directiveName]);
|
||||
return function ngEventHandler(scope, element) {
|
||||
element.on(eventName, function(event) {
|
||||
var callback = function() {
|
||||
fn(scope, {$event:event});
|
||||
fn(scope, {$event: event});
|
||||
};
|
||||
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
|
||||
scope.$evalAsync(callback);
|
||||
|
||||
+71
-20
@@ -31,32 +31,57 @@ var ngModelMinErr = minErr('ngModel');
|
||||
* @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
|
||||
* String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
|
||||
* is set.
|
||||
*
|
||||
* @property {*} $modelValue The value in the model that the control is bound to.
|
||||
*
|
||||
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
|
||||
the control reads value from the DOM. The functions are called in array order, each passing
|
||||
its return value through to the next. The last return value is forwarded to the
|
||||
{@link ngModel.NgModelController#$validators `$validators`} collection.
|
||||
* the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
|
||||
`$viewValue`} from the DOM, usually via user input.
|
||||
See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
|
||||
Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
|
||||
|
||||
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
|
||||
`$viewValue`}.
|
||||
The functions are called in array order, each passing
|
||||
its return value through to the next. The last return value is forwarded to the
|
||||
{@link ngModel.NgModelController#$validators `$validators`} collection.
|
||||
|
||||
Returning `undefined` from a parser means a parse error occurred. In that case,
|
||||
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
|
||||
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
|
||||
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
|
||||
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
|
||||
`$viewValue`}.
|
||||
|
||||
Returning `undefined` from a parser means a parse error occurred. In that case,
|
||||
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
|
||||
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
|
||||
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
|
||||
|
||||
This simple example shows a parser that would convert text input value to lowercase:
|
||||
* ```js
|
||||
* function parse(value) {
|
||||
* if (value) {
|
||||
* return value.toLowerCase();
|
||||
* }
|
||||
* }
|
||||
* ngModelController.$parsers.push(parse);
|
||||
* ```
|
||||
|
||||
*
|
||||
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
|
||||
the model value changes. The functions are called in reverse array order, each passing the value through to the
|
||||
next. The last return value is used as the actual DOM value.
|
||||
Used to format / convert values for display in the control.
|
||||
the bound ngModel expression changes programmatically. The `$formatters` are not called when the
|
||||
value of the control is changed by user interaction.
|
||||
|
||||
Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
|
||||
`$modelValue`} for display in the control.
|
||||
|
||||
The functions are called in reverse array order, each passing the value through to the
|
||||
next. The last return value is used as the actual DOM value.
|
||||
|
||||
This simple example shows a formatter that would convert the model value to uppercase:
|
||||
|
||||
* ```js
|
||||
* function formatter(value) {
|
||||
* function format(value) {
|
||||
* if (value) {
|
||||
* return value.toUpperCase();
|
||||
* }
|
||||
* }
|
||||
* ngModel.$formatters.push(formatter);
|
||||
* ngModel.$formatters.push(format);
|
||||
* ```
|
||||
*
|
||||
* @property {Object.<string, function>} $validators A collection of validators that are applied
|
||||
@@ -256,7 +281,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
|
||||
|
||||
this.$$currentValidationRunId = 0;
|
||||
|
||||
this.$$scope = $scope;
|
||||
// https://github.com/angular/angular.js/issues/15833
|
||||
// Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched
|
||||
Object.defineProperty(this, '$$scope', {value: $scope});
|
||||
this.$$attr = $attr;
|
||||
this.$$element = $element;
|
||||
this.$$animate = $animate;
|
||||
@@ -764,9 +791,10 @@ NgModelController.prototype = {
|
||||
*
|
||||
* When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
|
||||
* and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
|
||||
* value sent directly for processing, finally to be applied to `$modelValue` and then the
|
||||
* **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
|
||||
* in the `$viewChangeListeners` list, are called.
|
||||
* value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
|
||||
* `$asyncValidators` are called and the value is applied to `$modelValue`.
|
||||
* Finally, the value is set to the **expression** specified in the `ng-model` attribute and
|
||||
* all the registered change listeners, in the `$viewChangeListeners` list are called.
|
||||
*
|
||||
* In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
|
||||
* and the `default` trigger is not listed, all those actions will remain pending until one of the
|
||||
@@ -829,6 +857,29 @@ NgModelController.prototype = {
|
||||
that.$commitViewValue();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
*
|
||||
* @name ngModel.NgModelController#$overrideModelOptions
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Override the current model options settings programmatically.
|
||||
*
|
||||
* The previous `ModelOptions` value will not be modified. Instead, a
|
||||
* new `ModelOptions` object will inherit from the previous one overriding
|
||||
* or inheriting settings that are defined in the given parameter.
|
||||
*
|
||||
* See {@link ngModelOptions} for information about what options can be specified
|
||||
* and how model option inheritance works.
|
||||
*
|
||||
* @param {Object} options a hash of settings to override the previous options
|
||||
*
|
||||
*/
|
||||
$overrideModelOptions: function(options) {
|
||||
this.$options = this.$options.createChild(options);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -841,8 +892,8 @@ function setupModelWatcher(ctrl) {
|
||||
// -> scope value did not change since the last digest as
|
||||
// ng-change executes in apply phase
|
||||
// 4. view should be changed back to 'a'
|
||||
ctrl.$$scope.$watch(function ngModelWatch() {
|
||||
var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
|
||||
ctrl.$$scope.$watch(function ngModelWatch(scope) {
|
||||
var modelValue = ctrl.$$ngModelGet(scope);
|
||||
|
||||
// if scope model value and ngModel value are out of sync
|
||||
// TODO(perf): why not move this to the action fn?
|
||||
|
||||
@@ -331,19 +331,27 @@ defaultModelOptions = new ModelOptions({
|
||||
*
|
||||
*/
|
||||
var ngModelOptionsDirective = function() {
|
||||
NgModelOptionsController.$inject = ['$attrs', '$scope'];
|
||||
function NgModelOptionsController($attrs, $scope) {
|
||||
this.$$attrs = $attrs;
|
||||
this.$$scope = $scope;
|
||||
}
|
||||
NgModelOptionsController.prototype = {
|
||||
$onInit: function() {
|
||||
var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
|
||||
var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
|
||||
|
||||
this.$options = parentOptions.createChild(modelOptionsDefinition);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
// ngModelOptions needs to run before ngModel and input directives
|
||||
priority: 10,
|
||||
require: ['ngModelOptions', '?^^ngModelOptions'],
|
||||
controller: function NgModelOptionsController() {},
|
||||
link: {
|
||||
pre: function ngModelOptionsPreLinkFn(scope, element, attrs, ctrls) {
|
||||
var optionsCtrl = ctrls[0];
|
||||
var parentOptions = ctrls[1] ? ctrls[1].$options : defaultModelOptions;
|
||||
optionsCtrl.$options = parentOptions.createChild(scope.$eval(attrs.ngModelOptions));
|
||||
}
|
||||
}
|
||||
require: {parentCtrl: '?^^ngModelOptions'},
|
||||
bindToController: true,
|
||||
controller: NgModelOptionsController
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -111,13 +111,8 @@ var ngOptionsMinErr = minErr('ngOptions');
|
||||
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
|
||||
*
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} required The control is considered valid only if value is entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {comprehension_expression=} ngOptions in one of the following forms:
|
||||
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
|
||||
* @param {comprehension_expression} ngOptions in one of the following forms:
|
||||
*
|
||||
* * for array data sources:
|
||||
* * `label` **`for`** `value` **`in`** `array`
|
||||
@@ -156,6 +151,13 @@ var ngOptionsMinErr = minErr('ngOptions');
|
||||
* used to identify the objects in the array. The `trackexpr` will most likely refer to the
|
||||
* `value` variable (e.g. `value.propertyName`). With this the selection is preserved
|
||||
* even when the options are recreated (e.g. reloaded from the server).
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} required The control is considered valid only if value is entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
|
||||
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
|
||||
*
|
||||
* @example
|
||||
<example module="selectExample" name="select">
|
||||
@@ -505,17 +507,17 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
} else {
|
||||
|
||||
selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
|
||||
options.items.forEach(function(option) {
|
||||
option.element.selected = false;
|
||||
});
|
||||
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
|
||||
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
|
||||
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
|
||||
|
||||
if (value) {
|
||||
value.forEach(function(item) {
|
||||
var option = options.getOptionFromViewValue(item);
|
||||
if (option) option.element.selected = true;
|
||||
});
|
||||
}
|
||||
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
|
||||
|
||||
options.items.forEach(function(option) {
|
||||
if (option.element.selected && !includes(selectedOptions, option)) {
|
||||
option.element.selected = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -605,6 +607,14 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
updateOptionElement(option, optionElement);
|
||||
}
|
||||
|
||||
function getAndUpdateSelectedOption(viewValue) {
|
||||
var option = options.getOptionFromViewValue(viewValue);
|
||||
var element = option && option.element;
|
||||
|
||||
if (element && !element.selected) element.selected = true;
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
function updateOptionElement(option, element) {
|
||||
option.element = element;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ngRepeat
|
||||
* @multiElement
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
|
||||
|
||||
+224
-159
@@ -8,11 +8,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `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. The `.ng-hide` CSS class is predefined
|
||||
* in AngularJS and sets the display style to none (using an !important flag).
|
||||
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
|
||||
* The `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.
|
||||
* The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
|
||||
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
|
||||
* {@link ng.directive:ngCsp ngCsp}).
|
||||
*
|
||||
* ```html
|
||||
* <!-- when $scope.myValue is truthy (element is visible) -->
|
||||
@@ -22,31 +24,32 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
* <div ng-show="myValue" class="ng-hide"></div>
|
||||
* ```
|
||||
*
|
||||
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
|
||||
* attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
|
||||
* from the element causing the element not to appear hidden.
|
||||
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
|
||||
* to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
|
||||
* CSS class is removed from the element causing the element not to appear hidden.
|
||||
*
|
||||
* ## Why is !important used?
|
||||
* ## Why is `!important` used?
|
||||
*
|
||||
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
|
||||
* can be easily overridden by heavier selectors. For example, something as simple
|
||||
* as changing the display style on a HTML list item would make hidden elements appear visible.
|
||||
* This also becomes a bigger issue when dealing with CSS frameworks.
|
||||
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
|
||||
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
|
||||
* simple as changing the display style on a HTML list item would make hidden elements appear
|
||||
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
|
||||
*
|
||||
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
|
||||
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
|
||||
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
|
||||
* By using `!important`, the show and hide behavior will work as expected despite any clash between
|
||||
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
|
||||
* developer chooses to override the styling to change how to hide an element then it is just a
|
||||
* matter of using `!important` in their own CSS code.
|
||||
*
|
||||
* ### Overriding `.ng-hide`
|
||||
*
|
||||
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
|
||||
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
|
||||
* class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
|
||||
* with extra animation classes that can be added.
|
||||
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
|
||||
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
|
||||
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
|
||||
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
|
||||
*
|
||||
* ```css
|
||||
* .ng-hide:not(.ng-hide-animate) {
|
||||
* /* this is just another form of hiding an element */
|
||||
* /* These are just alternative ways of hiding an element */
|
||||
* display: block!important;
|
||||
* position: absolute;
|
||||
* top: -9999px;
|
||||
@@ -54,29 +57,20 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* By default you don't need to override in CSS anything and the animations will work around the display style.
|
||||
* By default you don't need to override anything in CSS and the animations will work around the
|
||||
* display style.
|
||||
*
|
||||
* ## A note about animations with `ngShow`
|
||||
*
|
||||
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
|
||||
* is true and false. This system works like the animation system present with ngClass except that
|
||||
* you must also include the !important flag to override the display property
|
||||
* so that you can perform an animation when the element is hidden during the time of the animation.
|
||||
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
|
||||
* directive expression is true and false. This system works like the animation system present with
|
||||
* `ngClass` except that you must also include the `!important` flag to override the display
|
||||
* property so that the elements are not actually hidden during the animation.
|
||||
*
|
||||
* ```css
|
||||
* //
|
||||
* //a working example can be found at the bottom of this page
|
||||
* //
|
||||
* /* A working example can be found at the bottom of this page. */
|
||||
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
|
||||
* /* this is required as of 1.3x to properly
|
||||
* apply all styling in a show/hide animation */
|
||||
* transition: 0s linear all;
|
||||
* }
|
||||
*
|
||||
* .my-element.ng-hide-add-active,
|
||||
* .my-element.ng-hide-remove-active {
|
||||
* /* the transition is defined in the active class */
|
||||
* transition: 1s linear all;
|
||||
* transition: all 0.5s linear;
|
||||
* }
|
||||
*
|
||||
* .my-element.ng-hide-add { ... }
|
||||
@@ -85,76 +79,108 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
|
||||
* ```
|
||||
*
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
|
||||
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
|
||||
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
|
||||
* | Animation | Occurs |
|
||||
* |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
|
||||
* then the element is shown or hidden respectively.
|
||||
* @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
|
||||
* element is shown/hidden respectively.
|
||||
*
|
||||
* @example
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show">
|
||||
* A simple example, animating the element's opacity:
|
||||
*
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
|
||||
<file name="index.html">
|
||||
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
|
||||
<div>
|
||||
Show:
|
||||
<div class="check-element animate-show" ng-show="checked">
|
||||
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||
</div>
|
||||
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
|
||||
<div class="check-element animate-show-hide" ng-show="checked">
|
||||
I show up when your checkbox is checked.
|
||||
</div>
|
||||
<div>
|
||||
Hide:
|
||||
<div class="check-element animate-show" ng-hide="checked">
|
||||
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="glyphicons.css">
|
||||
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.animate-show {
|
||||
line-height: 20px;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
.animate-show-hide.ng-hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
transition: all linear 0.5s;
|
||||
}
|
||||
|
||||
.animate-show.ng-hide {
|
||||
line-height: 0;
|
||||
opacity: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.check-element {
|
||||
padding: 10px;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
|
||||
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
|
||||
it('should check ngShow', function() {
|
||||
var checkbox = element(by.model('checked'));
|
||||
var checkElem = element(by.css('.check-element'));
|
||||
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
||||
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
||||
expect(checkElem.isDisplayed()).toBe(false);
|
||||
checkbox.click();
|
||||
expect(checkElem.isDisplayed()).toBe(true);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* <hr />
|
||||
* @example
|
||||
* A more complex example, featuring different show/hide animations:
|
||||
*
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
|
||||
<file name="index.html">
|
||||
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
|
||||
<div class="check-element funky-show-hide" ng-show="checked">
|
||||
I show up when your checkbox is checked.
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
body {
|
||||
overflow: hidden;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
element(by.model('checked')).click();
|
||||
.funky-show-hide.ng-hide-add {
|
||||
transform: rotateZ(0);
|
||||
transform-origin: right;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
||||
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
||||
.funky-show-hide.ng-hide-add.ng-hide-add-active {
|
||||
transform: rotateZ(-135deg);
|
||||
}
|
||||
|
||||
.funky-show-hide.ng-hide-remove {
|
||||
transform: rotateY(90deg);
|
||||
transform-origin: left;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
|
||||
.check-element {
|
||||
border: 1px solid black;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should check ngShow', function() {
|
||||
var checkbox = element(by.model('checked'));
|
||||
var checkElem = element(by.css('.check-element'));
|
||||
|
||||
expect(checkElem.isDisplayed()).toBe(false);
|
||||
checkbox.click();
|
||||
expect(checkElem.isDisplayed()).toBe(true);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -184,11 +210,13 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `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. The `.ng-hide` CSS class is predefined
|
||||
* in AngularJS and sets the display style to none (using an !important flag).
|
||||
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
|
||||
* The `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.
|
||||
* The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
|
||||
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
|
||||
* {@link ng.directive:ngCsp ngCsp}).
|
||||
*
|
||||
* ```html
|
||||
* <!-- when $scope.myValue is truthy (element is hidden) -->
|
||||
@@ -198,30 +226,32 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* <div ng-hide="myValue"></div>
|
||||
* ```
|
||||
*
|
||||
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
|
||||
* attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
|
||||
* from the element causing the element not to appear hidden.
|
||||
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
|
||||
* to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
|
||||
* CSS class is removed from the element causing the element not to appear hidden.
|
||||
*
|
||||
* ## Why is !important used?
|
||||
* ## Why is `!important` used?
|
||||
*
|
||||
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
|
||||
* can be easily overridden by heavier selectors. For example, something as simple
|
||||
* as changing the display style on a HTML list item would make hidden elements appear visible.
|
||||
* This also becomes a bigger issue when dealing with CSS frameworks.
|
||||
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
|
||||
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
|
||||
* simple as changing the display style on a HTML list item would make hidden elements appear
|
||||
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
|
||||
*
|
||||
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
|
||||
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
|
||||
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
|
||||
* By using `!important`, the show and hide behavior will work as expected despite any clash between
|
||||
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
|
||||
* developer chooses to override the styling to change how to hide an element then it is just a
|
||||
* matter of using `!important` in their own CSS code.
|
||||
*
|
||||
* ### Overriding `.ng-hide`
|
||||
*
|
||||
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
|
||||
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
|
||||
* class in CSS:
|
||||
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
|
||||
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
|
||||
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
|
||||
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
|
||||
*
|
||||
* ```css
|
||||
* .ng-hide {
|
||||
* /* this is just another form of hiding an element */
|
||||
* .ng-hide:not(.ng-hide-animate) {
|
||||
* /* These are just alternative ways of hiding an element */
|
||||
* display: block!important;
|
||||
* position: absolute;
|
||||
* top: -9999px;
|
||||
@@ -229,20 +259,20 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* By default you don't need to override in CSS anything and the animations will work around the display style.
|
||||
* By default you don't need to override in CSS anything and the animations will work around the
|
||||
* display style.
|
||||
*
|
||||
* ## A note about animations with `ngHide`
|
||||
*
|
||||
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
|
||||
* is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
|
||||
* CSS class is added and removed for you instead of your own CSS class.
|
||||
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
|
||||
* directive expression is true and false. This system works like the animation system present with
|
||||
* `ngClass` except that you must also include the `!important` flag to override the display
|
||||
* property so that the elements are not actually hidden during the animation.
|
||||
*
|
||||
* ```css
|
||||
* //
|
||||
* //a working example can be found at the bottom of this page
|
||||
* //
|
||||
* /* A working example can be found at the bottom of this page. */
|
||||
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
|
||||
* transition: 0.5s linear all;
|
||||
* transition: all 0.5s linear;
|
||||
* }
|
||||
*
|
||||
* .my-element.ng-hide-add { ... }
|
||||
@@ -251,74 +281,109 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
|
||||
* ```
|
||||
*
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
|
||||
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
|
||||
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
|
||||
* | Animation | Occurs |
|
||||
* |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
|
||||
*
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
|
||||
* the element is shown or hidden respectively.
|
||||
* @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
|
||||
* element is hidden/shown respectively.
|
||||
*
|
||||
* @example
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide">
|
||||
* A simple example, animating the element's opacity:
|
||||
*
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
|
||||
<file name="index.html">
|
||||
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
|
||||
<div>
|
||||
Show:
|
||||
<div class="check-element animate-hide" ng-show="checked">
|
||||
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||
</div>
|
||||
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
|
||||
<div class="check-element animate-show-hide" ng-hide="checked">
|
||||
I hide when your checkbox is checked.
|
||||
</div>
|
||||
<div>
|
||||
Hide:
|
||||
<div class="check-element animate-hide" ng-hide="checked">
|
||||
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="glyphicons.css">
|
||||
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.animate-hide {
|
||||
transition: all linear 0.5s;
|
||||
line-height: 20px;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
.animate-show-hide.ng-hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-hide.ng-hide {
|
||||
line-height: 0;
|
||||
opacity: 0;
|
||||
padding: 0 10px;
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
transition: all linear 0.5s;
|
||||
}
|
||||
|
||||
.check-element {
|
||||
padding: 10px;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
|
||||
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
|
||||
it('should check ngHide', function() {
|
||||
var checkbox = element(by.model('checked'));
|
||||
var checkElem = element(by.css('.check-element'));
|
||||
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
||||
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
||||
expect(checkElem.isDisplayed()).toBe(true);
|
||||
checkbox.click();
|
||||
expect(checkElem.isDisplayed()).toBe(false);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* <hr />
|
||||
* @example
|
||||
* A more complex example, featuring different show/hide animations:
|
||||
*
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
|
||||
<file name="index.html">
|
||||
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
|
||||
<div class="check-element funky-show-hide" ng-hide="checked">
|
||||
I hide when your checkbox is checked.
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
body {
|
||||
overflow: hidden;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
element(by.model('checked')).click();
|
||||
.funky-show-hide.ng-hide-add {
|
||||
transform: rotateZ(0);
|
||||
transform-origin: right;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
||||
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
||||
.funky-show-hide.ng-hide-add.ng-hide-add-active {
|
||||
transform: rotateZ(-135deg);
|
||||
}
|
||||
|
||||
.funky-show-hide.ng-hide-remove {
|
||||
transform: rotateY(90deg);
|
||||
transform-origin: left;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
|
||||
.check-element {
|
||||
border: 1px solid black;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should check ngHide', function() {
|
||||
var checkbox = element(by.model('checked'));
|
||||
var checkElem = element(by.css('.check-element'));
|
||||
|
||||
expect(checkElem.isDisplayed()).toBe(true);
|
||||
checkbox.click();
|
||||
expect(checkElem.isDisplayed()).toBe(false);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
+38
-17
@@ -4,6 +4,18 @@
|
||||
|
||||
var noopNgModelController = { $setViewValue: noop, $render: noop };
|
||||
|
||||
function setOptionSelectedStatus(optionEl, value) {
|
||||
optionEl.prop('selected', value); // needed for IE
|
||||
/**
|
||||
* When unselecting an option, setting the property to null / false should be enough
|
||||
* However, screenreaders might react to the selected attribute instead, see
|
||||
* https://github.com/angular/angular.js/issues/14419
|
||||
* Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
|
||||
* or null
|
||||
*/
|
||||
optionEl.attr('selected', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name select.SelectController
|
||||
@@ -16,7 +28,7 @@ var SelectController =
|
||||
['$element', '$scope', /** @this */ function($element, $scope) {
|
||||
|
||||
var self = this,
|
||||
optionsMap = new HashMap();
|
||||
optionsMap = new NgMap();
|
||||
|
||||
self.selectValueMap = {}; // Keys are the hashed values, values the original values
|
||||
|
||||
@@ -44,14 +56,14 @@ var SelectController =
|
||||
var unknownVal = self.generateUnknownOptionValue(val);
|
||||
self.unknownOption.val(unknownVal);
|
||||
$element.prepend(self.unknownOption);
|
||||
setOptionAsSelected(self.unknownOption);
|
||||
setOptionSelectedStatus(self.unknownOption, true);
|
||||
$element.val(unknownVal);
|
||||
};
|
||||
|
||||
self.updateUnknownOption = function(val) {
|
||||
var unknownVal = self.generateUnknownOptionValue(val);
|
||||
self.unknownOption.val(unknownVal);
|
||||
setOptionAsSelected(self.unknownOption);
|
||||
setOptionSelectedStatus(self.unknownOption, true);
|
||||
$element.val(unknownVal);
|
||||
};
|
||||
|
||||
@@ -66,7 +78,7 @@ var SelectController =
|
||||
self.selectEmptyOption = function() {
|
||||
if (self.emptyOption) {
|
||||
$element.val('');
|
||||
setOptionAsSelected(self.emptyOption);
|
||||
setOptionSelectedStatus(self.emptyOption, true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -102,7 +114,7 @@ var SelectController =
|
||||
// Make sure to remove the selected attribute from the previously selected option
|
||||
// Otherwise, screen readers might get confused
|
||||
var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
|
||||
if (currentlySelectedOption) currentlySelectedOption.removeAttribute('selected');
|
||||
if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
|
||||
|
||||
if (self.hasOption(value)) {
|
||||
self.removeUnknownOption();
|
||||
@@ -112,7 +124,7 @@ var SelectController =
|
||||
|
||||
// Set selected attribute and property on selected option for screen readers
|
||||
var selectedOption = $element[0].options[$element[0].selectedIndex];
|
||||
setOptionAsSelected(jqLite(selectedOption));
|
||||
setOptionSelectedStatus(jqLite(selectedOption), true);
|
||||
} else {
|
||||
if (value == null && self.emptyOption) {
|
||||
self.removeUnknownOption();
|
||||
@@ -137,7 +149,7 @@ var SelectController =
|
||||
self.emptyOption = element;
|
||||
}
|
||||
var count = optionsMap.get(value) || 0;
|
||||
optionsMap.put(value, count + 1);
|
||||
optionsMap.set(value, count + 1);
|
||||
// Only render at the end of a digest. This improves render performance when many options
|
||||
// are added during a digest and ensures all relevant options are correctly marked as selected
|
||||
scheduleRender();
|
||||
@@ -148,13 +160,13 @@ var SelectController =
|
||||
var count = optionsMap.get(value);
|
||||
if (count) {
|
||||
if (count === 1) {
|
||||
optionsMap.remove(value);
|
||||
optionsMap.delete(value);
|
||||
if (value === '') {
|
||||
self.hasEmptyOption = false;
|
||||
self.emptyOption = undefined;
|
||||
}
|
||||
} else {
|
||||
optionsMap.put(value, count - 1);
|
||||
optionsMap.set(value, count - 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -281,7 +293,7 @@ var SelectController =
|
||||
var removeValue = optionAttrs.value;
|
||||
|
||||
self.removeOption(removeValue);
|
||||
self.ngModelCtrl.$render();
|
||||
scheduleRender();
|
||||
|
||||
if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
|
||||
currentValue === removeValue
|
||||
@@ -292,11 +304,6 @@ var SelectController =
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function setOptionAsSelected(optionEl) {
|
||||
optionEl.prop('selected', true); // needed for IE
|
||||
optionEl.attr('selected', true);
|
||||
}
|
||||
}];
|
||||
|
||||
/**
|
||||
@@ -366,6 +373,8 @@ var SelectController =
|
||||
* interaction with the select element.
|
||||
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
|
||||
* set on the model on selection. See {@link ngOptions `ngOptions`}.
|
||||
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
|
||||
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
|
||||
*
|
||||
* @example
|
||||
* ### Simple `select` elements with static options
|
||||
@@ -606,9 +615,21 @@ var selectDirective = function() {
|
||||
|
||||
// Write value now needs to set the selected property of each matching option
|
||||
selectCtrl.writeValue = function writeMultipleValue(value) {
|
||||
var items = new HashMap(value);
|
||||
forEach(element.find('option'), function(option) {
|
||||
option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value]));
|
||||
var shouldBeSelected = !!value && (includes(value, option.value) ||
|
||||
includes(value, selectCtrl.selectValueMap[option.value]));
|
||||
var currentlySelected = option.selected;
|
||||
|
||||
// IE and Edge, adding options to the selection via shift+click/UP/DOWN,
|
||||
// will de-select already selected options if "selected" on those options was set
|
||||
// more than once (i.e. when the options were already selected)
|
||||
// So we only modify the selected property if neccessary.
|
||||
// Note: this behavior cannot be replicated via unit tests because it only shows in the
|
||||
// actual user interface.
|
||||
if (shouldBeSelected !== currentlySelected) {
|
||||
setOptionSelectedStatus(jqLite(option), shouldBeSelected);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+10
-3
@@ -9,6 +9,9 @@
|
||||
* Selects a subset of items from `array` and returns it as a new array.
|
||||
*
|
||||
* @param {Array} array The source array.
|
||||
* <div class="alert alert-info">
|
||||
* **Note**: If the array contains objects that reference themselves, filtering is not possible.
|
||||
* </div>
|
||||
* @param {string|Object|function()} expression The predicate to be used for selecting items from
|
||||
* `array`.
|
||||
*
|
||||
@@ -42,8 +45,9 @@
|
||||
* The final result is an array of those elements that the predicate returned true for.
|
||||
*
|
||||
* @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
|
||||
* determining if the expected value (from the filter expression) and actual value (from
|
||||
* the object in the array) should be considered a match.
|
||||
* determining if values retrieved using `expression` (when it is not a function) should be
|
||||
* considered a match based on the the expected value (from the filter expression) and actual
|
||||
* value (from the object in the array).
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
@@ -226,7 +230,10 @@ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstA
|
||||
var key;
|
||||
if (matchAgainstAnyProp) {
|
||||
for (key in actual) {
|
||||
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
|
||||
// Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
|
||||
// See: https://github.com/angular/angular.js/issues/15644
|
||||
if (key.charAt && (key.charAt(0) !== '$') &&
|
||||
deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ var DATE_FORMATS = {
|
||||
GGGG: longEraGetter
|
||||
};
|
||||
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,
|
||||
NUMBER_STRING = /^-?\d+$/;
|
||||
|
||||
/**
|
||||
@@ -534,6 +534,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+
|
||||
* `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* Any other characters in the `format` string will be output as-is.
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
|
||||
+8
-2
@@ -138,7 +138,12 @@ function defaultHttpResponseTransform(data, headers) {
|
||||
if (tempData) {
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
|
||||
data = fromJson(tempData);
|
||||
try {
|
||||
data = fromJson(tempData);
|
||||
} catch (e) {
|
||||
throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
|
||||
'Parse error: "{1}"', data, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1255,7 +1260,8 @@ function $HttpProvider() {
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = isObject(config.cache) ? config.cache
|
||||
: isObject(defaults.cache) ? defaults.cache
|
||||
: isObject(/** @type {?} */ (defaults).cache)
|
||||
? /** @type {?} */ (defaults).cache
|
||||
: defaultCache;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -33,14 +33,15 @@ function $IntervalProvider() {
|
||||
* appropriate moment. See the example below for more details on how and when to do this.
|
||||
* </div>
|
||||
*
|
||||
* @param {function()} fn A function that should be called repeatedly.
|
||||
* @param {function()} fn A function that should be called repeatedly. If no additional arguments
|
||||
* are passed (see below), the function is called with the current iteration count.
|
||||
* @param {number} delay Number of milliseconds between each function call.
|
||||
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
|
||||
* indefinitely.
|
||||
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
||||
* @param {...*=} Pass additional parameters to the executed function.
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
* @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete.
|
||||
*
|
||||
* @example
|
||||
* <example module="intervalExample" name="interval-service">
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
* how they vary compared to the requested url.
|
||||
*/
|
||||
var $jsonpCallbacksProvider = /** @this */ function() {
|
||||
this.$get = ['$window', function($window) {
|
||||
var callbacks = $window.angular.callbacks;
|
||||
this.$get = function() {
|
||||
var callbacks = angular.callbacks;
|
||||
var callbackMap = {};
|
||||
|
||||
function createCallback(callbackId) {
|
||||
@@ -78,5 +78,5 @@ var $jsonpCallbacksProvider = /** @this */ function() {
|
||||
delete callbackMap[callbackPath];
|
||||
}
|
||||
};
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
+37
-26
@@ -137,6 +137,8 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
|
||||
|
||||
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
|
||||
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
|
||||
|
||||
this.$$urlUpdatedByLocation = true;
|
||||
};
|
||||
|
||||
this.$$parseLinkUrl = function(url, relHref) {
|
||||
@@ -214,7 +216,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
|
||||
withoutHashUrl = '';
|
||||
if (isUndefined(withoutBaseUrl)) {
|
||||
appBase = url;
|
||||
this.replace();
|
||||
/** @type {?} */ (this).replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,6 +272,8 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
|
||||
|
||||
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
|
||||
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
|
||||
|
||||
this.$$urlUpdatedByLocation = true;
|
||||
};
|
||||
|
||||
this.$$parseLinkUrl = function(url, relHref) {
|
||||
@@ -327,6 +331,8 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
|
||||
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
|
||||
// include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
|
||||
this.$$absUrl = appBase + hashPrefix + this.$$url;
|
||||
|
||||
this.$$urlUpdatedByLocation = true;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -656,6 +662,7 @@ forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], fun
|
||||
// but we're changing the $$state reference to $browser.state() during the $digest
|
||||
// so the modification window is narrow.
|
||||
this.$$state = isUndefined(state) ? null : state;
|
||||
this.$$urlUpdatedByLocation = true;
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -968,36 +975,40 @@ function $LocationProvider() {
|
||||
|
||||
// update browser
|
||||
$rootScope.$watch(function $locationWatch() {
|
||||
var oldUrl = trimEmptyHash($browser.url());
|
||||
var newUrl = trimEmptyHash($location.absUrl());
|
||||
var oldState = $browser.state();
|
||||
var currentReplace = $location.$$replace;
|
||||
var urlOrStateChanged = oldUrl !== newUrl ||
|
||||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
|
||||
if (initializing || $location.$$urlUpdatedByLocation) {
|
||||
$location.$$urlUpdatedByLocation = false;
|
||||
|
||||
if (initializing || urlOrStateChanged) {
|
||||
initializing = false;
|
||||
var oldUrl = trimEmptyHash($browser.url());
|
||||
var newUrl = trimEmptyHash($location.absUrl());
|
||||
var oldState = $browser.state();
|
||||
var currentReplace = $location.$$replace;
|
||||
var urlOrStateChanged = oldUrl !== newUrl ||
|
||||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
|
||||
|
||||
$rootScope.$evalAsync(function() {
|
||||
var newUrl = $location.absUrl();
|
||||
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
|
||||
$location.$$state, oldState).defaultPrevented;
|
||||
if (initializing || urlOrStateChanged) {
|
||||
initializing = false;
|
||||
|
||||
// if the location was changed by a `$locationChangeStart` handler then stop
|
||||
// processing this location change
|
||||
if ($location.absUrl() !== newUrl) return;
|
||||
$rootScope.$evalAsync(function() {
|
||||
var newUrl = $location.absUrl();
|
||||
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
|
||||
$location.$$state, oldState).defaultPrevented;
|
||||
|
||||
if (defaultPrevented) {
|
||||
$location.$$parse(oldUrl);
|
||||
$location.$$state = oldState;
|
||||
} else {
|
||||
if (urlOrStateChanged) {
|
||||
setBrowserUrlWithFallback(newUrl, currentReplace,
|
||||
oldState === $location.$$state ? null : $location.$$state);
|
||||
// if the location was changed by a `$locationChangeStart` handler then stop
|
||||
// processing this location change
|
||||
if ($location.absUrl() !== newUrl) return;
|
||||
|
||||
if (defaultPrevented) {
|
||||
$location.$$parse(oldUrl);
|
||||
$location.$$state = oldState;
|
||||
} else {
|
||||
if (urlOrStateChanged) {
|
||||
setBrowserUrlWithFallback(newUrl, currentReplace,
|
||||
oldState === $location.$$state ? null : $location.$$state);
|
||||
}
|
||||
afterLocationChange(oldUrl, oldState);
|
||||
}
|
||||
afterLocationChange(oldUrl, oldState);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$location.$$replace = false;
|
||||
|
||||
+11
-2
@@ -60,13 +60,22 @@ function $LogProvider() {
|
||||
this.debugEnabled = function(flag) {
|
||||
if (isDefined(flag)) {
|
||||
debug = flag;
|
||||
return this;
|
||||
return this;
|
||||
} else {
|
||||
return debug;
|
||||
}
|
||||
};
|
||||
|
||||
this.$get = ['$window', function($window) {
|
||||
// Support: IE 9-11, Edge 12-14+
|
||||
// IE/Edge display errors in such a way that it requires the user to click in 4 places
|
||||
// to see the stack trace. There is no way to feature-detect it so there's a chance
|
||||
// of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
|
||||
// break apps. Other browsers display errors in a sensible way and some of them map stack
|
||||
// traces along source maps if available so it makes sense to let browsers display it
|
||||
// as they want.
|
||||
var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -124,7 +133,7 @@ function $LogProvider() {
|
||||
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (arg.stack) {
|
||||
if (arg.stack && formatStackTrace) {
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
|
||||
? 'Error: ' + arg.message + '\n' + arg.stack
|
||||
: arg.stack;
|
||||
|
||||
+52
-65
@@ -717,6 +717,13 @@ function findConstantAndWatchExpressions(ast, $filter) {
|
||||
if (!property.value.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
|
||||
}
|
||||
if (property.computed) {
|
||||
findConstantAndWatchExpressions(property.key, $filter);
|
||||
if (!property.key.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, property.key.toWatch);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
ast.constant = allConstants;
|
||||
ast.toWatch = argsToWatch;
|
||||
@@ -762,15 +769,13 @@ function isConstant(ast) {
|
||||
return ast.constant;
|
||||
}
|
||||
|
||||
function ASTCompiler(astBuilder, $filter) {
|
||||
this.astBuilder = astBuilder;
|
||||
function ASTCompiler($filter) {
|
||||
this.$filter = $filter;
|
||||
}
|
||||
|
||||
ASTCompiler.prototype = {
|
||||
compile: function(expression) {
|
||||
compile: function(ast) {
|
||||
var self = this;
|
||||
var ast = this.astBuilder.ast(expression);
|
||||
this.state = {
|
||||
nextId: 0,
|
||||
filters: {},
|
||||
@@ -825,8 +830,6 @@ ASTCompiler.prototype = {
|
||||
ifDefined,
|
||||
plusFn);
|
||||
this.state = this.stage = undefined;
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -1229,15 +1232,13 @@ ASTCompiler.prototype = {
|
||||
};
|
||||
|
||||
|
||||
function ASTInterpreter(astBuilder, $filter) {
|
||||
this.astBuilder = astBuilder;
|
||||
function ASTInterpreter($filter) {
|
||||
this.$filter = $filter;
|
||||
}
|
||||
|
||||
ASTInterpreter.prototype = {
|
||||
compile: function(expression) {
|
||||
compile: function(ast) {
|
||||
var self = this;
|
||||
var ast = this.astBuilder.ast(expression);
|
||||
findConstantAndWatchExpressions(ast, self.$filter);
|
||||
var assignable;
|
||||
var assign;
|
||||
@@ -1276,8 +1277,6 @@ ASTInterpreter.prototype = {
|
||||
if (inputs) {
|
||||
fn.inputs = inputs;
|
||||
}
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -1606,20 +1605,21 @@ ASTInterpreter.prototype = {
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var Parser = function Parser(lexer, $filter, options) {
|
||||
this.lexer = lexer;
|
||||
this.$filter = $filter;
|
||||
this.options = options;
|
||||
function Parser(lexer, $filter, options) {
|
||||
this.ast = new AST(lexer, options);
|
||||
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
|
||||
new ASTCompiler(this.ast, $filter);
|
||||
};
|
||||
this.astCompiler = options.csp ? new ASTInterpreter($filter) :
|
||||
new ASTCompiler($filter);
|
||||
}
|
||||
|
||||
Parser.prototype = {
|
||||
constructor: Parser,
|
||||
|
||||
parse: function(text) {
|
||||
return this.astCompiler.compile(text);
|
||||
var ast = this.ast.ast(text);
|
||||
var fn = this.astCompiler.compile(ast);
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1765,8 +1765,8 @@ function $ParseProvider() {
|
||||
if (parsedExpression.constant) {
|
||||
parsedExpression.$$watchDelegate = constantWatchDelegate;
|
||||
} else if (oneTime) {
|
||||
parsedExpression.$$watchDelegate = parsedExpression.literal ?
|
||||
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
|
||||
parsedExpression.oneTime = true;
|
||||
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
|
||||
} else if (parsedExpression.inputs) {
|
||||
parsedExpression.$$watchDelegate = inputsWatchDelegate;
|
||||
}
|
||||
@@ -1782,7 +1782,7 @@ function $ParseProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
|
||||
function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
|
||||
|
||||
if (newValue == null || oldValueOfValue == null) { // null/undefined
|
||||
return newValue === oldValueOfValue;
|
||||
@@ -1795,7 +1795,7 @@ function $ParseProvider() {
|
||||
// be cheaply dirty-checked
|
||||
newValue = getValueOf(newValue);
|
||||
|
||||
if (typeof newValue === 'object') {
|
||||
if (typeof newValue === 'object' && !compareObjectIdentity) {
|
||||
// objects/arrays are not supported - deep-watching them would be too expensive
|
||||
return false;
|
||||
}
|
||||
@@ -1817,7 +1817,7 @@ function $ParseProvider() {
|
||||
inputExpressions = inputExpressions[0];
|
||||
return scope.$watch(function expressionInputWatch(scope) {
|
||||
var newInputValue = inputExpressions(scope);
|
||||
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
|
||||
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
|
||||
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
|
||||
oldInputValueOf = newInputValue && getValueOf(newInputValue);
|
||||
}
|
||||
@@ -1837,7 +1837,7 @@ function $ParseProvider() {
|
||||
|
||||
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
|
||||
var newInputValue = inputExpressions[i](scope);
|
||||
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
|
||||
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
|
||||
oldInputValues[i] = newInputValue;
|
||||
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
|
||||
}
|
||||
@@ -1852,6 +1852,7 @@ function $ParseProvider() {
|
||||
}
|
||||
|
||||
function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
|
||||
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
|
||||
var unwatch, lastValue;
|
||||
if (parsedExpression.inputs) {
|
||||
unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression);
|
||||
@@ -1868,9 +1869,9 @@ function $ParseProvider() {
|
||||
if (isFunction(listener)) {
|
||||
listener(value, old, scope);
|
||||
}
|
||||
if (isDefined(value)) {
|
||||
if (isDone(value)) {
|
||||
scope.$$postDigest(function() {
|
||||
if (isDefined(lastValue)) {
|
||||
if (isDone(lastValue)) {
|
||||
unwatch();
|
||||
}
|
||||
});
|
||||
@@ -1878,31 +1879,12 @@ function $ParseProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
|
||||
var unwatch, lastValue;
|
||||
unwatch = scope.$watch(function oneTimeWatch(scope) {
|
||||
return parsedExpression(scope);
|
||||
}, function oneTimeListener(value, old, scope) {
|
||||
lastValue = value;
|
||||
if (isFunction(listener)) {
|
||||
listener(value, old, scope);
|
||||
}
|
||||
if (isAllDefined(value)) {
|
||||
scope.$$postDigest(function() {
|
||||
if (isAllDefined(lastValue)) unwatch();
|
||||
});
|
||||
}
|
||||
}, objectEquality);
|
||||
|
||||
return unwatch;
|
||||
|
||||
function isAllDefined(value) {
|
||||
var allDefined = true;
|
||||
forEach(value, function(val) {
|
||||
if (!isDefined(val)) allDefined = false;
|
||||
});
|
||||
return allDefined;
|
||||
}
|
||||
function isAllDefined(value) {
|
||||
var allDefined = true;
|
||||
forEach(value, function(val) {
|
||||
if (!isDefined(val)) allDefined = false;
|
||||
});
|
||||
return allDefined;
|
||||
}
|
||||
|
||||
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
|
||||
@@ -1918,26 +1900,31 @@ function $ParseProvider() {
|
||||
var watchDelegate = parsedExpression.$$watchDelegate;
|
||||
var useInputs = false;
|
||||
|
||||
var regularWatch =
|
||||
watchDelegate !== oneTimeLiteralWatchDelegate &&
|
||||
watchDelegate !== oneTimeWatchDelegate;
|
||||
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
|
||||
|
||||
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
|
||||
function regularInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
|
||||
return interceptorFn(value, scope, locals);
|
||||
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = parsedExpression(scope, locals, assign, inputs);
|
||||
}
|
||||
|
||||
function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
|
||||
var result = interceptorFn(value, scope, locals);
|
||||
// we only return the interceptor's result if the
|
||||
// initial value is defined (for bind-once)
|
||||
return isDefined(value) ? result : value;
|
||||
};
|
||||
return isDone(value) ? result : value;
|
||||
}
|
||||
|
||||
// Propagate $$watchDelegates other then inputsWatchDelegate
|
||||
var fn = parsedExpression.oneTime ? oneTimeInterceptedExpression : regularInterceptedExpression;
|
||||
|
||||
// Propogate the literal/oneTime attributes
|
||||
fn.literal = parsedExpression.literal;
|
||||
fn.oneTime = parsedExpression.oneTime;
|
||||
|
||||
// Propagate or create inputs / $$watchDelegates
|
||||
useInputs = !parsedExpression.inputs;
|
||||
if (parsedExpression.$$watchDelegate &&
|
||||
parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
|
||||
fn.$$watchDelegate = parsedExpression.$$watchDelegate;
|
||||
if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
|
||||
fn.$$watchDelegate = watchDelegate;
|
||||
fn.inputs = parsedExpression.inputs;
|
||||
} else if (!interceptorFn.$stateful) {
|
||||
// If there is an interceptor, but no watchDelegate then treat the interceptor like
|
||||
|
||||
+6
-1
@@ -239,6 +239,7 @@ function $QProvider() {
|
||||
*
|
||||
* @description
|
||||
* Retrieves or overrides whether to generate an error when a rejected promise is not handled.
|
||||
* This feature is enabled by default.
|
||||
*
|
||||
* @param {boolean=} value Whether to generate an error when a rejected promise is not handled.
|
||||
* @returns {boolean|ng.$qProvider} Current value when called without a new value or self for
|
||||
@@ -380,7 +381,11 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
|
||||
if (!toCheck.pur) {
|
||||
toCheck.pur = true;
|
||||
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
|
||||
exceptionHandler(errorMessage);
|
||||
if (toCheck.value instanceof Error) {
|
||||
exceptionHandler(toCheck.value, errorMessage);
|
||||
} else {
|
||||
exceptionHandler(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-8
@@ -416,15 +416,21 @@ function $RootScopeProvider() {
|
||||
|
||||
if (!array) {
|
||||
array = scope.$$watchers = [];
|
||||
array.$$digestWatchIndex = -1;
|
||||
}
|
||||
// we use unshift since we use a while loop in $digest for speed.
|
||||
// the while loop reads in reverse order.
|
||||
array.unshift(watcher);
|
||||
array.$$digestWatchIndex++;
|
||||
incrementWatchersCount(this, 1);
|
||||
|
||||
return function deregisterWatch() {
|
||||
if (arrayRemove(array, watcher) >= 0) {
|
||||
var index = arrayRemove(array, watcher);
|
||||
if (index >= 0) {
|
||||
incrementWatchersCount(scope, -1);
|
||||
if (index < array.$$digestWatchIndex) {
|
||||
array.$$digestWatchIndex--;
|
||||
}
|
||||
}
|
||||
lastDirtyWatch = null;
|
||||
};
|
||||
@@ -757,7 +763,6 @@ function $RootScopeProvider() {
|
||||
$digest: function() {
|
||||
var watch, value, last, fn, get,
|
||||
watchers,
|
||||
length,
|
||||
dirty, ttl = TTL,
|
||||
next, current, target = this,
|
||||
watchLog = [],
|
||||
@@ -781,12 +786,13 @@ function $RootScopeProvider() {
|
||||
current = target;
|
||||
|
||||
// It's safe for asyncQueuePosition to be a local variable here because this loop can't
|
||||
// be reentered recursively. Calling $digest from a function passed to $applyAsync would
|
||||
// be reentered recursively. Calling $digest from a function passed to $evalAsync would
|
||||
// lead to a '$digest already in progress' error.
|
||||
for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
|
||||
try {
|
||||
asyncTask = asyncQueue[asyncQueuePosition];
|
||||
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
|
||||
fn = asyncTask.fn;
|
||||
fn(asyncTask.scope, asyncTask.locals);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
@@ -798,10 +804,10 @@ function $RootScopeProvider() {
|
||||
do { // "traverse the scopes" loop
|
||||
if ((watchers = current.$$watchers)) {
|
||||
// process our watches
|
||||
length = watchers.length;
|
||||
while (length--) {
|
||||
watchers.$$digestWatchIndex = watchers.length;
|
||||
while (watchers.$$digestWatchIndex--) {
|
||||
try {
|
||||
watch = watchers[length];
|
||||
watch = watchers[watchers.$$digestWatchIndex];
|
||||
// Most common watches are on primitives, in which case we can short
|
||||
// circuit it with === operator, only when === fails do we use .equals
|
||||
if (watch) {
|
||||
@@ -871,6 +877,10 @@ function $RootScopeProvider() {
|
||||
}
|
||||
}
|
||||
postDigestQueue.length = postDigestQueuePosition = 0;
|
||||
|
||||
// Check for changes to browser url that happened during the $digest
|
||||
// (for which no event is fired; e.g. via `history.pushState()`)
|
||||
$browser.$$checkUrlChange();
|
||||
},
|
||||
|
||||
|
||||
@@ -1016,7 +1026,7 @@ function $RootScopeProvider() {
|
||||
});
|
||||
}
|
||||
|
||||
asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
|
||||
asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
|
||||
},
|
||||
|
||||
$$postDigest: function(fn) {
|
||||
|
||||
+252
-182
@@ -16,12 +16,21 @@
|
||||
var $sceMinErr = minErr('$sce');
|
||||
|
||||
var SCE_CONTEXTS = {
|
||||
// HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding).
|
||||
HTML: 'html',
|
||||
|
||||
// Style statements or stylesheets. Currently unused in AngularJS.
|
||||
CSS: 'css',
|
||||
|
||||
// An URL used in a context where it does not refer to a resource that loads code. Currently
|
||||
// unused in AngularJS.
|
||||
URL: 'url',
|
||||
// RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
|
||||
// url. (e.g. ng-include, script src, templateUrl)
|
||||
|
||||
// RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as
|
||||
// code. (e.g. ng-include, script src binding, templateUrl)
|
||||
RESOURCE_URL: 'resourceUrl',
|
||||
|
||||
// Script. Currently unused in AngularJS.
|
||||
JS: 'js'
|
||||
};
|
||||
|
||||
@@ -83,6 +92,16 @@ function adjustMatchers(matchers) {
|
||||
* `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
|
||||
* Contextual Escaping (SCE)} services to AngularJS.
|
||||
*
|
||||
* For an overview of this service and the functionnality it provides in AngularJS, see the main
|
||||
* page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how
|
||||
* SCE works in their application, which shouldn't be needed in most cases.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* AngularJS strongly relies on contextual escaping for the security of bindings: disabling or
|
||||
* modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners,
|
||||
* changes to this service will also influence users, so be extra careful and document your changes.
|
||||
* </div>
|
||||
*
|
||||
* Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
|
||||
* the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
|
||||
* because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
|
||||
@@ -108,10 +127,14 @@ function adjustMatchers(matchers) {
|
||||
* @description
|
||||
*
|
||||
* The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
|
||||
* $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
|
||||
* that the URLs used for sourcing Angular templates are safe. Refer {@link
|
||||
* ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
|
||||
* $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}.
|
||||
*
|
||||
* The `$sceDelegateProvider` allows one to get/set the whitelists and blacklists used to ensure
|
||||
* that the URLs used for sourcing AngularJS templates and other script-running URLs are safe (all
|
||||
* places that use the `$sce.RESOURCE_URL` context). See
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist}
|
||||
* and
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist},
|
||||
*
|
||||
* For the general details about this service in Angular, read the main page for {@link ng.$sce
|
||||
* Strict Contextual Escaping (SCE)}.
|
||||
@@ -140,6 +163,13 @@ function adjustMatchers(matchers) {
|
||||
* ]);
|
||||
* });
|
||||
* ```
|
||||
* Note that an empty whitelist will block every resource URL from being loaded, and will require
|
||||
* you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates
|
||||
* requested by {@link ng.$templateRequest $templateRequest} that are present in
|
||||
* {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism
|
||||
* to populate your templates in that cache at config time, then it is a good idea to remove 'self'
|
||||
* from that whitelist. This helps to mitigate the security impact of certain types of issues, like
|
||||
* for instance attacker-controlled `ng-includes`.
|
||||
*/
|
||||
|
||||
function $SceDelegateProvider() {
|
||||
@@ -155,23 +185,23 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
* @return {Array} The currently set whitelist array.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** an empty whitelist array will block all URLs!
|
||||
* </div>
|
||||
*
|
||||
* @return {Array} the currently set whitelist array.
|
||||
* @description
|
||||
* Sets/Gets the whitelist of trusted resource URLs.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is `['self']` allowing only
|
||||
* same origin resource requests.
|
||||
*
|
||||
* @description
|
||||
* Sets/Gets the whitelist of trusted resource URLs.
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** the default whitelist of 'self' is not recommended if your app shares its origin
|
||||
* with other apps! It is a good idea to limit it to only your application's directory.
|
||||
* </div>
|
||||
*/
|
||||
this.resourceUrlWhitelist = function(value) {
|
||||
if (arguments.length) {
|
||||
@@ -186,25 +216,23 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.</p><p>
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.</p><p>
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
* </p><p>
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
*
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
*
|
||||
* @return {Array} the currently set blacklist array.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
|
||||
* is no blacklist.)
|
||||
* @return {Array} The currently set blacklist array.
|
||||
*
|
||||
* @description
|
||||
* Sets/Gets the blacklist of trusted resource URLs.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
|
||||
* is no blacklist.)
|
||||
*/
|
||||
|
||||
this.resourceUrlBlacklist = function(value) {
|
||||
@@ -288,17 +316,24 @@ function $SceDelegateProvider() {
|
||||
* @name $sceDelegate#trustAs
|
||||
*
|
||||
* @description
|
||||
* Returns an object that is trusted by angular for use in specified strict
|
||||
* contextual escaping contexts (such as ng-bind-html, ng-include, any src
|
||||
* attribute interpolation, any dom event binding attribute interpolation
|
||||
* such as for onclick, etc.) that uses the provided value.
|
||||
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
|
||||
* Returns a trusted representation of the parameter for the specified context. This trusted
|
||||
* object will later on be used as-is, without any security check, by bindings or directives
|
||||
* that require this security context.
|
||||
* For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass
|
||||
* the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as
|
||||
* `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the
|
||||
* sanitizer loaded, passing the value itself will render all the HTML that does not pose a
|
||||
* security risk.
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||||
* resourceUrl, html, js and css.
|
||||
* @param {*} value The value that that should be considered trusted/safe.
|
||||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||||
* where Angular expects a $sce.trustAs() return value.
|
||||
* See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those
|
||||
* trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual
|
||||
* escaping.
|
||||
*
|
||||
* @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
|
||||
* `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
|
||||
*
|
||||
* @param {*} value The value that should be considered trusted.
|
||||
* @return {*} A trusted representation of value, that can be used in the given context.
|
||||
*/
|
||||
function trustAs(type, trustedValue) {
|
||||
var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||||
@@ -330,11 +365,11 @@ function $SceDelegateProvider() {
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
|
||||
*
|
||||
* If the passed parameter is not a value that had been returned by {@link
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is.
|
||||
*
|
||||
* @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
|
||||
* call or anything else.
|
||||
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* call or anything else.
|
||||
* @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
||||
* `value` unchanged.
|
||||
*/
|
||||
@@ -351,33 +386,38 @@ function $SceDelegateProvider() {
|
||||
* @name $sceDelegate#getTrusted
|
||||
*
|
||||
* @description
|
||||
* Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
|
||||
* returns the originally supplied value if the queried context type is a supertype of the
|
||||
* created type. If this condition isn't satisfied, throws an exception.
|
||||
* Takes any input, and either returns a value that's safe to use in the specified context, or
|
||||
* throws an exception.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
|
||||
* (XSS) vulnerability in your application.
|
||||
* </div>
|
||||
* In practice, there are several cases. When given a string, this function runs checks
|
||||
* and sanitization to make it safe without prior assumptions. When given the result of a {@link
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied
|
||||
* value if that value's context is valid for this call's context. Finally, this function can
|
||||
* also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization
|
||||
* is available or possible.)
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is to be used.
|
||||
* @param {string} type The context in which this value is to be used (such as `$sce.HTML`).
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} call.
|
||||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
|
||||
* `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.)
|
||||
* @return {*} A version of the value that's safe to use in the given context, or throws an
|
||||
* exception if this is impossible.
|
||||
*/
|
||||
function getTrusted(type, maybeTrusted) {
|
||||
if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
|
||||
return maybeTrusted;
|
||||
}
|
||||
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||||
// If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return
|
||||
// as-is.
|
||||
if (constructor && maybeTrusted instanceof constructor) {
|
||||
return maybeTrusted.$$unwrapTrustedValue();
|
||||
}
|
||||
// If we get here, then we may only take one of two actions.
|
||||
// 1. sanitize the value for the requested type, or
|
||||
// 2. throw an exception.
|
||||
// Otherwise, if we get here, then we may either make it safe, or throw an exception. This
|
||||
// depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL),
|
||||
// some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS
|
||||
// has no corresponding sinks.
|
||||
if (type === SCE_CONTEXTS.RESOURCE_URL) {
|
||||
// RESOURCE_URL uses a whitelist.
|
||||
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
|
||||
return maybeTrusted;
|
||||
} else {
|
||||
@@ -386,8 +426,10 @@ function $SceDelegateProvider() {
|
||||
maybeTrusted.toString());
|
||||
}
|
||||
} else if (type === SCE_CONTEXTS.HTML) {
|
||||
// htmlSanitizer throws its own error when no sanitizer is available.
|
||||
return htmlSanitizer(maybeTrusted);
|
||||
}
|
||||
// Default error when the $sce service has no way to make the input safe.
|
||||
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||||
}
|
||||
|
||||
@@ -423,21 +465,27 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* # Strict Contextual Escaping
|
||||
*
|
||||
* Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
|
||||
* contexts to result in a value that is marked as safe to use for that context. One example of
|
||||
* such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
|
||||
* to these contexts as privileged or SCE contexts.
|
||||
* Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render
|
||||
* trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and
|
||||
* (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
|
||||
*
|
||||
* As of version 1.2, Angular ships with SCE enabled by default.
|
||||
* ## Overview
|
||||
*
|
||||
* Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
|
||||
* one to execute arbitrary javascript by the use of the expression() syntax. Refer
|
||||
* <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
|
||||
* You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
|
||||
* to the top of your HTML document.
|
||||
* To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in
|
||||
* HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically
|
||||
* run security checks on them (sanitizations, whitelists, depending on context), or throw when it
|
||||
* cannot guarantee the security of the result. That behavior depends strongly on contexts: HTML
|
||||
* can be sanitized, but template URLs cannot, for instance.
|
||||
*
|
||||
* SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
|
||||
* security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
|
||||
* To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML:
|
||||
* we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it
|
||||
* before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and
|
||||
* render the input as-is, you will need to mark it as trusted for that context before attempting
|
||||
* to bind it.
|
||||
*
|
||||
* As of version 1.2, AngularJS ships with SCE enabled by default.
|
||||
*
|
||||
* ## In practice
|
||||
*
|
||||
* Here's an example of a binding in a privileged context:
|
||||
*
|
||||
@@ -447,10 +495,10 @@ function $SceDelegateProvider() {
|
||||
* ```
|
||||
*
|
||||
* Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
|
||||
* disabled, this application allows the user to render arbitrary HTML into the DIV.
|
||||
* In a more realistic example, one may be rendering user comments, blog articles, etc. via
|
||||
* bindings. (HTML is just one example of a context where rendering user controlled input creates
|
||||
* security vulnerabilities.)
|
||||
* disabled, this application allows the user to render arbitrary HTML into the DIV, which would
|
||||
* be an XSS security bug. In a more realistic example, one may be rendering user comments, blog
|
||||
* articles, etc. via bindings. (HTML is just one example of a context where rendering user
|
||||
* controlled input creates security vulnerabilities.)
|
||||
*
|
||||
* For the case of HTML, you might use a library, either on the client side, or on the server side,
|
||||
* to sanitize unsafe HTML before binding to the value and rendering it in the document.
|
||||
@@ -460,25 +508,29 @@ function $SceDelegateProvider() {
|
||||
* ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
|
||||
* properties/fields and forgot to update the binding to the sanitized value?
|
||||
*
|
||||
* To be secure by default, you want to ensure that any such bindings are disallowed unless you can
|
||||
* determine that something explicitly says it's safe to use a value for binding in that
|
||||
* context. You can then audit your code (a simple grep would do) to ensure that this is only done
|
||||
* for those values that you can easily tell are safe - because they were received from your server,
|
||||
* sanitized by your library, etc. You can organize your codebase to help with this - perhaps
|
||||
* allowing only the files in a specific directory to do this. Ensuring that the internal API
|
||||
* exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
|
||||
* To be secure by default, AngularJS makes sure bindings go through that sanitization, or
|
||||
* any similar validation process, unless there's a good reason to trust the given value in this
|
||||
* context. That trust is formalized with a function call. This means that as a developer, you
|
||||
* can assume all untrusted bindings are safe. Then, to audit your code for binding security issues,
|
||||
* you just need to ensure the values you mark as trusted indeed are safe - because they were
|
||||
* received from your server, sanitized by your library, etc. You can organize your codebase to
|
||||
* help with this - perhaps allowing only the files in a specific directory to do this.
|
||||
* Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then
|
||||
* becomes a more manageable task.
|
||||
*
|
||||
* In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
|
||||
* (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
|
||||
* obtain values that will be accepted by SCE / privileged contexts.
|
||||
*
|
||||
* build the trusted versions of your values.
|
||||
*
|
||||
* ## How does it work?
|
||||
*
|
||||
* In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
|
||||
* $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
|
||||
* ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
|
||||
* {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
|
||||
* $sce.getTrusted(context, value)} rather than to the value directly. Think of this function as
|
||||
* a way to enforce the required security context in your data sink. Directives use {@link
|
||||
* ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs
|
||||
* the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also,
|
||||
* when binding without directives, AngularJS will understand the context of your bindings
|
||||
* automatically.
|
||||
*
|
||||
* As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
|
||||
* ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
|
||||
@@ -519,11 +571,12 @@ function $SceDelegateProvider() {
|
||||
* It's important to remember that SCE only applies to interpolation expressions.
|
||||
*
|
||||
* If your expressions are constant literals, they're automatically trusted and you don't need to
|
||||
* call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
|
||||
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
|
||||
*
|
||||
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||||
* through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
|
||||
* call `$sce.trustAs` on them (e.g.
|
||||
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. The `$sceDelegate` will
|
||||
* also use the `$sanitize` service if it is available when binding untrusted values to
|
||||
* `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you
|
||||
* wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in
|
||||
* your application.
|
||||
*
|
||||
* The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
|
||||
* templates in `ng-include` from your application's domain without having to even know about SCE.
|
||||
@@ -541,11 +594,17 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* | Context | Notes |
|
||||
* |---------------------|----------------|
|
||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
|
||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
|
||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
|
||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered, and the {@link ngSanitize.$sanitize $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. |
|
||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=`, `<img src=`, and some others sanitize their urls and don't constitute an SCE context.) |
|
||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. |
|
||||
*
|
||||
*
|
||||
* Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||||
* through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings
|
||||
* in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This
|
||||
* might evolve.
|
||||
*
|
||||
* ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
|
||||
*
|
||||
@@ -664,14 +723,15 @@ function $SceDelegateProvider() {
|
||||
* for little coding overhead. It will be much harder to take an SCE disabled application and
|
||||
* either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
|
||||
* for cases where you have a lot of existing code that was written before SCE was introduced and
|
||||
* you're migrating them a module at a time.
|
||||
* you're migrating them a module at a time. Also do note that this is an app-wide setting, so if
|
||||
* you are writing a library, you will cause security bugs applications using it.
|
||||
*
|
||||
* That said, here's how you can completely disable SCE:
|
||||
*
|
||||
* ```
|
||||
* angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
|
||||
* // Completely disable SCE. For demonstration purposes only!
|
||||
* // Do not use in new projects.
|
||||
* // Do not use in new projects or libraries.
|
||||
* $sceProvider.enabled(false);
|
||||
* });
|
||||
* ```
|
||||
@@ -686,8 +746,8 @@ function $SceProvider() {
|
||||
* @name $sceProvider#enabled
|
||||
* @kind function
|
||||
*
|
||||
* @param {boolean=} value If provided, then enables/disables SCE.
|
||||
* @return {boolean} true if SCE is enabled, false otherwise.
|
||||
* @param {boolean=} value If provided, then enables/disables SCE application-wide.
|
||||
* @return {boolean} True if SCE is enabled, false otherwise.
|
||||
*
|
||||
* @description
|
||||
* Enables/disables SCE and returns the current value.
|
||||
@@ -741,9 +801,9 @@ function $SceProvider() {
|
||||
* getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
|
||||
* will also succeed.
|
||||
*
|
||||
* Inheritance happens to capture this in a natural way. In some future, we
|
||||
* may not use inheritance anymore. That is OK because no code outside of
|
||||
* sce.js and sceSpecs.js would need to be aware of this detail.
|
||||
* Inheritance happens to capture this in a natural way. In some future, we may not use
|
||||
* inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to
|
||||
* be aware of this detail.
|
||||
*/
|
||||
|
||||
this.$get = ['$parse', '$sceDelegate', function(
|
||||
@@ -765,8 +825,8 @@ function $SceProvider() {
|
||||
* @name $sce#isEnabled
|
||||
* @kind function
|
||||
*
|
||||
* @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
|
||||
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
|
||||
* @return {Boolean} True if SCE is enabled, false otherwise. If you want to set the value, you
|
||||
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
|
||||
*
|
||||
* @description
|
||||
* Returns a boolean indicating if SCE is enabled.
|
||||
@@ -793,14 +853,14 @@ function $SceProvider() {
|
||||
* wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
|
||||
* *result*)}
|
||||
*
|
||||
* @param {string} type The kind of SCE context in which this result will be used.
|
||||
* @param {string} type The SCE context in which this result will be used.
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
sce.parseAs = function sceParseAs(type, expr) {
|
||||
var parsed = $parse(expr);
|
||||
@@ -818,18 +878,18 @@ function $SceProvider() {
|
||||
* @name $sce#trustAs
|
||||
*
|
||||
* @description
|
||||
* Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
|
||||
* returns an object that is trusted by angular for use in specified strict contextual
|
||||
* escaping contexts (such as ng-bind-html, ng-include, any src attribute
|
||||
* interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
|
||||
* that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
|
||||
* escaping.
|
||||
* Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a
|
||||
* wrapped object that represents your value, and the trust you have in its safety for the given
|
||||
* context. AngularJS can then use that value as-is in bindings of the specified secure context.
|
||||
* This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute
|
||||
* interpolations. See {@link ng.$sce $sce} for strict contextual escaping.
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||||
* resourceUrl, html, js and css.
|
||||
* @param {*} value The value that that should be considered trusted/safe.
|
||||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||||
* where Angular expects a $sce.trustAs() return value.
|
||||
* @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
|
||||
* `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
|
||||
*
|
||||
* @param {*} value The value that that should be considered trusted.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in the context you specified.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -840,11 +900,23 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsHtml(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
|
||||
* $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.HTML` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.HTML` context (like `ng-bind-html`).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $sce#trustAsCss
|
||||
*
|
||||
* @description
|
||||
* Shorthand method. `$sce.trustAsCss(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`}
|
||||
*
|
||||
* @param {*} value The value to mark as trusted for `$sce.CSS` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant
|
||||
* of your `value` in `$sce.CSS` context. This context is currently unused, so there are
|
||||
* almost no reasons to use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -855,11 +927,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsUrl(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
|
||||
* $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.URL` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.URL` context. That context is currently unused, so there are almost no reasons
|
||||
* to use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -870,11 +941,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsResourceUrl(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
|
||||
* $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the return
|
||||
* value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute
|
||||
* bindings, ...)
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -885,11 +955,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsJs(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
|
||||
* $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.JS` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.JS` context. That context is currently unused, so there are almost no reasons to
|
||||
* use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -898,16 +967,17 @@ function $SceProvider() {
|
||||
*
|
||||
* @description
|
||||
* Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
|
||||
* takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
|
||||
* originally supplied value if the queried context type is a supertype of the created type.
|
||||
* If this condition isn't satisfied, throws an exception.
|
||||
* takes any input, and either returns a value that's safe to use in the specified context,
|
||||
* or throws an exception. This function is aware of trusted values created by the `trustAs`
|
||||
* function and its shorthands, and when contexts are appropriate, returns the unwrapped value
|
||||
* as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a
|
||||
* safe value (e.g., no sanitization is available or possible.)
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is to be used.
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
|
||||
* call.
|
||||
* @returns {*} The value the was originally provided to
|
||||
* {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
|
||||
* Otherwise, throws an exception.
|
||||
* @param {string} type The context in which this value is to be used.
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs
|
||||
* `$sce.trustAs`} call, or anything else (which will not be considered trusted.)
|
||||
* @return {*} A version of the value that's safe to use in the given context, or throws an
|
||||
* exception if this is impossible.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -919,7 +989,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.HTML, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -931,7 +1001,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.CSS, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -943,7 +1013,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.URL, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -955,7 +1025,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sceDelegate.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -967,7 +1037,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.JS, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -979,12 +1049,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -996,12 +1066,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1013,12 +1083,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1030,12 +1100,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1047,12 +1117,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
// Shorthand delegations.
|
||||
|
||||
@@ -25,7 +25,10 @@ function $SnifferProvider() {
|
||||
// (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
|
||||
// the presence of an extension runtime ID and the absence of other Chrome runtime APIs
|
||||
// (see https://developer.chrome.com/apps/manifest/sandbox).
|
||||
// (NW.js apps have access to Chrome APIs, but do support `history`.)
|
||||
isNw = $window.nw && $window.nw.process,
|
||||
isChromePackagedApp =
|
||||
!isNw &&
|
||||
$window.chrome &&
|
||||
($window.chrome.app && $window.chrome.app.runtime ||
|
||||
!$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ var originUrl = urlResolve(window.location.href);
|
||||
* URL will be resolved into an absolute URL in the context of the application document.
|
||||
* Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
|
||||
* properties are all populated to reflect the normalized URL. This approach has wide
|
||||
* compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
|
||||
* compatibility - Safari 1+, Mozilla 1+ etc. See
|
||||
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
|
||||
*
|
||||
* Implementation Notes for IE
|
||||
|
||||
@@ -36,9 +36,9 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
|
||||
function isAllowed(ruleType, currentAnimation, previousAnimation) {
|
||||
return rules[ruleType].some(function(fn) {
|
||||
return fn(element, currentAnimation, previousAnimation);
|
||||
return fn(currentAnimation, previousAnimation);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,40 +48,40 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return and ? a && b : a || b;
|
||||
}
|
||||
|
||||
rules.join.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.join.push(function(newAnimation, currentAnimation) {
|
||||
// if the new animation is class-based then we can just tack that on
|
||||
return !newAnimation.structural && hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// there is no need to animate anything if no classes are being added and
|
||||
// there is no structural animation that will be triggered
|
||||
return !newAnimation.structural && !hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// why should we trigger a new structural animation if the element will
|
||||
// be removed from the DOM anyway?
|
||||
return currentAnimation.event === 'leave' && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// if there is an ongoing current animation then don't even bother running the class-based animation
|
||||
return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(newAnimation, currentAnimation) {
|
||||
// there can never be two structural animations running at the same time
|
||||
return currentAnimation.structural && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(newAnimation, currentAnimation) {
|
||||
// if the previous animation is already running, but the new animation will
|
||||
// be triggered, but the new animation is structural
|
||||
return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(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
|
||||
|
||||
@@ -100,15 +100,15 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
|
||||
});
|
||||
|
||||
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
|
||||
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$Map',
|
||||
'$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
|
||||
'$$isDocumentHidden',
|
||||
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
|
||||
function($$rAF, $rootScope, $rootElement, $document, $$Map,
|
||||
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow,
|
||||
$$isDocumentHidden) {
|
||||
|
||||
var activeAnimationsLookup = new $$HashMap();
|
||||
var disabledElementsLookup = new $$HashMap();
|
||||
var activeAnimationsLookup = new $$Map();
|
||||
var disabledElementsLookup = new $$Map();
|
||||
var animationsEnabled = null;
|
||||
|
||||
function postDigestTaskFactory() {
|
||||
@@ -181,10 +181,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
|
||||
};
|
||||
|
||||
function findCallbacks(parent, element, event) {
|
||||
var targetNode = getDomNode(element);
|
||||
var targetParentNode = getDomNode(parent);
|
||||
|
||||
function findCallbacks(targetParentNode, targetNode, event) {
|
||||
var matches = [];
|
||||
var entries = callbackRegistry[event];
|
||||
if (entries) {
|
||||
@@ -209,11 +206,11 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupEventListeners(phase, element) {
|
||||
if (phase === 'close' && !element[0].parentNode) {
|
||||
function cleanupEventListeners(phase, node) {
|
||||
if (phase === 'close' && !node.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);
|
||||
$animate.off(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +291,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
bool = !disabledElementsLookup.get(node);
|
||||
} else {
|
||||
// (element, bool) - Element setter
|
||||
disabledElementsLookup.put(node, !bool);
|
||||
disabledElementsLookup.set(node, !bool);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,18 +302,15 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
return $animate;
|
||||
|
||||
function queueAnimation(element, event, initialOptions) {
|
||||
function queueAnimation(originalElement, event, initialOptions) {
|
||||
// we always make a copy of the options since
|
||||
// there should never be any side effects on
|
||||
// the input data when running `$animateCss`.
|
||||
var options = copy(initialOptions);
|
||||
|
||||
var node, parent;
|
||||
element = stripCommentsFromElement(element);
|
||||
if (element) {
|
||||
node = getDomNode(element);
|
||||
parent = element.parent();
|
||||
}
|
||||
var element = stripCommentsFromElement(originalElement);
|
||||
var node = getDomNode(element);
|
||||
var parentNode = node && node.parentNode;
|
||||
|
||||
options = prepareAnimationOptions(options);
|
||||
|
||||
@@ -381,7 +375,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// there is no point in traversing the same collection of parent ancestors if a followup
|
||||
// animation will be run on the same element that already did all that checking work
|
||||
if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) {
|
||||
skipAnimations = !areAnimationsAllowed(element, parent, event);
|
||||
skipAnimations = !areAnimationsAllowed(node, parentNode, event);
|
||||
}
|
||||
|
||||
if (skipAnimations) {
|
||||
@@ -393,7 +387,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
|
||||
if (isStructural) {
|
||||
closeChildAnimations(element);
|
||||
closeChildAnimations(node);
|
||||
}
|
||||
|
||||
var newAnimation = {
|
||||
@@ -408,7 +402,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
};
|
||||
|
||||
if (hasExistingAnimation) {
|
||||
var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
|
||||
var skipAnimationFlag = isAllowed('skip', newAnimation, existingAnimation);
|
||||
if (skipAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
close();
|
||||
@@ -418,7 +412,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return existingAnimation.runner;
|
||||
}
|
||||
}
|
||||
var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
|
||||
var cancelAnimationFlag = isAllowed('cancel', newAnimation, existingAnimation);
|
||||
if (cancelAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
// this will end the animation right away and it is safe
|
||||
@@ -440,7 +434,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// a joined animation means that this animation will take over the existing one
|
||||
// so an example would involve a leave animation taking over an enter. Then when
|
||||
// the postDigest kicks in the enter will be ignored.
|
||||
var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
|
||||
var joinAnimationFlag = isAllowed('join', newAnimation, existingAnimation);
|
||||
if (joinAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
normalizeAnimationDetails(element, newAnimation);
|
||||
@@ -474,7 +468,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
if (!isValidAnimation) {
|
||||
close();
|
||||
clearElementAnimationState(element);
|
||||
clearElementAnimationState(node);
|
||||
return runner;
|
||||
}
|
||||
|
||||
@@ -482,9 +476,18 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
var counter = (existingAnimation.counter || 0) + 1;
|
||||
newAnimation.counter = counter;
|
||||
|
||||
markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
|
||||
markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation);
|
||||
|
||||
$rootScope.$$postDigest(function() {
|
||||
// It is possible that the DOM nodes inside `originalElement` have been replaced. This can
|
||||
// happen if the animated element is a transcluded clone and also has a `templateUrl`
|
||||
// directive on it. Therefore, we must recreate `element` in order to interact with the
|
||||
// actual DOM nodes.
|
||||
// Note: We still need to use the old `node` for certain things, such as looking up in
|
||||
// HashMaps where it was used as the key.
|
||||
|
||||
element = stripCommentsFromElement(originalElement);
|
||||
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
var animationCancelled = !animationDetails;
|
||||
animationDetails = animationDetails || {};
|
||||
@@ -523,7 +526,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// isn't allowed to animate from here then we need to clear the state of the element
|
||||
// so that any future animations won't read the expired animation data.
|
||||
if (!isValidAnimation) {
|
||||
clearElementAnimationState(element);
|
||||
clearElementAnimationState(node);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -535,7 +538,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
? 'setClass'
|
||||
: animationDetails.event;
|
||||
|
||||
markElementAnimationState(element, RUNNING_STATE);
|
||||
markElementAnimationState(node, RUNNING_STATE);
|
||||
var realRunner = $$animation(element, event, animationDetails.options);
|
||||
|
||||
// this will update the runner's flow-control events based on
|
||||
@@ -547,7 +550,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
close(!status);
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
if (animationDetails && animationDetails.counter === counter) {
|
||||
clearElementAnimationState(getDomNode(element));
|
||||
clearElementAnimationState(node);
|
||||
}
|
||||
notifyProgress(runner, event, 'close', {});
|
||||
});
|
||||
@@ -557,7 +560,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
function notifyProgress(runner, event, phase, data) {
|
||||
runInNextPostDigestOrNow(function() {
|
||||
var callbacks = findCallbacks(parent, element, event);
|
||||
var callbacks = findCallbacks(parentNode, node, event);
|
||||
if (callbacks.length) {
|
||||
// do not optimize this call here to RAF because
|
||||
// we don't know how heavy the callback code here will
|
||||
@@ -567,10 +570,10 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
forEach(callbacks, function(callback) {
|
||||
callback(element, phase, data);
|
||||
});
|
||||
cleanupEventListeners(phase, element);
|
||||
cleanupEventListeners(phase, node);
|
||||
});
|
||||
} else {
|
||||
cleanupEventListeners(phase, element);
|
||||
cleanupEventListeners(phase, node);
|
||||
}
|
||||
});
|
||||
runner.progress(event, phase, data);
|
||||
@@ -585,8 +588,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
}
|
||||
|
||||
function closeChildAnimations(element) {
|
||||
var node = getDomNode(element);
|
||||
function closeChildAnimations(node) {
|
||||
var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
|
||||
forEach(children, function(child) {
|
||||
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
|
||||
@@ -597,21 +599,16 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
animationDetails.runner.end();
|
||||
/* falls through */
|
||||
case PRE_DIGEST_STATE:
|
||||
activeAnimationsLookup.remove(child);
|
||||
activeAnimationsLookup.delete(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearElementAnimationState(element) {
|
||||
var node = getDomNode(element);
|
||||
function clearElementAnimationState(node) {
|
||||
node.removeAttribute(NG_ANIMATE_ATTR_NAME);
|
||||
activeAnimationsLookup.remove(node);
|
||||
}
|
||||
|
||||
function isMatchingElement(nodeOrElmA, nodeOrElmB) {
|
||||
return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
|
||||
activeAnimationsLookup.delete(node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -621,54 +618,54 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
* c) the element is not a child of the body
|
||||
* d) the element is not a child of the $rootElement
|
||||
*/
|
||||
function areAnimationsAllowed(element, parentElement, event) {
|
||||
var bodyElement = jqLite($document[0].body);
|
||||
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
|
||||
var rootElementDetected = isMatchingElement(element, $rootElement);
|
||||
var parentAnimationDetected = false;
|
||||
var animateChildren;
|
||||
var elementDisabled = disabledElementsLookup.get(getDomNode(element));
|
||||
function areAnimationsAllowed(node, parentNode, event) {
|
||||
var bodyNode = $document[0].body;
|
||||
var rootNode = getDomNode($rootElement);
|
||||
|
||||
var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
|
||||
var bodyNodeDetected = (node === bodyNode) || node.nodeName === 'HTML';
|
||||
var rootNodeDetected = (node === rootNode);
|
||||
var parentAnimationDetected = false;
|
||||
var elementDisabled = disabledElementsLookup.get(node);
|
||||
var animateChildren;
|
||||
|
||||
var parentHost = jqLite.data(node, NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
parentElement = parentHost;
|
||||
parentNode = getDomNode(parentHost);
|
||||
}
|
||||
|
||||
parentElement = getDomNode(parentElement);
|
||||
|
||||
while (parentElement) {
|
||||
if (!rootElementDetected) {
|
||||
while (parentNode) {
|
||||
if (!rootNodeDetected) {
|
||||
// angular doesn't want to attempt to animate elements outside of the application
|
||||
// therefore we need to ensure that the rootElement is an ancestor of the current element
|
||||
rootElementDetected = isMatchingElement(parentElement, $rootElement);
|
||||
rootNodeDetected = (parentNode === rootNode);
|
||||
}
|
||||
|
||||
if (parentElement.nodeType !== ELEMENT_NODE) {
|
||||
if (parentNode.nodeType !== ELEMENT_NODE) {
|
||||
// no point in inspecting the #document element
|
||||
break;
|
||||
}
|
||||
|
||||
var details = activeAnimationsLookup.get(parentElement) || {};
|
||||
var details = activeAnimationsLookup.get(parentNode) || {};
|
||||
// either an enter, leave or move animation will commence
|
||||
// therefore we can't allow any animations to take place
|
||||
// but if a parent animation is class-based then that's ok
|
||||
if (!parentAnimationDetected) {
|
||||
var parentElementDisabled = disabledElementsLookup.get(parentElement);
|
||||
var parentNodeDisabled = disabledElementsLookup.get(parentNode);
|
||||
|
||||
if (parentElementDisabled === true && elementDisabled !== false) {
|
||||
if (parentNodeDisabled === true && elementDisabled !== false) {
|
||||
// disable animations if the user hasn't explicitly enabled animations on the
|
||||
// current element
|
||||
elementDisabled = true;
|
||||
// element is disabled via parent element, no need to check anything else
|
||||
break;
|
||||
} else if (parentElementDisabled === false) {
|
||||
} else if (parentNodeDisabled === false) {
|
||||
elementDisabled = false;
|
||||
}
|
||||
parentAnimationDetected = details.structural;
|
||||
}
|
||||
|
||||
if (isUndefined(animateChildren) || animateChildren === true) {
|
||||
var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
|
||||
var value = jqLite.data(parentNode, NG_ANIMATE_CHILDREN_DATA);
|
||||
if (isDefined(value)) {
|
||||
animateChildren = value;
|
||||
}
|
||||
@@ -677,47 +674,46 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// there is no need to continue traversing at this point
|
||||
if (parentAnimationDetected && animateChildren === false) break;
|
||||
|
||||
if (!bodyElementDetected) {
|
||||
if (!bodyNodeDetected) {
|
||||
// we also need to ensure that the element is or will be a part of the body element
|
||||
// otherwise it is pointless to even issue an animation to be rendered
|
||||
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
|
||||
bodyNodeDetected = (parentNode === bodyNode);
|
||||
}
|
||||
|
||||
if (bodyElementDetected && rootElementDetected) {
|
||||
if (bodyNodeDetected && rootNodeDetected) {
|
||||
// If both body and root have been found, any other checks are pointless,
|
||||
// as no animation data should live outside the application
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rootElementDetected) {
|
||||
// If no rootElement is detected, check if the parentElement is pinned to another element
|
||||
parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
|
||||
if (!rootNodeDetected) {
|
||||
// If `rootNode` is not detected, check if `parentNode` is pinned to another element
|
||||
parentHost = jqLite.data(parentNode, NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
// The pin target element becomes the next parent element
|
||||
parentElement = getDomNode(parentHost);
|
||||
parentNode = getDomNode(parentHost);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
parentElement = parentElement.parentNode;
|
||||
parentNode = parentNode.parentNode;
|
||||
}
|
||||
|
||||
var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
|
||||
return allowAnimation && rootElementDetected && bodyElementDetected;
|
||||
return allowAnimation && rootNodeDetected && bodyNodeDetected;
|
||||
}
|
||||
|
||||
function markElementAnimationState(element, state, details) {
|
||||
function markElementAnimationState(node, state, details) {
|
||||
details = details || {};
|
||||
details.state = state;
|
||||
|
||||
var node = getDomNode(element);
|
||||
node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
|
||||
|
||||
var oldValue = activeAnimationsLookup.get(node);
|
||||
var newValue = oldValue
|
||||
? extend(oldValue, details)
|
||||
: details;
|
||||
activeAnimationsLookup.put(node, newValue);
|
||||
activeAnimationsLookup.set(node, newValue);
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
@@ -21,21 +21,21 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro
|
||||
return element.data(RUNNER_STORAGE_KEY);
|
||||
}
|
||||
|
||||
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
|
||||
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
|
||||
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$Map', '$$rAFScheduler',
|
||||
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$Map, $$rAFScheduler) {
|
||||
|
||||
var animationQueue = [];
|
||||
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
|
||||
|
||||
function sortAnimations(animations) {
|
||||
var tree = { children: [] };
|
||||
var i, lookup = new $$HashMap();
|
||||
var i, lookup = new $$Map();
|
||||
|
||||
// this is done first beforehand so that the hashmap
|
||||
// this is done first beforehand so that the map
|
||||
// is filled with a list of the elements that will be animated
|
||||
for (i = 0; i < animations.length; i++) {
|
||||
var animation = animations[i];
|
||||
lookup.put(animation.domNode, animations[i] = {
|
||||
lookup.set(animation.domNode, animations[i] = {
|
||||
domNode: animation.domNode,
|
||||
fn: animation.fn,
|
||||
children: []
|
||||
@@ -54,7 +54,7 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro
|
||||
|
||||
var elementNode = entry.domNode;
|
||||
var parentNode = elementNode.parentNode;
|
||||
lookup.put(elementNode, entry);
|
||||
lookup.set(elementNode, entry);
|
||||
|
||||
var parentEntry;
|
||||
while (parentNode) {
|
||||
|
||||
@@ -177,6 +177,10 @@
|
||||
* /* As of 1.4.4, this must always be set: it signals ngAnimate
|
||||
* to not accidentally inherit a delay property from another CSS class */
|
||||
* transition-duration: 0s;
|
||||
*
|
||||
* /* if you are using animations instead of transitions you should configure as follows:
|
||||
* animation-delay: 0.1s;
|
||||
* animation-duration: 0s; */
|
||||
* }
|
||||
* .my-animation.ng-enter.ng-enter-active {
|
||||
* /* standard transition styles */
|
||||
@@ -756,6 +760,7 @@ angular.module('ngAnimate', [], function initAngularHelpers() {
|
||||
isFunction = angular.isFunction;
|
||||
isElement = angular.isElement;
|
||||
})
|
||||
.info({ angularVersion: '"NG_VERSION_FULL"' })
|
||||
.directive('ngAnimateSwap', ngAnimateSwapDirective)
|
||||
|
||||
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
|
||||
|
||||
+2
-1
@@ -54,6 +54,7 @@
|
||||
* {@link guide/accessibility Developer Guide}.
|
||||
*/
|
||||
var ngAriaModule = angular.module('ngAria', ['ng']).
|
||||
info({ angularVersion: '"NG_VERSION_FULL"' }).
|
||||
provider('$aria', $AriaProvider);
|
||||
|
||||
/**
|
||||
@@ -355,7 +356,7 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function(elem, attr) {
|
||||
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
|
||||
var fn = $parse(attr.ngClick);
|
||||
return function(scope, elem, attr) {
|
||||
|
||||
if (!isNodeOneOf(elem, nodeBlackList)) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
info({ angularVersion: '"NG_VERSION_FULL"' }).
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $cookiesProvider
|
||||
|
||||
@@ -216,6 +216,7 @@ var toJson;
|
||||
var $$stringify;
|
||||
|
||||
var module = window['angular']['module']('ngMessageFormat', ['ng']);
|
||||
module['info']({ 'angularVersion': '"NG_VERSION_FULL"' });
|
||||
module['factory']('$$messageFormat', $$MessageFormatFactory);
|
||||
module['config'](['$provide', function($provide) {
|
||||
$interpolateMinErr = window['angular']['$interpolateMinErr'];
|
||||
|
||||
@@ -267,6 +267,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
||||
isString = angular.isString;
|
||||
jqLite = angular.element;
|
||||
})
|
||||
.info({ angularVersion: '"NG_VERSION_FULL"' })
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
@@ -629,10 +630,8 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
* `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
|
||||
* value, it accepts an expression to be evaluated for the message key.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
|
||||
Vendored
+40
-22
@@ -37,10 +37,30 @@ angular.mock.$Browser = function() {
|
||||
self.$$lastUrl = self.$$url; // used by url polling fn
|
||||
self.pollFns = [];
|
||||
|
||||
// TODO(vojta): remove this temporary api
|
||||
self.$$completeOutstandingRequest = angular.noop;
|
||||
self.$$incOutstandingRequestCount = angular.noop;
|
||||
// Testability API
|
||||
|
||||
var outstandingRequestCount = 0;
|
||||
var outstandingRequestCallbacks = [];
|
||||
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
|
||||
self.$$completeOutstandingRequest = function(fn) {
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (!outstandingRequestCount) {
|
||||
while (outstandingRequestCallbacks.length) {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
if (outstandingRequestCount) {
|
||||
outstandingRequestCallbacks.push(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// register url polling fn
|
||||
|
||||
@@ -65,6 +85,8 @@ angular.mock.$Browser = function() {
|
||||
self.deferredNextId = 0;
|
||||
|
||||
self.defer = function(fn, delay) {
|
||||
// Note that we do not use `$$incOutstandingRequestCount` or `$$completeOutstandingRequest`
|
||||
// in this mock implementation.
|
||||
delay = delay || 0;
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
|
||||
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
|
||||
@@ -166,10 +188,6 @@ angular.mock.$Browser.prototype = {
|
||||
|
||||
state: function() {
|
||||
return this.$$state;
|
||||
},
|
||||
|
||||
notifyWhenNoOutstandingRequests: function(fn) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -772,6 +790,7 @@ angular.mock.TzDate.prototype = Date.prototype;
|
||||
* You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
|
||||
*/
|
||||
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
.info({ angularVersion: '"NG_VERSION_FULL"' })
|
||||
|
||||
.config(['$provide', function($provide) {
|
||||
|
||||
@@ -1114,6 +1133,8 @@ angular.mock.dump = function(object) {
|
||||
$http.get('/auth.py').then(function(response) {
|
||||
authToken = response.headers('A-Token');
|
||||
$scope.user = response.data;
|
||||
}).catch(function() {
|
||||
$scope.status = 'Failed...';
|
||||
});
|
||||
|
||||
$scope.saveMessage = function(message) {
|
||||
@@ -1295,9 +1316,8 @@ angular.mock.dump = function(object) {
|
||||
});
|
||||
```
|
||||
*/
|
||||
angular.mock.$HttpBackendProvider = function() {
|
||||
this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
|
||||
};
|
||||
angular.mock.$httpBackendDecorator =
|
||||
['$rootScope', '$timeout', '$delegate', createHttpBackendMock];
|
||||
|
||||
/**
|
||||
* General factory function for $httpBackend mock.
|
||||
@@ -1318,7 +1338,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
expectations = [],
|
||||
responses = [],
|
||||
responsesPush = angular.bind(responses, responses.push),
|
||||
copy = angular.copy;
|
||||
copy = angular.copy,
|
||||
// We cache the original backend so that if both ngMock and ngMockE2E override the
|
||||
// service the ngMockE2E version can pass through to the real backend
|
||||
originalHttpBackend = $delegate.$$originalHttpBackend || $delegate;
|
||||
|
||||
function createResponse(status, data, headers, statusText) {
|
||||
if (angular.isFunction(status)) return status;
|
||||
@@ -1403,7 +1426,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
// if $browser specified, we do auto flush all requests
|
||||
($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
|
||||
} else if (definition.passThrough) {
|
||||
$delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
|
||||
originalHttpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
|
||||
} else throw new Error('No response defined !');
|
||||
return;
|
||||
}
|
||||
@@ -1879,6 +1902,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
responses.length = 0;
|
||||
};
|
||||
|
||||
$httpBackend.$$originalHttpBackend = originalHttpBackend;
|
||||
|
||||
return $httpBackend;
|
||||
|
||||
|
||||
@@ -2376,7 +2401,6 @@ angular.module('ngMock', ['ng']).provider({
|
||||
$exceptionHandler: angular.mock.$ExceptionHandlerProvider,
|
||||
$log: angular.mock.$LogProvider,
|
||||
$interval: angular.mock.$IntervalProvider,
|
||||
$httpBackend: angular.mock.$HttpBackendProvider,
|
||||
$rootElement: angular.mock.$RootElementProvider,
|
||||
$componentController: angular.mock.$ComponentControllerProvider
|
||||
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
|
||||
@@ -2384,7 +2408,8 @@ angular.module('ngMock', ['ng']).provider({
|
||||
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
||||
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
|
||||
$provide.decorator('$controller', createControllerDecorator($compileProvider));
|
||||
}]);
|
||||
$provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator);
|
||||
}]).info({ angularVersion: '"NG_VERSION_FULL"' });
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
@@ -2398,9 +2423,8 @@ angular.module('ngMock', ['ng']).provider({
|
||||
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
||||
*/
|
||||
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
|
||||
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
||||
}]);
|
||||
}]).info({ angularVersion: '"NG_VERSION_FULL"' });
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
@@ -2964,12 +2988,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||
delete fn.$inject;
|
||||
});
|
||||
|
||||
angular.forEach(currentSpec.$modules, function(module) {
|
||||
if (module && module.$$hashKey) {
|
||||
module.$$hashKey = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
currentSpec.$injector = null;
|
||||
currentSpec.$modules = null;
|
||||
currentSpec.$providerInjector = null;
|
||||
|
||||
@@ -44,4 +44,5 @@ function isValidIdentifierContinue(ch, cp) {
|
||||
angular.module('ngParseExt', [])
|
||||
.config(['$parseProvider', function($parseProvider) {
|
||||
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
|
||||
}]);
|
||||
}])
|
||||
.info({ angularVersion: '"NG_VERSION_FULL"' });
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user