Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a34c8e1b8 | |||
| fec2ea8c1b | |||
| 7566c7d26f | |||
| 02a19c0f39 | |||
| 6e6e4eec9d | |||
| 46f14fa4b8 | |||
| bad2249bcd | |||
| c2148ec15c | |||
| 474e40498e | |||
| e9a6792d7f | |||
| adc1501caa | |||
| 7a53707c8f | |||
| 57fe5b320d | |||
| 671bebde0a | |||
| b51ded6736 | |||
| 3ec1819b91 | |||
| 009ebec64c | |||
| 9256dbc420 | |||
| 7504656a26 | |||
| e74cdf4b59 | |||
| 9d6c3f3ec2 | |||
| ac0e260765 | |||
| 28c0213ee5 | |||
| 8b4d85c015 | |||
| 2aeda67909 | |||
| ad68ee192e | |||
| 970ba117eb | |||
| 30e097b389 | |||
| e8e02b8bce | |||
| e36a3e89f5 | |||
| 1102c41196 | |||
| e970c8fce9 | |||
| 04271d6b2c | |||
| 2e1163ef5c | |||
| 75befe723a | |||
| 5e2bc5bbf3 | |||
| 079c485b92 | |||
| 19a0c9324c | |||
| 9ef02e72ab | |||
| 2101126ce7 | |||
| a222d0b452 | |||
| 1b8eb231c9 | |||
| 06baf1869b | |||
| 82597fc12b | |||
| ff52b188a7 | |||
| dc41f465ba | |||
| 6e3b5a57cd | |||
| f003d93a3d | |||
| aac5623247 | |||
| aa03812fd0 | |||
| bcb6a494de | |||
| a1e3f8728e | |||
| 9e8e3e187d | |||
| c6554433cf | |||
| 43d2a75f4e | |||
| 37d2b50812 | |||
| 8f31f1ff43 | |||
| cf84fcf544 | |||
| f63bc3cfde | |||
| a77943110e | |||
| ec97686f2f | |||
| 4ff7b7aa48 | |||
| 57b837bd5c | |||
| 6daca023e4 | |||
| 91b4eb0f69 | |||
| e50ed4da9e | |||
| fe5dd1da8f | |||
| df4c03fa33 | |||
| 8d4e626326 | |||
| 92aef5d456 | |||
| eed13cf732 | |||
| e90200b4de | |||
| 7f36ba77a0 | |||
| 457c58827b | |||
| aae768611f | |||
| ce5ffbf667 | |||
| ab114af850 | |||
| 2759788737 | |||
| c643323c17 | |||
| 9b97a033b0 | |||
| a3226d01fa | |||
| 510d0f946f | |||
| 0b962d4881 | |||
| 71b4daa4e1 | |||
| e0b02a5040 | |||
| a0dd9b0fdd | |||
| 498bef199a | |||
| 17d34b7a98 | |||
| 72359fd097 | |||
| 72882190f2 | |||
| fd2d8a5755 | |||
| fbe84f95a1 | |||
| 3671a43be4 | |||
| f6a1ad528d | |||
| d9128e7b23 | |||
| 0b54c1d4a9 | |||
| 608d623b55 | |||
| 9f30bb5475 | |||
| 182fb18f00 | |||
| 3a9fdceeee | |||
| ee4ac72170 | |||
| eb9fc571a0 | |||
| 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 |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "docs-angularjs-org-9p2"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@ performance/temp*.html
|
||||
*~
|
||||
*.swp
|
||||
angular.js.tmproj
|
||||
/node_modules/
|
||||
node_modules/
|
||||
bower_components/
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
|
||||
+58
-16
@@ -4,14 +4,13 @@ node_js:
|
||||
- '6'
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- bower_components
|
||||
- docs/bower_components
|
||||
|
||||
branches:
|
||||
except:
|
||||
- /^g3_.*$/
|
||||
- "/^g3_.*$/"
|
||||
|
||||
env:
|
||||
matrix:
|
||||
@@ -21,14 +20,15 @@ env:
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
|
||||
global:
|
||||
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
|
||||
# node 4 likes the G++ v4.8 compiler
|
||||
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
- CXX=g++-4.8
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
|
||||
- secure: oTBjhnOKhs0qDSKTf7fE4f6DYiNDPycvB7qfSF5QRIbJK/LK/J4UtFwetXuXj79HhUZG9qnoT+5e7lPaiaMlpsIKn9ann7ffqFWN1E8TMtpJF+AGigx3djYElwfgf5nEnFUFhwjFzvbfpZNnxVGgX5YbIZpe/WUbHkP4ffU0Wks=
|
||||
|
||||
# node 4 likes the G++ v4.8 compiler
|
||||
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@@ -37,25 +37,67 @@ addons:
|
||||
- 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
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5
|
||||
- export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
before_script:
|
||||
- du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true
|
||||
- ./scripts/travis/before_build.sh
|
||||
- du -sh ./node_modules ./bower_components/ || true
|
||||
- "./scripts/travis/before_build.sh"
|
||||
|
||||
script:
|
||||
- ./scripts/travis/build.sh
|
||||
- "./scripts/travis/build.sh"
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/tear_down_browser_provider.sh
|
||||
- ./scripts/travis/print_logs.sh
|
||||
- "./scripts/travis/tear_down_browser_provider.sh"
|
||||
- "./scripts/travis/print_logs.sh"
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2
|
||||
- http://104.197.9.155:8484/hubot/travis/activity #hubot-server
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: always # default: false
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: always # default: false
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: deploy
|
||||
env:
|
||||
- JOB=deploy
|
||||
before_script: skip
|
||||
script:
|
||||
- "./scripts/travis/build.sh"
|
||||
# Work around the 10min Travis timeout so the code.angularjs firebase+gcs code deploy can complete
|
||||
before_deploy: |
|
||||
function keep_alive() {
|
||||
while true; do
|
||||
echo -en "\a"
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
keep_alive &
|
||||
deploy:
|
||||
- provider: firebase
|
||||
skip_cleanup: true
|
||||
token:
|
||||
secure: $FIREBASE_TOKEN
|
||||
on:
|
||||
repo: angular/angular.js
|
||||
all_branches: true
|
||||
# deploy a new docs version when the commit is tagged on the "latest" npm version
|
||||
condition: $TRAVIS_TAG != '' && $( jq ".distTag" "package.json" | tr -d "\"[:space:]" ) = latest
|
||||
- provider: gcs
|
||||
skip_cleanup: true
|
||||
access_key_id: GOOGLDB7W2J3LFHICF3R
|
||||
secret_access_key:
|
||||
secure: tHIFdSq55qkyZf9zT/3+VkhUrTvOTMuswxXU3KyWaBrSieZqG0UnUDyNm+n3lSfX95zEl/+rJAWbfvhVSxZi13ndOtvRF+MdI1cvow2JynP0aDSiPffEvVrZOmihD6mt2SlMfhskr5FTduQ69kZG6DfLcve1PPDaIwnbOv3phb8=
|
||||
bucket: code-angularjs-org-338b8.appspot.com
|
||||
local-dir: upload
|
||||
detect_encoding: true # detects gzip compression
|
||||
on:
|
||||
repo: angular/angular.js
|
||||
all_branches: true
|
||||
# upload the build when the commit is tagged or the branch is "master"
|
||||
condition: $TRAVIS_TAG != '' || ($TRAVIS_PULL_REQUEST = false && $TRAVIS_BRANCH = master)
|
||||
|
||||
|
||||
+180
-17
@@ -1,9 +1,172 @@
|
||||
<a name="1.6.6"></a>
|
||||
# 1.6.6 interdimensional-cable (2017-08-18)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$httpParamSerializer:** ignore functions
|
||||
([b51ded](https://github.com/angular/angular.js/commit/b51ded67366865f36c5781dd5d9b801488ec95ea),
|
||||
[#16133](https://github.com/angular/angular.js/issues/16133))
|
||||
- **$resource:** do not throw when calling old `$cancelRequest()`
|
||||
([009ebe](https://github.com/angular/angular.js/commit/009ebec64c81d11b280c635167050e8906e191c6),
|
||||
[#16037](https://github.com/angular/angular.js/issues/16037))
|
||||
- **$parse:**
|
||||
- do not shallow-watch computed property keys
|
||||
([750465](https://github.com/angular/angular.js/commit/7504656a26202de591e4ac9674333254304edf8a))
|
||||
- support constants in computed keys
|
||||
([9d6c3f](https://github.com/angular/angular.js/commit/9d6c3f3ec233279885e37a250d25860d5c15f716))
|
||||
- **$http:** do not throw error if `Content-Type` is not `application/json` but response is JSON-like
|
||||
([2e1163](https://github.com/angular/angular.js/commit/2e1163ef5cb56d1933e8ecd7b74020b9df9c6693),
|
||||
[#16027](https://github.com/angular/angular.js/issues/16027),
|
||||
[#16075](https://github.com/angular/angular.js/issues/16075))
|
||||
|
||||
|
||||
## New Features
|
||||
- **$compile:** add `strictComponentBindingsEnabled()` method
|
||||
([3ec181](https://github.com/angular/angular.js/commit/3ec1819b913c8edf0649e06217dbd5920f29f126),
|
||||
[#16129](https://github.com/angular/angular.js/issues/16129))
|
||||
- **$resource:** add resource to response for error interceptors
|
||||
([9256db](https://github.com/angular/angular.js/commit/9256dbc4201343ce5cd63a9eadf98da4793f45af),
|
||||
[#16109](https://github.com/angular/angular.js/issues/16109))
|
||||
- **$http:** allow differentiation between XHR completion, error, abort, timeout
|
||||
([5e2bc5](https://github.com/angular/angular.js/commit/5e2bc5bbf347a9dfadc08b1514b8be06fd550913),
|
||||
[#15924](https://github.com/angular/angular.js/issues/15924),
|
||||
[#15847](https://github.com/angular/angular.js/issues/15847))
|
||||
|
||||
|
||||
<a name="1.6.5"></a>
|
||||
# 1.6.5 toffee-salinization (2017-07-03)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **core:**
|
||||
- correctly detect Error instances from different contexts
|
||||
([6daca0](https://github.com/angular/angular.js/commit/6daca023e42098f7098b9bf153c8e53a17af84f1),
|
||||
[#15868](https://github.com/angular/angular.js/issues/15868),
|
||||
[#15872](https://github.com/angular/angular.js/issues/15872))
|
||||
- deprecate `angular.merge`
|
||||
([dc41f4](https://github.com/angular/angular.js/commit/dc41f465baae9bc91418a61f446596157c530b6e),
|
||||
[#12653](https://github.com/angular/angular.js/issues/12653),
|
||||
[#14941](https://github.com/angular/angular.js/issues/14941),
|
||||
[#15180](https://github.com/angular/angular.js/issues/15180),
|
||||
[#15992](https://github.com/angular/angular.js/issues/15992),
|
||||
[#16036](https://github.com/angular/angular.js/issues/16036))
|
||||
- **ngOptions:**
|
||||
- re-render after empty option has been removed
|
||||
([510d0f](https://github.com/angular/angular.js/commit/510d0f946fa1a443ad43fa31bc9337676ef31332))
|
||||
- allow empty option to be removed and re-added
|
||||
([71b4da](https://github.com/angular/angular.js/commit/71b4daa4e10b6912891927ee2a7930c604b538f8))
|
||||
- select unknown option if unmatched model does not match empty option
|
||||
([17d34b](https://github.com/angular/angular.js/commit/17d34b7a983a0ef63f6cf404490385c696fb0da1))
|
||||
- **orderBy:** guarantee stable sort
|
||||
([e50ed4](https://github.com/angular/angular.js/commit/e50ed4da9e8177168f67da68bdf02f07da4e7bcf),
|
||||
[#14881](https://github.com/angular/angular.js/issues/14881),
|
||||
[#15914](https://github.com/angular/angular.js/issues/15914))
|
||||
- **$parse:**
|
||||
- do not shallow-watch inputs to one-time intercepted expressions
|
||||
([6e3b5a](https://github.com/angular/angular.js/commit/6e3b5a57cd921823f3eca7200a79ac5c2ef0567a))
|
||||
- standardize one-time literal vs non-literal and interceptors
|
||||
([f003d9](https://github.com/angular/angular.js/commit/f003d93a3dd052dccddef41125d9c51034ac3605))
|
||||
- do not shallow-watch inputs when wrapped in an interceptor fn
|
||||
([aac562](https://github.com/angular/angular.js/commit/aac5623247a86681cbe0e1c8179617b816394c1d),
|
||||
[#15905](https://github.com/angular/angular.js/issues/15905))
|
||||
- always re-evaluate filters within literals when an input is an object
|
||||
([ec9768](https://github.com/angular/angular.js/commit/ec97686f2f4a5481cc806462313a664fc7a1c893),
|
||||
[#15964](https://github.com/angular/angular.js/issues/15964),
|
||||
[#15990](https://github.com/angular/angular.js/issues/15990))
|
||||
- **$sanitize:** use appropriate inert document strategy for Firefox and Safari
|
||||
([8f31f1](https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94))
|
||||
- **$timeout/$interval:** do not trigger a digest on cancel
|
||||
([a222d0](https://github.com/angular/angular.js/commit/a222d0b452622624dc498ef0b9d3c43647fd4fbc),
|
||||
[#16057](https://github.com/angular/angular.js/issues/16057),
|
||||
[#16064](https://github.com/angular/angular.js/issues/16064))<br>
|
||||
This change might affect the use of `$timeout.flush()` in unit tests. See the commit message for
|
||||
more info.
|
||||
- **ngMock/$interval:** add support for zero-delay intervals in tests
|
||||
([a1e3f8](https://github.com/angular/angular.js/commit/a1e3f8728e0a80396f980e48f8dc68dde6721b2b),
|
||||
[#15952](https://github.com/angular/angular.js/issues/15952),
|
||||
[#15953](https://github.com/angular/angular.js/issues/15953))
|
||||
- **angular-loader:** do not depend on "closure" globals that may not be available
|
||||
([a3226d](https://github.com/angular/angular.js/commit/a3226d01fadaf145713518dc5b8022b581c34e81),
|
||||
[#15880](https://github.com/angular/angular.js/issues/15880),
|
||||
[#15881](https://github.com/angular/angular.js/issues/15881))
|
||||
|
||||
|
||||
## New Features
|
||||
- **select:** expose info about selection state in controller
|
||||
([0b962d](https://github.com/angular/angular.js/commit/0b962d4881e98327a91c37f7317da557aa991663),
|
||||
[#13172](https://github.com/angular/angular.js/issues/13172),
|
||||
[#10127](https://github.com/angular/angular.js/issues/10127))
|
||||
- **$animate:** add support for `customFilter`
|
||||
([ab114a](https://github.com/angular/angular.js/commit/ab114af8508bdbdb1fa5fd1e070d08818d882e28),
|
||||
[#14891](https://github.com/angular/angular.js/issues/14891))
|
||||
- **$compile:** overload `.component()` to accept object map of components
|
||||
([210112](https://github.com/angular/angular.js/commit/2101126ce72308d8fc468ca2411bb9972e614f79),
|
||||
[#14579](https://github.com/angular/angular.js/issues/14579),
|
||||
[#16062](https://github.com/angular/angular.js/issues/16062))
|
||||
- **$log:** log all parameters in IE 9, not just the first two.
|
||||
([3671a4](https://github.com/angular/angular.js/commit/3671a43be43d05b00c90dfb3a3f746c013139581))
|
||||
- **ngMock:** describe unflushed http requests
|
||||
([d9128e](https://github.com/angular/angular.js/commit/d9128e7b2371ab2bb5169ba854b21c78baa784d2),
|
||||
[#10596](https://github.com/angular/angular.js/issues/10596),
|
||||
[#15928](https://github.com/angular/angular.js/issues/15928))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
- **ngOptions:** prevent initial options repainting
|
||||
([ff52b1](https://github.com/angular/angular.js/commit/ff52b188a759f2cc7ee6ee78a8c646c2354a47eb),
|
||||
[#15801](https://github.com/angular/angular.js/issues/15801),
|
||||
[#15812](https://github.com/angular/angular.js/issues/15812),
|
||||
[#16071](https://github.com/angular/angular.js/issues/16071))
|
||||
- **$animate:**
|
||||
- avoid unnecessary computations if animations are globally disabled
|
||||
([ce5ffb](https://github.com/angular/angular.js/commit/ce5ffbf667464bd58eae4c4af0917eb2685f1f6a),
|
||||
[#14914](https://github.com/angular/angular.js/issues/14914))
|
||||
- do not retrieve `className` unless `classNameFilter` is used
|
||||
([275978](https://github.com/angular/angular.js/commit/27597887379a1904cd86832602e286894b449a75))
|
||||
|
||||
|
||||
|
||||
<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:**
|
||||
- **AngularJS:**
|
||||
- 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
|
||||
@@ -572,10 +735,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
|
||||
- **feat($compile): set preAssignBindingsEnabled to false by default
|
||||
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858))**:
|
||||
|
||||
Previously, `$compileProvider.preAssignBindingsEnabled` was
|
||||
set to true by default. This means bindings were pre-assigned in component
|
||||
constructors. In Angular 1.5+ the place to put the initialization logic
|
||||
relying on bindings being present is the controller `$onInit` method.
|
||||
Previously, `$compileProvider.preAssignBindingsEnabled` was set to true by default. This means
|
||||
bindings were pre-assigned on component/directive controller instances (which made them available
|
||||
inside the constructors). In AngularJS 1.5+ the place to put the initialization logic relying on
|
||||
bindings being present is the controller's `$onInit` method.
|
||||
|
||||
To migrate follow the example below:
|
||||
|
||||
@@ -1156,7 +1319,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
|
||||
@@ -1774,7 +1937,7 @@ validation), you can overwrite the built-in `step` validator with a custom direc
|
||||
# 1.5.9 timeturning-lockdown (2016-11-24)
|
||||
|
||||
This is an interim release primarily to publish some security fixes, in particular a modification to
|
||||
ensure that Angular 1 can pass the linter checks for Mozilla add-ons.
|
||||
ensure that AngularJS can pass the linter checks for Mozilla add-ons.
|
||||
|
||||
## Security Fixes
|
||||
- **bootstrap:**
|
||||
@@ -1862,7 +2025,7 @@ ensure that Angular 1 can pass the linter checks for Mozilla add-ons.
|
||||
|
||||
Previously, `$compileProvider.preAssignBindingsEnabled` was
|
||||
set to true by default. This means bindings were pre-assigned in component
|
||||
constructors. In Angular 1.5+ the place to put the initialization logic
|
||||
constructors. In AngularJS 1.5+ the place to put the initialization logic
|
||||
relying on bindings being present is the controller `$onInit` method.
|
||||
|
||||
To migrate follow the example below:
|
||||
@@ -2567,7 +2730,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
|
||||
@@ -4976,12 +5139,12 @@ before the $parsers are applied. Previously, the modelValue
|
||||
|
||||
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).
|
||||
`Date` and `Number` respectively (starting with AngularJS 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
|
||||
in AngularJS 1.2, the value that was validated by pattern could have
|
||||
been manipulated by the $parsers, as all validation was done
|
||||
inside this pipeline.
|
||||
|
||||
@@ -5092,12 +5255,12 @@ before the $parsers are applied. Previously, the modelValue
|
||||
|
||||
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).
|
||||
`Date` and `Number` respectively (starting with AngularJS 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
|
||||
in AngularJS 1.2, the value that was validated by pattern could have
|
||||
been manipulated by the $parsers, as all validation was done
|
||||
inside this pipeline.
|
||||
|
||||
@@ -5311,7 +5474,7 @@ describe('$q.when', function() {
|
||||
it('should not need a call to $timeout.flush() to resolve already resolved promises',
|
||||
inject(function($q, $timeout) {
|
||||
$q.when('foo');
|
||||
// In Angular 1.4.3 a call to `$timeout.flush();` was needed
|
||||
// In AngularJS 1.4.3 a call to `$timeout.flush();` was needed
|
||||
$timeout.verifyNoPendingTasks();
|
||||
}));
|
||||
|
||||
@@ -6787,7 +6950,7 @@ it is now implemented in the ngOptions directive itself.
|
||||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||||
values to determine which option is selected. This means `Number` scope values will not be matched
|
||||
against numeric option strings.
|
||||
In Angular 1.3.x, setting `scope.x = 200` would select the `option` with the value 200 in the following `select`:
|
||||
In AngularJS 1.3.x, setting `scope.x = 200` would select the `option` with the value 200 in the following `select`:
|
||||
|
||||
```
|
||||
<select ng-model="x">
|
||||
@@ -6796,7 +6959,7 @@ In Angular 1.3.x, setting `scope.x = 200` would select the `option` with the val
|
||||
</select>
|
||||
```
|
||||
|
||||
In Angular 1.4.x, the 'unknown option' will be selected.
|
||||
In AngularJS 1.4.x, the 'unknown option' will be selected.
|
||||
To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to
|
||||
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
|
||||
|
||||
@@ -15370,7 +15533,7 @@ with the `$route` service
|
||||
|
||||
### Breaking changes
|
||||
- we now support ISO 8601 extended format datetime strings (YYYY-MM-DDTHH:mm:ss.SSSZ) as defined
|
||||
in EcmaScript 5 throughout angular. This means that the following apis switched from
|
||||
in EcmaScript 5 throughout AngularJS. This means that the following apis switched from
|
||||
YYYY-MM-DDTHH:mm:ssZ to YYYY-MM-DDTHH:mm:ss.SSSZ (note the added millis) when representing dates:
|
||||
- angular.Date.toString
|
||||
- angular.String.fromDate
|
||||
|
||||
+71
-12
@@ -49,7 +49,6 @@ if (!process.env.TRAVIS && !process.env.JENKINS_HOME) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// this loads all the node_modules that start with `grunt-` as plugins
|
||||
@@ -64,6 +63,8 @@ module.exports = function(grunt) {
|
||||
NG_VERSION.cdn = versionInfo.cdnVersion;
|
||||
var dist = 'angular-' + NG_VERSION.full;
|
||||
|
||||
var deployVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.full;
|
||||
|
||||
if (versionInfo.cdnVersion == null) {
|
||||
throw new Error('Unable to read CDN version, are you offline or has the CDN not been properly pushed?\n' +
|
||||
'Perhaps you want to set the NG1_BUILD_NO_REMOTE_VERSION_REQUESTS environment variable?');
|
||||
@@ -325,6 +326,15 @@ module.exports = function(grunt) {
|
||||
expand: true,
|
||||
dot: true,
|
||||
dest: dist + '/'
|
||||
},
|
||||
firebaseCodeDeploy: {
|
||||
options: {
|
||||
mode: 'gzip'
|
||||
},
|
||||
src: ['**'],
|
||||
cwd: 'build',
|
||||
expand: true,
|
||||
dest: 'upload/' + deployVersion + '/'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -359,24 +369,73 @@ module.exports = function(grunt) {
|
||||
});
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['eslint', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', [
|
||||
'eslint',
|
||||
'package',
|
||||
'test:unit',
|
||||
'test:promises-aplus',
|
||||
'tests:docs',
|
||||
'test:protractor'
|
||||
]);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery (latest) unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:jquery-2.2', 'Run the jQuery 2.2 unit tests with Karma', ['tests:jquery-2.2']);
|
||||
grunt.registerTask('test:jquery-2.1', 'Run the jQuery 2.1 unit tests with Karma', ['tests:jquery-2.1']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', [
|
||||
'build',
|
||||
'tests:modules'
|
||||
]);
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['test:jqlite', 'test:jquery', 'test:jquery-2.2', 'test:jquery-2.1', 'test:modules']);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:normal']);
|
||||
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', [
|
||||
'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:jquery-2.2',
|
||||
'test:jquery-2.1',
|
||||
'test:modules'
|
||||
]);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', [
|
||||
'webdriver',
|
||||
'connect:testserver',
|
||||
'protractor:normal'
|
||||
]);
|
||||
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', [
|
||||
'connect:testserver',
|
||||
'protractor:travis'
|
||||
]);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', [
|
||||
'webdriver',
|
||||
'connect:testserver',
|
||||
'protractor:jenkins'
|
||||
]);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
|
||||
|
||||
grunt.registerTask('minify', ['bower', 'clean', 'build', 'minall']);
|
||||
grunt.registerTask('test:promises-aplus',[
|
||||
'build:promises-aplus-adapter',
|
||||
'shell:promises-aplus-tests'
|
||||
]);
|
||||
grunt.registerTask('minify', [
|
||||
'bower',
|
||||
'clean',
|
||||
'build',
|
||||
'minall'
|
||||
]);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'write', 'docs', 'copy', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'eslint']);
|
||||
grunt.registerTask('package', [
|
||||
'bower',
|
||||
'validate-angular-files',
|
||||
'clean',
|
||||
'buildall',
|
||||
'minall',
|
||||
'collect-errors',
|
||||
'write',
|
||||
'docs',
|
||||
'copy',
|
||||
'compress:build'
|
||||
]);
|
||||
grunt.registerTask('ci-checks', [
|
||||
'ddescribe-iit',
|
||||
'merge-conflict',
|
||||
'eslint'
|
||||
]);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ synchronizes data from your UI (view) with your JavaScript objects (model) throu
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control.
|
||||
|
||||
It also helps with server-side communication, taming async callbacks with promises and deferreds,
|
||||
and it makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
It also helps with server-side communication, taming async callbacks with promises and deferred objects,
|
||||
and it makes client-side navigation and deep linking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. Best of all? It makes development fun!
|
||||
|
||||
* Web site: https://angularjs.org
|
||||
|
||||
+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",
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.eot?v=1.1.0");
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.eot?#iefix&v=1.1.0") format("embedded-opentype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.woff?v=1.1.0") format("woff"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.ttf?v=1.1.0") format("truetype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.svg?v=1.1.0#OpenSansBold") format("svg");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.eot?v=1.1.0");
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.eot?#iefix&v=1.1.0") format("embedded-opentype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.woff?v=1.1.0") format("woff"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.ttf?v=1.1.0") format("truetype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.svg?v=1.1.0#OpenSansBold") format("svg");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.eot?v=1.1.0");
|
||||
src: url("../components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.eot?#iefix&v=1.1.0") format("embedded-opentype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.woff?v=1.1.0") format("woff"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.ttf?v=1.1.0") format("truetype"),
|
||||
url("../components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.svg?v=1.1.0#OpenSansBold") format("svg");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html, body {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
@@ -14,6 +14,7 @@ angular.module('docsApp', [
|
||||
'search',
|
||||
'tutorials',
|
||||
'versions',
|
||||
'deployment',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
@@ -18,7 +18,13 @@ angular.module('examples', [])
|
||||
return {
|
||||
restrict: 'C',
|
||||
scope : true,
|
||||
controller : ['$scope', function($scope) {
|
||||
controller : ['$scope', 'DEPLOYMENT', function($scope, DEPLOYMENT) {
|
||||
var exampleIndexFile = (DEPLOYMENT === 'default' ? 'index' : 'index-' + DEPLOYMENT) + '.html';
|
||||
|
||||
$scope.getExampleIndex = function(basePath) {
|
||||
return basePath + '/' + exampleIndexFile;
|
||||
};
|
||||
|
||||
$scope.setTab = function(index) {
|
||||
var tab = $scope.tabs[index];
|
||||
$scope.activeTabIndex = index;
|
||||
|
||||
+11
-1
@@ -112,6 +112,10 @@ module.exports = new Package('angularjs', [
|
||||
docTypes: ['indexPage'],
|
||||
pathTemplate: '.',
|
||||
outputPathTemplate: '${id}.html'
|
||||
}, {
|
||||
docTypes: ['deploymentData'],
|
||||
pathTemplate: '.',
|
||||
outputPathTemplate: 'js/${id}.js'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
@@ -125,8 +129,14 @@ module.exports = new Package('angularjs', [
|
||||
outputPathTemplate: 'partials/${area}/${moduleName}/${groupType}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['example'],
|
||||
pathTemplate: 'examples/${example.id}',
|
||||
outputPathTemplate: 'examples/${example.id}/index${deploymentQualifier}.html'
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['overview', 'tutorial', 'e2e-test', 'indexPage'],
|
||||
docTypes: ['overview', 'tutorial', 'e2e-test', 'indexPage', 'deploymentData'],
|
||||
getId: function(doc) { return doc.fileInfo.baseName; },
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
@@ -35,7 +35,17 @@ module.exports = function generateIndexPagesProcessor() {
|
||||
|
||||
indexDoc.id = 'index' + (deployment.name === 'default' ? '' : '-' + deployment.name);
|
||||
|
||||
var deploymentDoc = {
|
||||
docType: 'deploymentData',
|
||||
id: 'deployment-data-' + deployment.name,
|
||||
template: 'angular-service.template.js',
|
||||
ngModuleName: 'deployment',
|
||||
serviceName: 'DEPLOYMENT',
|
||||
serviceValue: deployment.name
|
||||
};
|
||||
|
||||
docs.push(indexDoc);
|
||||
docs.push(deploymentDoc);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -47,6 +47,14 @@ module.exports = function generateVersionDocProcessor(gitData) {
|
||||
|
||||
var latestMap = {};
|
||||
|
||||
// When the docs are built on a tagged commit, yarn info won't include the latest release,
|
||||
// so we add it manually based on the local version.json file.
|
||||
var missesCurrentVersion = !currentVersion.isSnapshot && !versions.find(function(version) {
|
||||
return version === currentVersion.version;
|
||||
});
|
||||
|
||||
if (missesCurrentVersion) versions.push(currentVersion.version);
|
||||
|
||||
versions = versions
|
||||
.filter(function(versionStr) {
|
||||
return blacklist.indexOf(versionStr) === -1;
|
||||
@@ -70,6 +78,7 @@ module.exports = function generateVersionDocProcessor(gitData) {
|
||||
})
|
||||
.reverse();
|
||||
|
||||
// List the latest version for each branch
|
||||
var latest = sortObject(latestMap, reverse(semver.compare))
|
||||
.map(function(version) { return makeOption(version, 'Latest'); });
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'js/all-versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/deployment-data-debug.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'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',
|
||||
|
||||
@@ -17,7 +17,7 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/marked.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
@@ -26,11 +26,11 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'js/all-versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/deployment-data-default.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'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',
|
||||
|
||||
+1
-1
@@ -30,11 +30,11 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'js/all-versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/deployment-data-jquery.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'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',
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = function productionDeployment(getVersion) {
|
||||
cdnUrl + '/angular-sanitize.min.js',
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked') + '/lib/marked.js',
|
||||
'components/marked-' + getVersion('marked') + '/marked.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
@@ -43,11 +43,11 @@ module.exports = function productionDeployment(getVersion) {
|
||||
'https://code.angularjs.org/snapshot/docs/js/all-versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/deployment-data-production.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'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',
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</head>
|
||||
<body class="homepage">
|
||||
<div id="wrapper">
|
||||
<header class="header" scroll-y-offset-element>
|
||||
<header class="header">
|
||||
<nav id="navbar-main" class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner" ng-controller="DocsSearchCtrl">
|
||||
<div class="container">
|
||||
@@ -156,7 +156,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top">
|
||||
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element>
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<version-picker></version-picker>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.deployments.default.outputPath $}" name="{$ doc.example.id $}"></iframe>
|
||||
<iframe class="runnable-example-frame" ng-src="{{getExampleIndex('{$ doc.example.deployments.default.path $}')}}" name="{$ doc.example.id $}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
<ul>
|
||||
{% if doc.restrict.element %}
|
||||
<li>as element:
|
||||
{% if doc.name.indexOf('ng') == 0 -%}
|
||||
(This directive can be used as custom element, but be aware of <a href="guide/ie">IE restrictions</a>).
|
||||
{%- endif %}
|
||||
{% code %}
|
||||
<{$ doc.name | dashCase $}
|
||||
{%- for param in doc.params %}
|
||||
|
||||
@@ -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.
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
This error occurs when the restrict property of a directive is not valid.
|
||||
|
||||
The directive restrict property must be a string including one of more of the following characters:
|
||||
The directive restrict property must be a string including one or more of the following characters:
|
||||
* E (element)
|
||||
* A (attribute)
|
||||
* C (class)
|
||||
@@ -15,4 +15,4 @@ For example:
|
||||
```javascript
|
||||
restrict: 'E'
|
||||
restrict: 'EAC'
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
@ngdoc error
|
||||
@name $compile:missingattr
|
||||
@fullName Missing required attribute
|
||||
@description
|
||||
|
||||
This error may occur only when `$compileProvider.strictComponentBindingsEnabled` is set to `true`.
|
||||
Then all attributes mentioned in `bindings` without `?` must be set. If one or more aren't set,
|
||||
the first one will throw an error.
|
||||
@@ -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.
|
||||
|
||||
To resolve this error, make sure you pass valid JSON data to `transformResponse`. If the response
|
||||
data looks like JSON, but has a different `Content-Type` header, you must
|
||||
{@link ng.$http#overriding-the-default-transformations-per-request implement your own response
|
||||
transformer on a per request basis}, or {@link ng.$http#default-transformations modify the default `$http` responseTransform}.
|
||||
+244
-197
@@ -6,20 +6,26 @@
|
||||
|
||||
# Animations
|
||||
|
||||
AngularJS provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
|
||||
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
|
||||
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
|
||||
or with JavaScript code when it's defined as a factory.
|
||||
AngularJS provides animation hooks for common directives such as
|
||||
{@link ng.directive:ngRepeat ngRepeat}, {@link ng.directive:ngSwitch ngSwitch}, and
|
||||
{@link ngRoute.directive:ngView ngView}, as well as custom directives via the `$animate` service.
|
||||
These animation hooks are set in place to trigger animations during the life cycle of various
|
||||
directives and when triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a
|
||||
JavaScript callback Animation (depending on whether an animation is placed on the given directive).
|
||||
Animations can be placed using vanilla CSS by following the naming conventions set in place by
|
||||
AngularJS or with JavaScript code, defined as a factory.
|
||||
|
||||
<div class="alert alert-info">
|
||||
Note that we have used non-prefixed CSS transition properties in our examples as the major browsers now support non-prefixed
|
||||
properties. If you intend to support older browsers or certain mobile browsers then you will need to include prefixed
|
||||
versions of the transition properties. Take a look at http://caniuse.com/#feat=css-transitions for what browsers require prefixes,
|
||||
and https://github.com/postcss/autoprefixer for a tool that can automatically generate the prefixes for you.
|
||||
Note that we have used non-prefixed CSS transition properties in our examples as the major
|
||||
browsers now support non-prefixed properties. If you intend to support older browsers or certain
|
||||
mobile browsers then you will need to include prefixed versions of the transition properties. Take
|
||||
a look at http://caniuse.com/#feat=css-transitions for what browsers require prefixes, and
|
||||
https://github.com/postcss/autoprefixer for a tool that can automatically generate the prefixes
|
||||
for you.
|
||||
</div>
|
||||
|
||||
Animations are not available unless you include the {@link ngAnimate `ngAnimate` module} as a dependency within your application.
|
||||
Animations are not available unless you include the {@link ngAnimate `ngAnimate` module} as a
|
||||
dependency of your application.
|
||||
|
||||
Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||
|
||||
@@ -59,8 +65,9 @@ You may also want to setup a separate CSS file for defining CSS-based animations
|
||||
|
||||
## How they work
|
||||
|
||||
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within
|
||||
your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so:
|
||||
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class
|
||||
attached to a HTML element within your application, you can apply animations to it. Lets say for
|
||||
example that we have an HTML template with a repeater like so:
|
||||
|
||||
```html
|
||||
<div ng-repeat="item in items" class="repeated-item">
|
||||
@@ -68,22 +75,21 @@ your website, you can apply animations to it. Lets say for example that we have
|
||||
</div>
|
||||
```
|
||||
|
||||
As you can see, the `.repeated-item` class is present on the element that will be repeated and this class will be
|
||||
used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation.
|
||||
As you can see, the `repeated-item` class is present on the element that will be repeated and this
|
||||
class will be used as a reference within our application's CSS and/or JavaScript animation code to
|
||||
tell AngularJS to perform an animation.
|
||||
|
||||
As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add
|
||||
a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around
|
||||
it will apply a `ng-move` class name.
|
||||
As `ngRepeat` does its thing, each time a new item is added into the list, `ngRepeat` will add an
|
||||
`ng-enter` class to the element that is being added. When removed it will apply an `ng-leave` class
|
||||
and when moved around it will apply an `ng-move` class.
|
||||
|
||||
Taking a look at the following CSS code, we can see some transition and keyframe animation code set for each of those events that
|
||||
occur when ngRepeat triggers them:
|
||||
Taking a look at the following CSS code, we can see some transition and keyframe animation code set
|
||||
up for each of those events that occur when `ngRepeat` triggers them:
|
||||
|
||||
```css
|
||||
/*
|
||||
We're using CSS transitions for when
|
||||
the enter and move events are triggered
|
||||
for the element that has the .repeated-item
|
||||
class
|
||||
We are using CSS transitions for when the enter and move events
|
||||
are triggered for the element that has the `repeated-item` class
|
||||
*/
|
||||
.repeated-item.ng-enter, .repeated-item.ng-move {
|
||||
transition: all 0.5s linear;
|
||||
@@ -91,10 +97,8 @@ occur when ngRepeat triggers them:
|
||||
}
|
||||
|
||||
/*
|
||||
The ng-enter-active and ng-move-active
|
||||
are where the transition destination properties
|
||||
are set so that the animation knows what to
|
||||
animate.
|
||||
`.ng-enter-active` and `.ng-move-active` are where the transition destination
|
||||
properties are set so that the animation knows what to animate
|
||||
*/
|
||||
.repeated-item.ng-enter.ng-enter-active,
|
||||
.repeated-item.ng-move.ng-move-active {
|
||||
@@ -102,73 +106,64 @@ occur when ngRepeat triggers them:
|
||||
}
|
||||
|
||||
/*
|
||||
We're using CSS keyframe animations for when
|
||||
the leave event is triggered for the element
|
||||
that has the .repeated-item class
|
||||
We are using CSS keyframe animations for when the `leave` event
|
||||
is triggered for the element that has the `repeated-item` class
|
||||
*/
|
||||
.repeated-item.ng-leave {
|
||||
animation: 0.5s my_animation;
|
||||
}
|
||||
|
||||
@keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**):
|
||||
The same approach to animation can be used using JavaScript code
|
||||
(**for simplicity, we rely on jQuery to perform animations here**):
|
||||
|
||||
```js
|
||||
myModule.animation('.repeated-item', function() {
|
||||
return {
|
||||
enter: function(element, done) {
|
||||
element.css('opacity',0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
// Initialize the element's opacity
|
||||
element.css('opacity', 0);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
// Animate the element's opacity
|
||||
// (`element.animate()` is provided by jQuery)
|
||||
element.animate({opacity: 1}, done);
|
||||
|
||||
// Optional `onDone`/`onCancel` callback function
|
||||
// to handle any post-animation cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
if (isCancelled) {
|
||||
// Abort the animation if cancelled
|
||||
// (`element.stop()` is provided by jQuery)
|
||||
element.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
leave: function(element, done) {
|
||||
// Initialize the element's opacity
|
||||
element.css('opacity', 1);
|
||||
jQuery(element).animate({
|
||||
opacity: 0
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
move: function(element, done) {
|
||||
element.css('opacity', 0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
// Animate the element's opacity
|
||||
// (`element.animate()` is provided by jQuery)
|
||||
element.animate({opacity: 0}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
// Optional `onDone`/`onCancel` callback function
|
||||
// to handle any post-animation cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
if (isCancelled) {
|
||||
// Abort the animation if cancelled
|
||||
// (`element.stop()` is provided by jQuery)
|
||||
element.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// you can also capture these animation events
|
||||
// We can also capture the following animation events:
|
||||
move: function(element, done) {},
|
||||
addClass: function(element, className, done) {},
|
||||
removeClass: function(element, className, done) {}
|
||||
}
|
||||
@@ -176,74 +171,84 @@ myModule.animation('.repeated-item', function() {
|
||||
```
|
||||
|
||||
With these generated CSS class names present on the element at the time, AngularJS automatically
|
||||
figures out whether to perform a CSS and/or JavaScript animation. If both CSS and JavaScript animation
|
||||
code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time.
|
||||
figures out whether to perform a CSS and/or JavaScript animation. Note that you can't have both CSS
|
||||
and JavaScript animations based on the same CSS class. See
|
||||
{@link ngAnimate#css-js-animations-together here} for more details.
|
||||
|
||||
## Class and ngClass animation hooks
|
||||
## Class and `ngClass` animation hooks
|
||||
|
||||
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
|
||||
This means that if a CSS class is added to or removed from an element then an animation can be executed in between,
|
||||
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
|
||||
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
|
||||
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and
|
||||
**remove** hooks. This means that if a CSS class is added to or removed from an element then an
|
||||
animation can be executed in between, before the CSS class addition or removal is finalized.
|
||||
(Keep in mind that AngularJS will only be able to capture class changes if an
|
||||
**interpolated expression** or the **ng-class** directive is used on the element.)
|
||||
|
||||
The example below shows how to perform animations during class changes:
|
||||
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="animate-css-class">
|
||||
<file name="index.html">
|
||||
<p>
|
||||
<input type="button" value="set" ng-click="myCssVar='css-class'">
|
||||
<input type="button" value="clear" ng-click="myCssVar=''">
|
||||
<br>
|
||||
<span ng-class="myCssVar">CSS-Animated Text</span>
|
||||
</p>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.css-class-add, .css-class-remove {
|
||||
transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
}
|
||||
<file name="index.html">
|
||||
<p>
|
||||
<button ng-click="myCssVar='css-class'">Set</button>
|
||||
<button ng-click="myCssVar=''">Clear</button>
|
||||
<br>
|
||||
<span ng-class="myCssVar">CSS-Animated Text</span>
|
||||
</p>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.css-class-add, .css-class-remove {
|
||||
transition: all 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940);
|
||||
}
|
||||
|
||||
.css-class,
|
||||
.css-class-add.css-class-add-active {
|
||||
color: red;
|
||||
font-size:3em;
|
||||
}
|
||||
.css-class,
|
||||
.css-class-add.css-class-add-active {
|
||||
color: red;
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.css-class-remove.css-class-remove-active {
|
||||
font-size:1.0em;
|
||||
color: black;
|
||||
}
|
||||
</file>
|
||||
.css-class-remove.css-class-remove-active {
|
||||
font-size: 1em;
|
||||
color: black;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Although the CSS is a little different than what we saw before, the idea is the same.
|
||||
|
||||
## Which directives support animations?
|
||||
|
||||
A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during its life cycle.
|
||||
The table below explains in detail which animation events are triggered
|
||||
A handful of common AngularJS directives support and trigger animation hooks whenever any major
|
||||
event occurs during their life cycle. The table below explains in detail which animation events are
|
||||
triggered:
|
||||
|
||||
| Directive | Supported Animations |
|
||||
|-------------------------------------------------------------------------------------|------------------------------------------|
|
||||
| {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
|
||||
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
|
||||
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
|
||||
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
|
||||
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
|
||||
| {@link ng.directive:ngClass#animations ngClass or {{class}}} | add and remove |
|
||||
| {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
| Directive | Supported Animations |
|
||||
|-------------------------------------------------------------------------------|---------------------------------------------------------------------------|
|
||||
| {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
|
||||
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
|
||||
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
|
||||
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
|
||||
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
|
||||
| {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave |
|
||||
| {@link ng.directive:ngClass#animations ngClass / {{class}​}} | add and remove |
|
||||
| {@link ng.directive:ngClass#animations ngClassEven / ngClassOdd} | add and remove |
|
||||
| {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) |
|
||||
| {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) |
|
||||
| {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) |
|
||||
| {@link ng.directive:form#animation-hooks form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) |
|
||||
| {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) |
|
||||
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link ng.$animate API docs}.
|
||||
For a full breakdown of the steps involved during each animation event, refer to the
|
||||
{@link ng.$animate API docs}.
|
||||
|
||||
## How do I use animations in my own directives?
|
||||
|
||||
Animations within custom directives can also be established by injecting `$animate` directly into your directive and
|
||||
making calls to it when needed.
|
||||
Animations within custom directives can also be established by injecting `$animate` directly into
|
||||
your directive and making calls to it when needed.
|
||||
|
||||
```js
|
||||
myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
return function(scope, element, attrs) {
|
||||
return function(scope, element) {
|
||||
element.on('click', function() {
|
||||
if(element.hasClass('clicked')) {
|
||||
if (element.hasClass('clicked')) {
|
||||
$animate.removeClass(element, 'clicked');
|
||||
} else {
|
||||
$animate.addClass(element, 'clicked');
|
||||
@@ -255,17 +260,19 @@ myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
|
||||
## Animations on app bootstrap / page load
|
||||
|
||||
By default, animations are disabled when the Angular app {@link guide/bootstrap bootstraps}. If you are using the {@link ngApp} directive,
|
||||
this happens in the `DOMContentLoaded` event, so immediately after the page has been loaded.
|
||||
Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on
|
||||
the page, the loading process may become too visually overwhelming, and the performance may suffer.
|
||||
By default, animations are disabled when the AngularJS app {@link guide/bootstrap bootstraps}. If you
|
||||
are using the {@link ngApp} directive, this happens in the `DOMContentLoaded` event, so immediately
|
||||
after the page has been loaded. Animations are disabled, so that UI and content are instantly
|
||||
visible. Otherwise, with many animations on the page, the loading process may become too visually
|
||||
overwhelming, and the performance may suffer.
|
||||
|
||||
Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap have finished.
|
||||
Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest} and the one after that to finish.
|
||||
This ensures that the whole app has been compiled fully before animations are attempted.
|
||||
Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap
|
||||
have finished. Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest $digest}
|
||||
and one more after that, to finish. This ensures that the whole app has been compiled fully before
|
||||
animations are attempted.
|
||||
|
||||
If you do want your animations to play when the app bootstraps, you can enable animations globally in
|
||||
your main module's {@link angular.Module#run run} function:
|
||||
If you do want your animations to play when the app bootstraps, you can enable animations globally
|
||||
in your main module's {@link angular.Module#run run} function:
|
||||
|
||||
```js
|
||||
myModule.run(function($animate) {
|
||||
@@ -275,17 +282,50 @@ myModule.run(function($animate) {
|
||||
|
||||
## How to (selectively) enable, disable and skip animations
|
||||
|
||||
There are three different ways to disable animations, both globally and for specific animations.
|
||||
Disabling specific animations can help to speed up the render performance, for example for large `ngRepeat`
|
||||
lists that don't actually have animations. Because ngAnimate checks at runtime if animations are present,
|
||||
performance will take a hit even if an element has no animation.
|
||||
There are several different ways to disable animations, both globally and for specific animations.
|
||||
Disabling specific animations can help to speed up the render performance, for example for large
|
||||
`ngRepeat` lists that don't actually have animations. Because `ngAnimate` checks at runtime if
|
||||
animations are present, performance will take a hit even if an element has no animation.
|
||||
|
||||
### In the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
|
||||
### During the config: {@link $animateProvider#customFilter $animateProvider.customFilter()}
|
||||
|
||||
This function can be called in the {@link angular.Module#config config} phase of an app. It takes a regex as the only argument,
|
||||
which will then be matched against the classes of any element that is about to be animated. The regex
|
||||
allows a lot of flexibility - you can either allow animations only for specific classes (useful when
|
||||
you are working with 3rd party animations), or exclude specific classes from getting animated.
|
||||
This function can be called during the {@link angular.Module#config config} phase of an app. It
|
||||
takes a filter function as the only argument, which will then be used to "filter" animations (based
|
||||
on the animated element, the event type, and the animation options). Only when the filter function
|
||||
returns `true`, will the animation be performed. This allows great flexibility - you can easily
|
||||
create complex rules, such as allowing specific events only or enabling animations on specific
|
||||
subtrees of the DOM, and dynamically modify them, for example disabling animations at certain points
|
||||
in time or under certain circumstances.
|
||||
|
||||
```js
|
||||
app.config(function($animateProvider) {
|
||||
$animateProvider.customFilter(function(node, event, options) {
|
||||
// Example: Only animate `enter` and `leave` operations.
|
||||
return event === 'enter' || event === 'leave';
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The `customFilter` approach generally gives a big speed boost compared to other strategies, because
|
||||
the matching is done before other animation disabling strategies are checked.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:**
|
||||
Keep the filtering function as lean as possible, because it will be called for each DOM
|
||||
action (e.g. insertion, removal, class change) performed by "animation-aware" directives.
|
||||
See {@link guide/animations#which-directives-support-animations- here} for a list of built-in
|
||||
directives that support animations.
|
||||
Performing computationally expensive or time-consuming operations on each call of the
|
||||
filtering function can make your animations sluggish.
|
||||
</div>
|
||||
|
||||
### During the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
|
||||
|
||||
This function too can be called during the {@link angular.Module#config config} phase of an app. It
|
||||
takes a regex as the only argument, which will then be matched against the classes of any element
|
||||
that is about to be animated. The regex allows a lot of flexibility - you can either allow
|
||||
animations for specific classes only (useful when you are working with 3rd party animations), or
|
||||
exclude specific classes from getting animated.
|
||||
|
||||
```js
|
||||
app.config(function($animateProvider) {
|
||||
@@ -294,42 +334,43 @@ app.config(function($animateProvider) {
|
||||
```
|
||||
|
||||
```css
|
||||
/* prefixed with animate- */
|
||||
/* prefixed with `animate-` */
|
||||
.animate-fade-add.animate-fade-add-active {
|
||||
transition: all 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
```
|
||||
|
||||
The classNameFilter approach generally applies the biggest speed boost, because the matching is
|
||||
done before any other animation disabling strategies are checked. However, that also means it is not
|
||||
possible to override class name matching with the two following strategies. It's of course still possible
|
||||
to enable / disable animations by changing an element's class name at runtime.
|
||||
The `classNameFilter` approach generally gives a big speed boost compared to other strategies,
|
||||
because the matching is done before other animation disabling strategies are checked. However, that
|
||||
also means it is not possible to override class name matching with the two following strategies.
|
||||
It's of course still possible to enable / disable animations by changing an element's class name at
|
||||
runtime.
|
||||
|
||||
### At runtime: {@link ng.$animate#enabled $animate.enabled()}
|
||||
|
||||
This function can be used to enable / disable animations in two different ways:
|
||||
|
||||
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
|
||||
disables all animations in your app.
|
||||
With a single `boolean` argument, it enables / disables animations globally:
|
||||
`$animate.enabled(false)` disables all animations in your app.
|
||||
|
||||
When the first argument is a native DOM or jqLite/jQuery element, the function enables / disables
|
||||
animations on this element *and all its children*: `$animate.enabled(myElement, false)`. This is the
|
||||
most flexible way to change the animation state. For example, even if you have used it to disable
|
||||
animations on a parent element, you can still re-enable it for a child element. And compared to the
|
||||
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
|
||||
animations on this element *and all its children*: `$animate.enabled(myElement, false)`. You can
|
||||
still use it to re-enable animations for a child element, even if you have disabled them on a parent
|
||||
element. And compared to the `classNameFilter`, you can change the animation status at runtime
|
||||
instead of during the config phase.
|
||||
|
||||
Note however that the `$animate.enabled()` state for individual elements does not overwrite disabling
|
||||
rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}.
|
||||
Note however that the `$animate.enabled()` state for individual elements does not overwrite
|
||||
disabling rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}.
|
||||
|
||||
### Via CSS styles: overwriting styles in the `ng-animate` CSS class
|
||||
Whenever an animation is started, ngAnimate applies the `ng-animate` class to the element for the
|
||||
whole duration of the animation. By applying CSS transition / animation styling to the class,
|
||||
you can skip an animation:
|
||||
|
||||
Whenever an animation is started, `ngAnimate` applies the `ng-animate` class to the element for the
|
||||
whole duration of the animation. By applying CSS transition / animation styling to that class, you
|
||||
can skip an animation:
|
||||
|
||||
```css
|
||||
|
||||
.my-class{
|
||||
.my-class {
|
||||
transition: transform 2s;
|
||||
}
|
||||
|
||||
@@ -340,23 +381,23 @@ you can skip an animation:
|
||||
my-class.ng-animate {
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
By setting `transition: 0s`, ngAnimate will ignore the existing transition styles, and not try to animate them (Javascript
|
||||
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
|
||||
issues with existing animations interfering with ngAnimate}.
|
||||
By setting `transition: 0s`, `ngAnimate` will ignore the existing transition styles, and not try to
|
||||
animate them (Javascript animations will still execute, though). This can be used to prevent
|
||||
{@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
|
||||
issues with existing animations interfering with `ngAnimate`}.
|
||||
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
|
||||
where the animated element is briefly visible.
|
||||
When nesting elements with structural animations, such as `ngIf`, into elements that have
|
||||
class-based animations such as `ngClass`, it sometimes happens that before the actual animation
|
||||
starts, there is a brief flicker or flash of content where the animated element is briefly visible.
|
||||
|
||||
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
|
||||
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
|
||||
animations (`enter`, `move`, and `leave`).
|
||||
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as
|
||||
an animation is initialized, but removed before the actual animation starts (after waiting for a
|
||||
`$digest`). This class is only added for *structural* animations (`enter`, `move`, and `leave`).
|
||||
|
||||
Here's an example where you might see flickering:
|
||||
|
||||
@@ -368,8 +409,9 @@ Here's an example where you might see flickering:
|
||||
</div>
|
||||
```
|
||||
|
||||
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
|
||||
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
|
||||
It is possible that during the `enter` event, the `.message` div will be briefly visible before it
|
||||
starts animating. In that case, you can add styles to the CSS that make sure the element stays
|
||||
hidden before the animation starts:
|
||||
|
||||
```css
|
||||
.message.ng-enter-prepare {
|
||||
@@ -379,66 +421,71 @@ In that case, you can add styles to the CSS that make sure the element stays hid
|
||||
/* Other animation styles ... */
|
||||
```
|
||||
|
||||
## Preventing Collisions with Existing Animations and Third Party Libraries
|
||||
By default, any `ngAnimate` enabled directives will assume any transition / animation styles on the
|
||||
element are part of an `ngAnimate` animation. This can lead to problems when the styles are actually
|
||||
for animations that are independent of `ngAnimate`.
|
||||
## Preventing collisions with existing animations and third-party libraries
|
||||
|
||||
For example, an element acts as a loading spinner. It has an infinite css animation on it, and also an
|
||||
{@link ngIf `ngIf`} directive, for which no animations are defined:
|
||||
By default, any `ngAnimate`-enabled directives will assume that `transition` / `animation` styles on
|
||||
the element are part of an `ngAnimate` animation. This can lead to problems when the styles are
|
||||
actually for animations that are independent of `ngAnimate`.
|
||||
|
||||
For example, an element acts as a loading spinner. It has an infinite css animation on it, and also
|
||||
an {@link ngIf `ngIf`} directive, for which no animations are defined:
|
||||
|
||||
```css
|
||||
@keyframes rotating {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
.spinner {
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: rotating 2s linear infinite;
|
||||
@keyframes rotating {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
```
|
||||
|
||||
Now, when the `ngIf` changes, `ngAnimate` will see the spinner animation and use
|
||||
it to animate the `enter`/`leave` event, which doesn't work because
|
||||
the animation is infinite. The element will still be added / removed after a timeout, but there will be a
|
||||
noticable delay.
|
||||
Now, when the `ngIf` expression changes, `ngAnimate` will see the spinner animation and use it to
|
||||
animate the `enter`/`leave` event, which doesn't work because the animation is infinite. The element
|
||||
will still be added / removed after a timeout, but there will be a noticeable delay.
|
||||
|
||||
This might also happen because some third-party frameworks place animation duration defaults
|
||||
across many element or className selectors in order to make their code small and reuseable.
|
||||
This might also happen because some third-party frameworks place animation duration defaults across
|
||||
many element or className selectors in order to make their code small and reusable.
|
||||
|
||||
You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class that is added
|
||||
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
|
||||
You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class, that is added for
|
||||
the whole duration of each animation. Simply overwrite the transition / animation duration. In the
|
||||
case of the spinner, this would be:
|
||||
|
||||
```css
|
||||
.spinner.ng-animate {
|
||||
transition: 0s none;
|
||||
animation: 0s none;
|
||||
animation: 0s none;
|
||||
transition: 0s none;
|
||||
}
|
||||
```
|
||||
|
||||
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
|
||||
than any styles that are independent from ngAnimate.
|
||||
If you do have CSS transitions / animations defined for the animation events, make sure they have a
|
||||
higher priority than any styles that are not related to `ngAnimate`.
|
||||
|
||||
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
|
||||
You can also use one of the other
|
||||
{@link guide/animations#how-to-selectively-enable-disable-and-skip-animations
|
||||
strategies to disable animations}.
|
||||
|
||||
|
||||
### Enable animations for elements outside of the Angular application DOM tree: {@link ng.$animate#pin $animate.pin()}
|
||||
## Enable animations outside of the application DOM tree: {@link ng.$animate#pin $animate.pin()}
|
||||
|
||||
Before animating, `ngAnimate` checks to see if the element being animated is inside the application DOM tree,
|
||||
and if it is not, no animation is run. Usually, this is not a problem as most apps use the `ngApp`
|
||||
attribute / bootstrap the app on the `html` or `body` element.
|
||||
Before animating, `ngAnimate` checks if the animated element is inside the application DOM tree. If
|
||||
not, no animation is run. Usually, this is not a problem since most apps use the `html` or `body`
|
||||
elements as their root.
|
||||
|
||||
Problems arise when the application is bootstrapped on a different element, and animations are
|
||||
attempted on elements that are outside the application tree, e.g. when libraries append popup and modal
|
||||
elements as the last child in the body tag.
|
||||
attempted on elements that are outside the application tree, e.g. when libraries append popup or
|
||||
modal elements to the body tag.
|
||||
|
||||
You can use {@link ng.$animate#pin `$animate.pin(elementToAnimate, parentHost)`} to specify that an
|
||||
element belongs to your application. Simply call it before the element is added to the DOM / before
|
||||
the animation starts, with the element you want to animate, and the element which should be its
|
||||
assumed parent.
|
||||
You can use {@link ng.$animate#pin `$animate.pin(element, parentHost)`} to associate an element with
|
||||
another element that belongs to your application. Simply call it before the element is added to the
|
||||
DOM / before the animation starts, with the element you want to animate, and the element which
|
||||
should be its assumed parent.
|
||||
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
For a full breakdown of each method available on `$animate`, see the
|
||||
{@link ng.$animate API documentation}.
|
||||
|
||||
To see a complete demo, see the {@link tutorial/step_14 animation step within the AngularJS phonecat tutorial}.
|
||||
To see a complete demo, see the {@link tutorial/step_14 animation step in the phonecat tutorial}.
|
||||
|
||||
@@ -40,8 +40,8 @@ initialization.
|
||||
<html ng-app>
|
||||
|
||||
3. If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
`ng:`.)
|
||||
when running the page in the XHTML mode. (This is here for historical reasons, and we no longer
|
||||
recommend use of `ng:`.)
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,14 +39,8 @@ Components can be registered using the `.component()` method of an Angular modul
|
||||
});
|
||||
</file>
|
||||
<file name="heroDetail.js">
|
||||
|
||||
function HeroDetailController() {
|
||||
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '='
|
||||
}
|
||||
@@ -462,7 +456,7 @@ The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
|
||||
|
||||
**Controller Test:**
|
||||
```js
|
||||
describe('component: heroDetail', function() {
|
||||
describe('HeroDetailController', function() {
|
||||
var $componentController;
|
||||
|
||||
beforeEach(module('heroApp'));
|
||||
@@ -470,15 +464,6 @@ describe('component: heroDetail', function() {
|
||||
$componentController = _$componentController_;
|
||||
}));
|
||||
|
||||
it('should expose a `hero` object', function() {
|
||||
// Here we are passing actual bindings to the component
|
||||
var bindings = {hero: {name: 'Wolverine'}};
|
||||
var ctrl = $componentController('heroDetail', null, bindings);
|
||||
|
||||
expect(ctrl.hero).toBeDefined();
|
||||
expect(ctrl.hero.name).toBe('Wolverine');
|
||||
});
|
||||
|
||||
it('should call the `onDelete` binding, when deleting the hero', function() {
|
||||
var onDeleteSpy = jasmine.createSpy('onDelete');
|
||||
var bindings = {hero: {}, onDelete: onDeleteSpy};
|
||||
|
||||
@@ -186,7 +186,7 @@ Right now, the `InvoiceController` contains all logic of our example. When the a
|
||||
is a good practice to move view-independent logic from the controller into a
|
||||
<a name="service">{@link services service}</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
from the web, e.g. by calling the [Fixer.io](http://fixer.io) exchange rate API, without changing the controller.
|
||||
|
||||
Let's refactor our example and move the currency conversion into a service in another file:
|
||||
|
||||
@@ -300,7 +300,7 @@ to something shorter like `a`.
|
||||
|
||||
## Accessing the backend
|
||||
|
||||
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
|
||||
Let's finish our example by fetching the exchange rates from the [Fixer.io](http://fixer.io) exchange rate API.
|
||||
The following example shows how this is done with Angular:
|
||||
|
||||
<example name="guide-concepts-3" ng-app-included="true">
|
||||
@@ -323,10 +323,6 @@ The following example shows how this is done with Angular:
|
||||
<file name="finance3.js">
|
||||
angular.module('finance3', [])
|
||||
.factory('currencyConverter', ['$http', function($http) {
|
||||
var YAHOO_FINANCE_URL_PATTERN =
|
||||
'//query.yahooapis.com/v1/public/yql?q=select * from ' +
|
||||
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&' +
|
||||
'env=store://datatables.org/alltableswithkeys';
|
||||
var currencies = ['USD', 'EUR', 'CNY'];
|
||||
var usdToForeignRates = {};
|
||||
|
||||
@@ -335,15 +331,10 @@ The following example shows how this is done with Angular:
|
||||
};
|
||||
|
||||
var refresh = function() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
var url = 'https://api.fixer.io/latest?base=USD&symbols=' + currencies.join(",");
|
||||
return $http.get(url).then(function(response) {
|
||||
var newUsdToForeignRates = {};
|
||||
angular.forEach(response.data.query.results.rate, function(rate) {
|
||||
var currency = rate.id.substring(3,6);
|
||||
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
|
||||
});
|
||||
usdToForeignRates = newUsdToForeignRates;
|
||||
usdToForeignRates = response.data.rates;
|
||||
usdToForeignRates['USD'] = 1;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ The other forms shown above are accepted for legacy reasons but we advise you to
|
||||
`$compile` can match directives based on element names (E), attributes (A), class names (C),
|
||||
and comments (M).
|
||||
|
||||
The built-in the AngularJS directives show in their documentation page which type of matching they support.
|
||||
The built-in 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.
|
||||
|
||||
@@ -139,7 +139,7 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[Pluralsight (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Pluralsight](https://www.pluralsight.com/search?q=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
|
||||
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs),
|
||||
|
||||
@@ -281,18 +281,18 @@ categories as you need.
|
||||
#### Selection Keywords
|
||||
|
||||
The selection keywords can be either exact matches or language dependent [plural
|
||||
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
|
||||
categories](http://cldr.unicode.org/index/cldr-spec/plural-rules).
|
||||
|
||||
Exact matches are written as the equal sign followed by the exact value. `=0`, `=1`, `=2` and
|
||||
`=123` are all examples of exact matches. Note that there should be no space between the equal sign
|
||||
and the numeric value.
|
||||
|
||||
Plural category matches are single words corresponding to the [plural
|
||||
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) of
|
||||
the CLDR plural category spec. These categories vary by locale. The "en" (English) locale, for
|
||||
example, defines just "one" and "other" while the "ga" (Irish) locale defines "one", "two", "few",
|
||||
"many" and "other". Typically, you would just write the categories for your language. During
|
||||
translation, the translators will add or remove more categories depending on the target locale.
|
||||
categories](http://cldr.unicode.org/index/cldr-spec/plural-rules) of the CLDR plural category spec.
|
||||
These categories vary by locale. The "en" (English) locale, for example, defines just "one" and
|
||||
"other" while the "ga" (Irish) locale defines "one", "two", "few", "many" and "other". Typically,
|
||||
you would just write the categories for your language. During translation, the translators will add
|
||||
or remove more categories depending on the target locale.
|
||||
|
||||
Exact matches always win over keyword matches. Therefore, if you define both `=0` and `zero`, when
|
||||
the value of the expression is zero, the `=0` message is the one that will be selected. (The
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** AngularJS 1.3 has dropped support for IE8. Read more about it on
|
||||
[our blog](http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
|
||||
[our blog](https://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
|
||||
AngularJS 1.2 will continue to support IE8, but the core team does not plan to spend time
|
||||
addressing issues specific to IE8 or earlier.
|
||||
</div>
|
||||
@@ -19,7 +19,7 @@ on IE.
|
||||
The project currently supports and will attempt to fix bugs for IE9 and above. The continuous
|
||||
integration server runs all the tests against IE9, IE10, and IE11. See
|
||||
[Travis CI](https://travis-ci.org/angular/angular.js) and
|
||||
[ci.angularjs.org](http://ci.angularjs.org).
|
||||
[ci.angularjs.org](https://ci.angularjs.org).
|
||||
|
||||
We do not run tests on IE8 and below. A subset of the AngularJS functionality may work on these
|
||||
browsers, but it is up to you to test and decide whether it works for your particular app.
|
||||
@@ -27,8 +27,8 @@ browsers, but it is up to you to test and decide whether it works for your parti
|
||||
|
||||
To ensure your Angular application works on IE please consider:
|
||||
|
||||
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
|
||||
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
|
||||
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome, Firefox,
|
||||
Safari and Edge but does not work in Internet Explorer (even 11).
|
||||
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
|
||||
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
|
||||
with `type="submit"` before Angular has a chance to interpolate it.
|
||||
|
||||
@@ -32,7 +32,7 @@ If the interpolated value is not a `String`, it is computed as follows:
|
||||
- `undefined` and `null` are converted to `''`
|
||||
- if the value is an object that is not a `Number`, `Date` or `Array`, $interpolate looks for
|
||||
a custom `toString()` function on the object, and uses that. Custom means that
|
||||
`myObject.toString !== `Object.prototype.toString`.
|
||||
`myObject.toString !== Object.prototype.toString`.
|
||||
- if the above doesn't apply, `JSON.stringify` is used.
|
||||
|
||||
### Binding to boolean attributes
|
||||
|
||||
@@ -75,8 +75,8 @@ commits for more info.
|
||||
- **$location** now uses `'!'` as the default hash-prefix for hash-bang URLs, instead of the empty
|
||||
string. ([Details](guide/migration#commit-aa077e8))
|
||||
|
||||
- **$compile** will (by default) not pre-assign bindings on controller instances.
|
||||
([Details](guide/migration#commit-bcd0d4))
|
||||
- **$compile** will (by default) not pre-assign bindings on component/directive controller
|
||||
instances. ([Details](guide/migration#commit-bcd0d4))
|
||||
|
||||
- **http** imposes additional restrictions to **JSONP** requests for security reasons
|
||||
(see [details](guide/migration#migrate1.5to1.6-ng-services-$http) below):
|
||||
@@ -412,14 +412,14 @@ if the option does not provide a value attribute.
|
||||
<major />
|
||||
<a name="commit-bcd0d4"></a>
|
||||
**Due to [bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858)**,
|
||||
pre-assigning bindings on controller instances is disabled by default. It is still possible to turn
|
||||
it back on, which should help during the migration. Pre-assigning bindings has been deprecated and
|
||||
will be removed in a future version, so we strongly recommend migrating your applications to not
|
||||
rely on it as soon as possible.
|
||||
pre-assigning bindings on component/directive controller instances is disabled by default, which
|
||||
means that they will no longer be available inside the constructors. It is still possible to turn it
|
||||
back on, which should help during the migration. Pre-assigning bindings has been deprecated and will
|
||||
be removed in a future version, so we strongly recommend migrating your applications to not 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 guaranteed to always be called _after_ the bindings have been
|
||||
assigned.
|
||||
`$onInit()` method, which is guaranteed to always be called _after_ the bindings have been assigned.
|
||||
|
||||
Before:
|
||||
|
||||
@@ -484,14 +484,6 @@ lifecycle hook), you may need to manually call `$onInit()` from your constructor
|
||||
})
|
||||
```
|
||||
|
||||
<hr />
|
||||
<minor />
|
||||
**Due to [13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**,
|
||||
on **IE11 only**, consecutive text nodes will always get merged. Previously, they would not get
|
||||
merged if they had no parent. The new behavior, which fixes an IE11 bug affecting interpolation
|
||||
under certain circumstances, might in some edge-cases have unexpected side effects that you should
|
||||
be aware of. Please, check the commit message for more details.
|
||||
|
||||
<hr />
|
||||
<minor />
|
||||
**Due to [04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa)**,
|
||||
@@ -588,7 +580,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
|
||||
@@ -1320,7 +1312,7 @@ jqLite/jQuery collections
|
||||
|
||||
#### Helper Functions:
|
||||
|
||||
The {@link angular.lowercase `angular.lowercase`} and {@link angular.uppercase `angular.uppercase`} functions have been **deprecated** and will be removed
|
||||
The `angular.lowercase` and `angular.uppercase` functions have been **deprecated** and will be removed
|
||||
in version 1.7.0. It is recommended to use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) and [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) functions instead.
|
||||
|
||||
|
||||
@@ -1692,12 +1684,12 @@ before the $parsers are applied. Previously, the `$modelValue`
|
||||
|
||||
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).
|
||||
`Date` and `Number` respectively (starting with AngularJS 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
|
||||
in AngularJS 1.2, the value that was validated by pattern could have
|
||||
been manipulated by the $parsers, as all validation was done
|
||||
inside this pipeline.
|
||||
|
||||
|
||||
@@ -100,8 +100,7 @@ Protection from JSON Hijacking is provided if the server prefixes all JSON reque
|
||||
Angular will automatically strip the prefix before processing it as JSON.
|
||||
For more information please visit {@link $http#json-vulnerability-protection JSON Hijacking Protection}.
|
||||
|
||||
Bear in mind that calling `$http.jsonp`, like in [our Yahoo! finance example](https://docs.angularjs.org/guide/concepts#accessing-the-backend),
|
||||
gives the remote server (and, if the request is not secured, any Man-in-the-Middle attackers)
|
||||
Bear in mind that calling `$http.jsonp` gives the remote server (and, if the request is not secured, any Man-in-the-Middle attackers)
|
||||
instant remote code execution in your application: the result of these requests is handed off
|
||||
to the browser as regular `<script>` tag.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ curly-brace {@link expression expression} bindings:
|
||||
string expression 'buttonText'
|
||||
wrapped in "{{ }}" markup -->
|
||||
<button ng-click="changeFoo()">{{buttonText}}</button>
|
||||
<script src="angular.js">
|
||||
<script src="angular.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
+95
-47
@@ -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,20 +136,18 @@ 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,
|
||||
but we don't actively test changes against it. You can also expect browsers to work that share
|
||||
a large part of their codebase with a browser we test, such as Opera > version 12
|
||||
If a browser is untested, it doesn't mean it won't work. You can also expect browsers to work that
|
||||
share a large part of their codebase with a browser we test, such as Opera 15 or newer
|
||||
(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 +162,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 +216,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 +227,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 +246,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 +259,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 +273,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 +297,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.
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+11
-8
@@ -52,13 +52,14 @@ var getMergedEslintConfig = function(filepath) {
|
||||
};
|
||||
};
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
var copyComponent = function(component, pattern, base, sourceFolder, packageFile) {
|
||||
pattern = pattern || '/**/*';
|
||||
base = base || '';
|
||||
sourceFolder = sourceFolder || '../node_modules';
|
||||
packageFile = packageFile || 'package.json';
|
||||
var version = require(path.resolve(sourceFolder, component, packageFile)).version;
|
||||
return gulp
|
||||
.src(sourceFolder + '/' + component + pattern)
|
||||
.src(sourceFolder + '/' + component + pattern, {base: sourceFolder + '/' + component + '/' + base})
|
||||
.pipe(gulp.dest(outputFolder + '/components/' + component + '-' + version));
|
||||
};
|
||||
|
||||
@@ -96,12 +97,14 @@ gulp.task('assets', function() {
|
||||
.pipe(gulp.dest(outputFolder));
|
||||
}
|
||||
})),
|
||||
copyComponent('bootstrap', '/dist/**/*'),
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr', '/*.js'),
|
||||
copyComponent('google-code-prettify'),
|
||||
copyComponent('jquery', '/dist/*.js'),
|
||||
copyComponent('marked', '/**/*.js')
|
||||
copyComponent('bootstrap', '/dist/css/bootstrap?(.min).css', 'dist'),
|
||||
copyComponent('bootstrap', '/dist/fonts/*', 'dist'),
|
||||
copyComponent('open-sans-fontface', '/fonts/{Regular,Semibold,Bold}/*'),
|
||||
copyComponent('lunr', '/lunr?(.min).js'),
|
||||
copyComponent('google-code-prettify', '/**/{lang-css,prettify}.js'),
|
||||
copyComponent('jquery', '/dist/jquery.js', 'dist'),
|
||||
copyComponent('marked', '/lib/marked.js'),
|
||||
copyComponent('marked', '/marked.min.js')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"hosting": {
|
||||
"public": "build/docs",
|
||||
"ignore": [
|
||||
"/index.html",
|
||||
"/index-debug.html",
|
||||
"/index-jquery.html"
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "/index-production.html"
|
||||
},
|
||||
{
|
||||
"source": "/index.html",
|
||||
"destination": "/index-production.html"
|
||||
},
|
||||
{
|
||||
"source": "**/*!(.jpg|.jpeg|.gif|.png|.html|.js|.json|.css|.svg|.ttf|.woff|.woff2|.eot)",
|
||||
"destination": "/index-production.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,21 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
goog.i18n.currency.tier2Enabled_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Tests if currency is available.
|
||||
*
|
||||
* Note: If the currency is not available it might be in the tier2 currency set:
|
||||
* {@link goog.i18n.currency.CurrencyInfoTier2}. If that is the case call
|
||||
* {@link goog.i18n.currency.addTier2Support} before calling any other function
|
||||
* in this namespace.
|
||||
*
|
||||
* @param {string} currencyCode Currency code to tested.
|
||||
* @return {boolean} If the currency is available.
|
||||
*/
|
||||
goog.i18n.currency.isAvailable = function(currencyCode) {
|
||||
return currencyCode in goog.i18n.currency.CurrencyInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will add tier2 currency support. Be default, only tier1
|
||||
* (most popular currencies) are supported. If an application really needs
|
||||
@@ -84,6 +99,7 @@ goog.i18n.currency.addTier2Support = function() {
|
||||
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
|
||||
* currency sign is added if it is different from currency code. Each currency
|
||||
* is unique in this form. The negative side is that ISO code looks weird in
|
||||
@@ -92,6 +108,9 @@ goog.i18n.currency.addTier2Support = function() {
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Global currency pattern string for given currency.
|
||||
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
|
||||
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
|
||||
* {@link goog.i18n.NumberFormat.CurrencyStyle.GLOBAL}
|
||||
*/
|
||||
goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
@@ -119,12 +138,16 @@ goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
* Local currency pattern is the most frequently used pattern in currency's
|
||||
* native region. It does not care about how it is distinguished from other
|
||||
* currencies.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Local currency pattern string for given currency.
|
||||
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
|
||||
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
|
||||
* {@link goog.i18n.NumberFormat.CurrencyStyle.LOCAL}
|
||||
*/
|
||||
goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
@@ -145,6 +168,7 @@ goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
* Portable currency pattern is a compromise between local and global. It is
|
||||
* not a mere blend or mid-way between the two. Currency sign is chosen so that
|
||||
* it looks familiar to native users. It also has enough information to
|
||||
@@ -154,6 +178,9 @@ goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Portable currency pattern string for given currency.
|
||||
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
|
||||
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
|
||||
* {@link goog.i18n.NumberFormat.CurrencyStyle.PORTABLE}
|
||||
*/
|
||||
goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
@@ -174,12 +201,17 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function returns the default currency sign position. Some applications
|
||||
* This function returns the default currency sign's position. Some applications
|
||||
* may want to handle currency sign and currency amount separately. This
|
||||
* function can be used in such situations to correctly position the currency
|
||||
* sign relative to the amount.
|
||||
*
|
||||
* To match the behavior of ICU, position is not determined by display locale.
|
||||
* Use {@link goog.i18n.NumberFormat#isCurrencyCodeBeforeValue} for a locale
|
||||
* aware version of this API (recommended). isPrefixSignPosition() returns the
|
||||
* default currency sign's position in the currency's default locale (e.g. 'en'
|
||||
* for 'USD'), but most commonly the position is needed for the locale in which
|
||||
* the number is going to be displayed. For example, in 'fr' 10.10 USD would be
|
||||
* displayed as '10,10 $'.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {boolean} true if currency should be positioned before amount field.
|
||||
@@ -267,6 +299,10 @@ goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
|
||||
* 18: two decimals precision (2), currency sign last (16), no space (0)
|
||||
* 50: two decimals precision (2), currency sign last (16), space (32)
|
||||
*
|
||||
* It's not recommended to read this data directly. Format numbers using
|
||||
* {@link goog.i18n.NumberFormat} with
|
||||
* {@link goog.i18n.NumberFormat.Format.CURRENCY} instead.
|
||||
*
|
||||
* @const {!Object<!Array<?>>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfo = {
|
||||
@@ -310,7 +346,7 @@ goog.i18n.currency.CurrencyInfo = {
|
||||
'NOK': [50, 'kr', 'NOkr'],
|
||||
'PAB': [2, 'B/.', 'B/.'],
|
||||
'PEN': [2, 'S/.', 'S/.'],
|
||||
'PHP': [2, '\u20B1', 'Php'],
|
||||
'PHP': [2, '\u20B1', 'PHP'],
|
||||
'PKR': [0, 'Rs', 'PKRs.'],
|
||||
'PLN': [50, 'z\u0142', 'z\u0142'],
|
||||
'RON': [2, 'RON', 'RON'],
|
||||
@@ -334,6 +370,11 @@ goog.i18n.currency.CurrencyInfo = {
|
||||
|
||||
/**
|
||||
* Tier 2 currency information.
|
||||
*
|
||||
* It's not recommended to read this data directly. Format numbers using
|
||||
* {@link goog.i18n.NumberFormat} with
|
||||
* {@link goog.i18n.NumberFormat.Format.CURRENCY} instead.
|
||||
*
|
||||
* @const {!Object<!Array<?>>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfoTier2 = {
|
||||
@@ -431,7 +472,7 @@ goog.i18n.currency.CurrencyInfoTier2 = {
|
||||
'XAF': [0, 'FCFA', 'FCFA'],
|
||||
'XCD': [2, '$', 'EC$'],
|
||||
'XOF': [0, 'CFA', 'CFA'],
|
||||
'XPF': [0, 'FCFP', 'FCFP'],
|
||||
'XPF': [48, 'FCFP', 'FCFP'],
|
||||
'ZMW': [0, 'ZMW', 'ZMW'],
|
||||
'ZWD': [0, '$', 'Z$']
|
||||
};
|
||||
|
||||
+1609
-2622
File diff suppressed because it is too large
Load Diff
+4146
-12018
File diff suppressed because it is too large
Load Diff
+494
-1936
File diff suppressed because it is too large
Load Diff
+4384
-2027
File diff suppressed because it is too large
Load Diff
+21
-33
@@ -17,7 +17,7 @@
|
||||
*
|
||||
* This file is autogenerated by script:
|
||||
* http://go/generate_pluralrules.py
|
||||
* File generated from CLDR ver. 29
|
||||
* File generated from CLDR ver. 31.0.1
|
||||
*
|
||||
* Before check in, this file could have been manually edited. This is to
|
||||
* incorporate changes before we could fix CLDR. All manual modification must be
|
||||
@@ -128,22 +128,6 @@ goog.i18n.pluralRules.filSelect_ = function(n, opt_precision) {
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for pt_PT locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @param {number=} opt_precision Precision for number formatting, if not default.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.pt_PTSelect_ = function(n, opt_precision) {
|
||||
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
|
||||
if (n == 1 && vf.v == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for br locale
|
||||
*
|
||||
@@ -240,6 +224,22 @@ goog.i18n.pluralRules.frSelect_ = function(n, opt_precision) {
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for pt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @param {number=} opt_precision Precision for number formatting, if not default.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
|
||||
var i = n | 0;
|
||||
if (i >= 0 && i <= 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for cs locale
|
||||
*
|
||||
@@ -559,21 +559,6 @@ goog.i18n.pluralRules.gaSelect_ = function(n, opt_precision) {
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for pt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @param {number=} opt_precision Precision for number formatting, if not default.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
|
||||
if (n >= 0 && n <= 2 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plural select rules for es locale
|
||||
*
|
||||
@@ -820,6 +805,9 @@ if (goog.LOCALE == 'am') {
|
||||
if (goog.LOCALE == 'ar') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'az') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
|
||||
}
|
||||
@@ -1061,7 +1049,7 @@ if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.pt_PTSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'ro') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
|
||||
|
||||
@@ -75,7 +75,6 @@ describe('findLocaleId', function() {
|
||||
it('should not find localeId if data is missing', function() {
|
||||
expect(findLocaleId('', 'num')).toBeUndefined();
|
||||
expect(findLocaleId('aa', 'datetime')).toBeUndefined();
|
||||
expect(findLocaleId('aa', 'randomType')).toBeUndefined();
|
||||
expect(findLocaleId('NumberFormatSymbols_en', 'datetime')).toBeUndefined();
|
||||
expect(findLocaleId('DateTimeSymbols_en', 'num')).toBeUndefined();
|
||||
});
|
||||
|
||||
+49
-1
@@ -1,6 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
var parsePattern = require('../src/parser.js').parsePattern;
|
||||
var parser = require('../src/parser');
|
||||
var ensureDecimalSep = parser.ensureDecimalSep;
|
||||
var parsePattern = parser.parsePattern;
|
||||
|
||||
describe('ensureDecimalSep', function() {
|
||||
it('should leave patterns with DECIMAL_SEP untouched', function() {
|
||||
[
|
||||
'#,##0.00',
|
||||
'$#,##0.00',
|
||||
'#,##0.00$',
|
||||
'$0.00',
|
||||
'0.00$',
|
||||
'0.0',
|
||||
'#,##0.',
|
||||
'0.'
|
||||
].forEach(function(pattern) {
|
||||
expect(ensureDecimalSep(pattern)).toBe(pattern);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add a DECIMAL_SEP in patterns that don\'t have one (after the last ZERO)', function() {
|
||||
var patterns = {
|
||||
'#,##000': '#,##000.',
|
||||
'$#,#0#00': '$#,#0#00.',
|
||||
'#,##000$': '#,##000.$',
|
||||
'$000': '$000.',
|
||||
'000$': '000.$',
|
||||
'00': '00.',
|
||||
'#,##0': '#,##0.',
|
||||
'0': '0.'
|
||||
};
|
||||
|
||||
Object.keys(patterns).forEach(function(input) {
|
||||
var output = patterns[input];
|
||||
expect(ensureDecimalSep(input)).toBe(output);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsePattern', function() {
|
||||
function parseAndExpect(pattern, pp, np, ps, ns, mii, mif, maf, g, lg) {
|
||||
@@ -28,6 +65,11 @@ describe('parsePattern', function() {
|
||||
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
||||
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
|
||||
|
||||
// Even patterns without a DECIMAL_SEP
|
||||
parseAndExpect('#,##0', '', '-', '', '', 1, 0, 0, 3, 3);
|
||||
parseAndExpect('+#,##0', '+', '-+', '', '', 1, 0, 0, 3, 3);
|
||||
parseAndExpect('#,#0;+#,#0', '', '+', '', '', 1, 0, 0, 2, 2);
|
||||
parseAndExpect('#,##,##0+;(#,##,##0)', '', '(', '+', ')', 1, 0, 0, 2, 3);
|
||||
});
|
||||
|
||||
it('should parse CURRENCY patterns', function() {
|
||||
@@ -51,5 +93,11 @@ describe('parsePattern', function() {
|
||||
parseAndExpect('\u00A4 #,##0.00;\u00A4 #,##0.00-',
|
||||
'\u00A4 ', '\u00A4 ', '', '-', 1, 2, 2, 3, 3);
|
||||
parseAndExpect('\u00A4 #,##,##0.00', '\u00A4 ', '-\u00A4 ', '', '', 1, 2, 2, 2, 3);
|
||||
|
||||
// Even patterns without a DECIMAL_SEP
|
||||
parseAndExpect('#,##0 \u00A4', '', '-', ' \u00A4', ' \u00A4', 1, 0, 0, 3, 3);
|
||||
parseAndExpect('\u00A4 #,##0', '\u00A4 ', '-\u00A4 ', '', '', 1, 0, 0, 3, 3);
|
||||
parseAndExpect('#,#0 \u00A4;+#,#0\u00A4', '', '+', ' \u00A4', '\u00A4', 1, 0, 0, 2, 2);
|
||||
parseAndExpect('\u00A4 #,##,##0;(\u00A4 #,##,##0)', '\u00A4 ', '(\u00A4 ', '', ')', 1, 0, 0, 2, 3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ var Q = require('q'),
|
||||
localeInfo = {};
|
||||
|
||||
|
||||
var NG_LOCALE_DIR = '../src/ngLocale/';
|
||||
var NG_LOCALE_DIR = __dirname + '/../../src/ngLocale/';
|
||||
|
||||
|
||||
function readSymbols() {
|
||||
|
||||
+28
-6
@@ -4,13 +4,26 @@
|
||||
* A simple parser to parse a number format into a pattern object
|
||||
*/
|
||||
|
||||
exports.ensureDecimalSep = ensureDecimalSep;
|
||||
exports.parsePattern = parsePattern;
|
||||
|
||||
var PATTERN_SEP = ';',
|
||||
DECIMAL_SEP = '.',
|
||||
GROUP_SEP = ',',
|
||||
ZERO = '0',
|
||||
DIGIT = '#';
|
||||
var PATTERN_SEP = ';',
|
||||
DECIMAL_SEP = '.',
|
||||
GROUP_SEP = ',',
|
||||
DIGIT = '#',
|
||||
ZERO = '0',
|
||||
LAST_ZERO_RE = /^(.*0)(?!0)(.*)$/;
|
||||
|
||||
/**
|
||||
* Helper function for parser.
|
||||
* Ensures that `pattern` (e.g #,##0.###) contains a DECIMAL_SEP, which is necessary for further
|
||||
* parsing. If a pattern does not include one, it is added after the last ZERO (which is the last
|
||||
* thing before the `posSuf` - if any).
|
||||
*/
|
||||
function ensureDecimalSep(pattern) {
|
||||
return (pattern.indexOf(DECIMAL_SEP) !== -1)
|
||||
? pattern : pattern.replace(LAST_ZERO_RE, '$1' + DECIMAL_SEP + '$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* main function for parser
|
||||
@@ -33,7 +46,16 @@ function parsePattern(pattern) {
|
||||
positive = patternParts[0],
|
||||
negative = patternParts[1];
|
||||
|
||||
var positiveParts = positive.split(DECIMAL_SEP),
|
||||
// The parsing logic further below assumes that there will always be a DECIMAL_SEP in the pattern.
|
||||
// However, some locales (e.g. agq_CM) do not have one, thus we add one after the last ZERO
|
||||
// (which is the last thing before the `posSuf` - if any). Since there will be no ZEROs or DIGITs
|
||||
// after DECIMAL_SEP, `min/maxFrac` will remain 0 (which is accurate - no fraction digits) and
|
||||
// `posSuf` will be processed correctly.
|
||||
// For example `#,##0$` would be converted to `#,##0.$`, which would (correctly) result in:
|
||||
// `minFrac: 0`, `maxFrac: 0`, `posSuf: '$'`
|
||||
// Note: We shouldn't modify `positive` directly, because it is used to parse the negative part.)
|
||||
var positiveWithDecimalSep = ensureDecimalSep(positive),
|
||||
positiveParts = positiveWithDecimalSep.split(DECIMAL_SEP),
|
||||
integer = positiveParts[0],
|
||||
fraction = positiveParts[1];
|
||||
|
||||
|
||||
@@ -9,18 +9,18 @@ var propertiesToExtract = {'IDS': 'Y', 'IDC': 'Y'};
|
||||
|
||||
function main() {
|
||||
extractValues(
|
||||
fs.createReadStream('./ucd/src/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
|
||||
fs.createReadStream(__dirname + '/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
|
||||
propertiesToExtract,
|
||||
writeFile);
|
||||
|
||||
function writeFile(validRanges) {
|
||||
var code = generateCode(validRanges);
|
||||
try {
|
||||
fs.lstatSync('../src/ngParseExt');
|
||||
fs.lstatSync(__dirname + '/../../../src/ngParseExt');
|
||||
} catch (e) {
|
||||
fs.mkdirSync('../src/ngParseExt');
|
||||
fs.mkdirSync(__dirname + '/../../../src/ngParseExt');
|
||||
}
|
||||
fs.writeFile('../src/ngParseExt/ucd.js', code);
|
||||
fs.writeFile(__dirname + '/../../../src/ngParseExt/ucd.js', code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-6
@@ -10,8 +10,16 @@ module.exports = function(config, specificOptions) {
|
||||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 2,
|
||||
browserNoActivityTimeout: 30000,
|
||||
|
||||
|
||||
reporters: ['dots'],
|
||||
specReporter: {
|
||||
maxLogLines: 5, // limit number of lines logged per test
|
||||
suppressErrorSummary: true, // do not print error summary
|
||||
suppressFailed: false, // do not print information about failed tests
|
||||
suppressPassed: true, // do not print information about passed tests
|
||||
suppressSkipped: false, // do not print information about skipped tests
|
||||
showSpecTiming: false, // print the time elapsed for each spec
|
||||
failFast: false // test would finish with error when a first fail occurs.
|
||||
},
|
||||
// SauceLabs config for local development.
|
||||
sauceLabs: {
|
||||
testName: specificOptions.testName || 'AngularJS',
|
||||
@@ -37,12 +45,12 @@ module.exports = function(config, specificOptions) {
|
||||
'SL_Chrome': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '51'
|
||||
version: '59'
|
||||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '47'
|
||||
version: '54'
|
||||
},
|
||||
'SL_Safari_8': {
|
||||
base: 'SauceLabs',
|
||||
@@ -74,6 +82,12 @@ module.exports = function(config, specificOptions) {
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'microsoftedge',
|
||||
platform: 'Windows 10',
|
||||
version: '14'
|
||||
},
|
||||
'SL_iOS': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
@@ -120,11 +134,18 @@ module.exports = function(config, specificOptions) {
|
||||
os: 'Windows',
|
||||
os_version: '8.1'
|
||||
},
|
||||
'BS_EDGE': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'edge',
|
||||
browser_version: '14',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_iOS': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6',
|
||||
device: 'iPhone 6S',
|
||||
os: 'ios',
|
||||
os_version: '8.0'
|
||||
os_version: '9.3'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ PORTS.forEach(function(port) {
|
||||
|
||||
var tunnel = new BrowserStackTunnel({
|
||||
key: ACCESS_KEY,
|
||||
tunnelIdentifier: TUNNEL_IDENTIFIER,
|
||||
localIdentifier: TUNNEL_IDENTIFIER,
|
||||
hosts: hosts
|
||||
});
|
||||
|
||||
|
||||
+20
-14
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"yarn": ">=0.17.9",
|
||||
"yarn": ">=0.21.3",
|
||||
"grunt": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -24,12 +24,13 @@
|
||||
"benchmark": "1.x.x",
|
||||
"bootstrap": "3.1.1",
|
||||
"bower": "~1.3.9",
|
||||
"browserstacktunnel-wrapper": "^1.4.2",
|
||||
"browserstacktunnel-wrapper": "2.0.0",
|
||||
"canonical-path": "0.0.2",
|
||||
"changez": "^2.1.1",
|
||||
"changez-angular": "^2.1.3",
|
||||
"changez-angular": "^2.1.2",
|
||||
"cheerio": "^0.17.0",
|
||||
"commitizen": "^2.3.0",
|
||||
"commitplease": "^2.7.10",
|
||||
"cross-spawn": "^4.0.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
@@ -56,19 +57,20 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-core": "2.5.2",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "^2.2.0",
|
||||
"jquery": "^3.1.1",
|
||||
"karma": "^1.1.2",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-junit-reporter": "^1.1.0",
|
||||
"jquery": "^3.2.1",
|
||||
"karma": "^1.7.0",
|
||||
"karma-browserstack-launcher": "^1.2.0",
|
||||
"karma-chrome-launcher": "^2.1.1",
|
||||
"karma-firefox-launcher": "^1.0.1",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"karma-ng-scenario": "^1.0.0",
|
||||
"karma-sauce-launcher": "^1.0.0",
|
||||
"karma-sauce-launcher": "^1.1.0",
|
||||
"karma-script-launcher": "^1.0.0",
|
||||
"karma-spec-reporter": "^0.0.31",
|
||||
"load-grunt-tasks": "^3.5.0",
|
||||
"lodash": "~2.4.1",
|
||||
"log4js": "^0.6.27",
|
||||
@@ -78,7 +80,7 @@
|
||||
"npm-run": "^4.1.0",
|
||||
"open-sans-fontface": "^1.4.0",
|
||||
"promises-aplus-tests": "~2.1.0",
|
||||
"protractor": "^4.0.10",
|
||||
"protractor": "^5.1.2",
|
||||
"q": "~1.0.0",
|
||||
"q-io": "^1.10.9",
|
||||
"qq": "^0.3.5",
|
||||
@@ -94,9 +96,13 @@
|
||||
"stringmap": "^0.2.2"
|
||||
},
|
||||
"dependencies": {},
|
||||
"commitplease": {
|
||||
"style": "angular",
|
||||
"nohook": true
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
Firebase for docs.angularjs.org
|
||||
===============================
|
||||
|
||||
The docs are deployed to Google Firebase hosting via Travis deployment config, which expects
|
||||
firebase.json and .firebaserc in the repository root.
|
||||
|
||||
See travis.yml for the complete deployment config.
|
||||
|
||||
See /scripts/code.angularjs.org-firebase/readme.firebase.code.md for the firebase deployment to
|
||||
code.angularjs.org
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "code-angularjs-org-338b8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"hosting": {
|
||||
"public": "public",
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/:version/docs",
|
||||
"destination": "/:version/docs/index.html",
|
||||
"type": 301
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/**",
|
||||
"function": "sendStoredFile"
|
||||
}
|
||||
]
|
||||
},
|
||||
"storage": {
|
||||
"rules": "storage.rules"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
'use strict';
|
||||
|
||||
const functions = require('firebase-functions');
|
||||
const gcs = require('@google-cloud/storage')();
|
||||
const path = require('path');
|
||||
|
||||
const gcsBucketId = `${process.env.GCLOUD_PROJECT}.appspot.com`;
|
||||
|
||||
const BROWSER_CACHE_DURATION = 300;
|
||||
const CDN_CACHE_DURATION = 600;
|
||||
|
||||
function sendStoredFile(request, response) {
|
||||
let filePathSegments = request.path.split('/').filter((segment) => {
|
||||
// Remove empty leading or trailing path parts
|
||||
return segment !== '';
|
||||
});
|
||||
|
||||
const version = filePathSegments[0];
|
||||
const isDocsPath = filePathSegments[1] === 'docs';
|
||||
const lastSegment = filePathSegments[filePathSegments.length - 1];
|
||||
const bucket = gcs.bucket(gcsBucketId);
|
||||
|
||||
let downloadSource;
|
||||
let fileName;
|
||||
|
||||
if (isDocsPath && filePathSegments.length === 2) {
|
||||
fileName = 'index.html';
|
||||
filePathSegments = [version, 'docs', fileName];
|
||||
} else {
|
||||
fileName = lastSegment;
|
||||
}
|
||||
|
||||
if (!fileName) {
|
||||
//Root
|
||||
return getDirectoryListing('/').catch(sendErrorResponse);
|
||||
}
|
||||
|
||||
downloadSource = path.join.apply(null, filePathSegments);
|
||||
|
||||
downloadAndSend(downloadSource).catch(error => {
|
||||
if (isDocsPath && error.code === 404) {
|
||||
fileName = 'index.html';
|
||||
filePathSegments = [version, 'docs', fileName];
|
||||
downloadSource = path.join.apply(null, filePathSegments);
|
||||
|
||||
return downloadAndSend(downloadSource);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}).catch(error => {
|
||||
|
||||
// If file not found, try the path as a directory
|
||||
return error.code === 404 ? getDirectoryListing(request.path.slice(1)) : Promise.reject(error);
|
||||
}).catch(sendErrorResponse);
|
||||
|
||||
function downloadAndSend(downloadSource) {
|
||||
|
||||
const file = bucket.file(downloadSource);
|
||||
|
||||
return file.getMetadata().then(data => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const readStream = file.createReadStream()
|
||||
.on('error', error => {
|
||||
reject(error);
|
||||
})
|
||||
.on('response', () => {
|
||||
resolve(response);
|
||||
});
|
||||
|
||||
response
|
||||
.status(200)
|
||||
.set({
|
||||
'Content-Type': data[0].contentType,
|
||||
'Cache-Control': `public, max-age=${BROWSER_CACHE_DURATION}, s-maxage=${CDN_CACHE_DURATION}`
|
||||
});
|
||||
|
||||
readStream.pipe(response);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function sendErrorResponse(error) {
|
||||
let code = 500;
|
||||
let message = `General error. Please try again later.
|
||||
If the error persists, please create an issue in the
|
||||
<a href="https://github.com/angular/angular.js/issues">AngularJS Github repository</a>`;
|
||||
|
||||
if (error.code === 404) {
|
||||
message = 'File or directory not found';
|
||||
code = 404;
|
||||
}
|
||||
|
||||
return response.status(code).send(message);
|
||||
}
|
||||
|
||||
function getDirectoryListing(path) {
|
||||
if (!path.endsWith('/')) path += '/';
|
||||
|
||||
const getFilesOptions = {
|
||||
delimiter: '/',
|
||||
autoPaginate: false
|
||||
};
|
||||
|
||||
if (path !== '/') getFilesOptions.prefix = path;
|
||||
|
||||
let fileList = [];
|
||||
let directoryList = [];
|
||||
|
||||
return getContent(getFilesOptions).then(() => {
|
||||
let contentList = '';
|
||||
|
||||
directoryList.forEach(directoryPath => {
|
||||
const dirName = directoryPath.split('/').reverse()[1];
|
||||
contentList += `<a href="${dirName}/">${dirName}/</a><br>`;
|
||||
});
|
||||
|
||||
fileList.forEach(file => {
|
||||
const fileName = file.metadata.name.split('/').pop();
|
||||
contentList += `<a href="${fileName}">${fileName}</a><br>`;
|
||||
});
|
||||
|
||||
// A trailing slash in the base creates correct relative links when the url is accessed
|
||||
// without trailing slash
|
||||
const base = request.originalUrl.endsWith('/') ? request.originalUrl : request.originalUrl + '/';
|
||||
|
||||
let directoryListing = `
|
||||
<base href="${base}">
|
||||
<h1>Index of ${path}</h1>
|
||||
<hr>
|
||||
<pre>${contentList}</pre>`;
|
||||
|
||||
return response
|
||||
.status(200)
|
||||
.set({
|
||||
'Cache-Control': `public, max-age=${BROWSER_CACHE_DURATION}, s-maxage=${CDN_CACHE_DURATION}`
|
||||
})
|
||||
.send(directoryListing);
|
||||
});
|
||||
|
||||
function getContent(options) {
|
||||
return bucket.getFiles(options).then(data => {
|
||||
const files = data[0];
|
||||
const nextQuery = data[1];
|
||||
const apiResponse = data[2];
|
||||
|
||||
if (!files.length && (!apiResponse || !apiResponse.prefixes)) {
|
||||
return Promise.reject({
|
||||
code: 404
|
||||
});
|
||||
}
|
||||
|
||||
fileList = fileList.concat(files);
|
||||
|
||||
if (apiResponse && apiResponse.prefixes) {
|
||||
directoryList = directoryList.concat(apiResponse.prefixes);
|
||||
}
|
||||
|
||||
if (nextQuery) {
|
||||
// If the results are paged, get the next page
|
||||
return getContent(nextQuery);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteOldSnapshotZip(event) {
|
||||
const object = event.data;
|
||||
|
||||
const bucketId = object.bucket;
|
||||
const filePath = object.name;
|
||||
const contentType = object.contentType;
|
||||
const resourceState = object.resourceState;
|
||||
|
||||
const bucket = gcs.bucket(bucketId);
|
||||
|
||||
if (contentType !== 'application/zip' ||
|
||||
!filePath.startsWith('snapshot/') ||
|
||||
resourceState === 'not_exists' // Deletion event
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
bucket.getFiles({
|
||||
prefix: 'snapshot/',
|
||||
delimiter: '/',
|
||||
autoPaginate: false
|
||||
}).then(function(data) {
|
||||
const files = data[0];
|
||||
|
||||
const oldZipFiles = files.filter(file => {
|
||||
return file.metadata.name !== filePath && file.metadata.contentType === 'application/zip';
|
||||
});
|
||||
|
||||
oldZipFiles.forEach(function(file) {
|
||||
file.delete();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
exports.sendStoredFile = functions.https.onRequest(sendStoredFile);
|
||||
exports.deleteOldSnapshotZip = functions.storage.object().onChange(deleteOldSnapshotZip);
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "functions-firebase-code.angularjs.org",
|
||||
"description": "Cloud Functions to serve files from gcs to code.angularjs.org",
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^1.1.1",
|
||||
"firebase-admin": "^4.2.1",
|
||||
"firebase-functions": "^0.5.9"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
google-site-verification: googleb96cceae5888d79f.html
|
||||
@@ -0,0 +1,5 @@
|
||||
User-agent: *
|
||||
|
||||
Disallow: /*docs/
|
||||
Disallow: /*i18n/
|
||||
Disallow: /*.zip$
|
||||
@@ -0,0 +1,12 @@
|
||||
Firebase for code.angularjs.org
|
||||
===============================
|
||||
|
||||
This folder contains the Google Firebase scripts for the code.angularjs.org setup.
|
||||
|
||||
firebase.json contains the rewrite rules that route every subdirectory request to the cloud function
|
||||
in functions/index.js that serves the docs from the Firebase Google Cloud Storage bucket.
|
||||
|
||||
The deployment to the Google Cloud Storage bucket happens automatically via Travis. See the travis.yml
|
||||
file in the repository root.
|
||||
|
||||
See /readme.firebase.docs.md for the firebase deployment to docs.angularjs.org
|
||||
@@ -0,0 +1,7 @@
|
||||
service firebase.storage {
|
||||
match /b/{bucket}/o {
|
||||
match /{allPaths=**} {
|
||||
allow read, write: if request.auth!=null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,19 +59,12 @@ function _update_code() {
|
||||
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
for backend in "$@" ; do
|
||||
echo "-- Refreshing code.angularjs.org: backend=$backend"
|
||||
curl http://$backend:8003/gitFetchSite.php
|
||||
done
|
||||
}
|
||||
|
||||
function publish {
|
||||
# The TXT record for backends.angularjs.org is a CSV of the IP addresses for
|
||||
# the currently serving Compute Engine backends.
|
||||
# code.angularjs.org is served out of port 8003 on these backends.
|
||||
backends=("$(dig backends.angularjs.org +short TXT | python -c 'print raw_input()[1:-1].replace(",", "\n")')")
|
||||
_update_code ${backends[@]}
|
||||
# publish updates the code.angularjs.org Github repository
|
||||
# the deployment to Firebase happens via Travis
|
||||
_update_code
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,15 +4,28 @@ set -e
|
||||
|
||||
yarn global add grunt-cli@1.2.0
|
||||
|
||||
mkdir -p $LOGS_DIR
|
||||
mkdir -p "$LOGS_DIR"
|
||||
|
||||
if [ $JOB != "ci-checks" ]; then
|
||||
if [ "$JOB" != "ci-checks" ]; then
|
||||
echo "start_browser_provider"
|
||||
./scripts/travis/start_browser_provider.sh
|
||||
fi
|
||||
|
||||
if [ $JOB != "ci-checks" ]; then
|
||||
# ci-checks and unit tests do not run against the packaged code
|
||||
if [ "$JOB" != "ci-checks" ] && [ "$JOB" != "unit" ]; then
|
||||
grunt package
|
||||
fi
|
||||
|
||||
# unit runs the docs tests too which need a built version of the code
|
||||
if [ "$JOB" = "unit" ]; then
|
||||
grunt bower
|
||||
grunt validate-angular-files
|
||||
grunt build
|
||||
fi
|
||||
|
||||
# check this after the package, because at this point the browser_provider
|
||||
# has probably arrived
|
||||
if [ "$JOB" != "ci-checks" ]; then
|
||||
echo "wait_for_browser_provider"
|
||||
./scripts/travis/wait_for_browser_provider.sh
|
||||
fi
|
||||
fi
|
||||
+50
-26
@@ -5,32 +5,56 @@ set -e
|
||||
export BROWSER_STACK_ACCESS_KEY=`echo $BROWSER_STACK_ACCESS_KEY | rev`
|
||||
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
|
||||
if [ "$JOB" == "ci-checks" ]; then
|
||||
grunt ci-checks
|
||||
elif [ "$JOB" == "unit" ]; then
|
||||
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
|
||||
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_iOS"
|
||||
else
|
||||
BROWSERS="SL_Chrome,SL_Firefox,SL_Safari_8,SL_Safari_9,SL_IE_9,SL_IE_10,SL_IE_11,SL_iOS"
|
||||
fi
|
||||
case "$JOB" in
|
||||
"ci-checks")
|
||||
grunt ci-checks
|
||||
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers="$BROWSERS" --reporters=dots
|
||||
grunt tests:docs --browsers="$BROWSERS" --reporters=dots
|
||||
elif [ "$JOB" == "docs-e2e" ]; then
|
||||
grunt test:travis-protractor --specs="docs/app/e2e/**/*.scenario.js"
|
||||
elif [ "$JOB" == "e2e" ]; then
|
||||
if [[ $TEST_TARGET == jquery* ]]; then
|
||||
export USE_JQUERY=1
|
||||
fi
|
||||
if [[ $TRAVIS_PULL_REQUEST != 'false' ]]; then
|
||||
# validate commit messages of all commits in the PR
|
||||
# convert commit range to 2 dots, as commitplease uses `git log`.
|
||||
# See https://github.com/travis-ci/travis-ci/issues/4596 for more info
|
||||
echo "Validate commit messages in PR:"
|
||||
yarn run commitplease -- "${TRAVIS_COMMIT_RANGE/.../..}"
|
||||
fi
|
||||
;;
|
||||
"unit")
|
||||
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
|
||||
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_EDGE,BS_iOS"
|
||||
else
|
||||
BROWSERS="SL_Chrome,SL_Firefox,SL_Safari_8,SL_Safari_9,SL_IE_9,SL_IE_10,SL_IE_11,SL_EDGE,SL_iOS"
|
||||
fi
|
||||
|
||||
export TARGET_SPECS="build/docs/ptore2e/**/default_test.js"
|
||||
if [[ "$TEST_TARGET" == jquery* ]]; then
|
||||
TARGET_SPECS="build/docs/ptore2e/**/jquery_test.js"
|
||||
fi
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers="$BROWSERS" --reporters=spec
|
||||
grunt tests:docs --browsers="$BROWSERS" --reporters=spec
|
||||
;;
|
||||
"docs-e2e")
|
||||
grunt test:travis-protractor --specs="docs/app/e2e/**/*.scenario.js"
|
||||
;;
|
||||
"e2e")
|
||||
if [[ $TEST_TARGET == jquery* ]]; then
|
||||
export USE_JQUERY=1
|
||||
fi
|
||||
|
||||
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
|
||||
grunt test:travis-protractor --specs="$TARGET_SPECS"
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=ci-checks, JOB=unit or JOB=e2e-*."
|
||||
fi
|
||||
export TARGET_SPECS="build/docs/ptore2e/**/default_test.js"
|
||||
|
||||
if [[ "$TEST_TARGET" == jquery* ]]; then
|
||||
TARGET_SPECS="build/docs/ptore2e/**/jquery_test.js"
|
||||
fi
|
||||
|
||||
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
|
||||
grunt test:travis-protractor --specs="$TARGET_SPECS"
|
||||
;;
|
||||
"deploy")
|
||||
# we never deploy on Pull requests, so it's safe to skip the build here
|
||||
if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then
|
||||
grunt package
|
||||
grunt compress:firebaseCodeDeploy
|
||||
else
|
||||
echo "Skipping build because Travis has been triggered by Pull Request"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unknown job type. Please set JOB=ci-checks, JOB=unit, JOB=deploy or JOB=e2e-*."
|
||||
;;
|
||||
esac
|
||||
@@ -50,6 +50,7 @@
|
||||
"isNumber": false,
|
||||
"isNumberNaN": false,
|
||||
"isDate": false,
|
||||
"isError": false,
|
||||
"isArray": false,
|
||||
"isFunction": false,
|
||||
"isRegExp": false,
|
||||
@@ -69,6 +70,7 @@
|
||||
"arrayRemove": false,
|
||||
"copy": false,
|
||||
"shallowCopy": false,
|
||||
"simpleCompare": false,
|
||||
"equals": false,
|
||||
"csp": false,
|
||||
"concat": false,
|
||||
@@ -165,6 +167,9 @@
|
||||
/* ng/compile.js */
|
||||
"directiveNormalize": false,
|
||||
|
||||
/* ng/q.js */
|
||||
"markQExceptionHandled": false,
|
||||
|
||||
/* ng/directive/directives.js */
|
||||
"ngDirective": false,
|
||||
|
||||
|
||||
+42
-52
@@ -45,6 +45,7 @@
|
||||
isNumber,
|
||||
isNumberNaN,
|
||||
isDate,
|
||||
isError,
|
||||
isArray,
|
||||
isFunction,
|
||||
isRegExp,
|
||||
@@ -62,6 +63,7 @@
|
||||
includes,
|
||||
arrayRemove,
|
||||
copy,
|
||||
simpleCompare,
|
||||
equals,
|
||||
csp,
|
||||
jq,
|
||||
@@ -128,50 +130,6 @@ 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
|
||||
@@ -479,6 +437,20 @@ function extend(dst) {
|
||||
* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
|
||||
* objects, performing a deep copy.
|
||||
*
|
||||
* @deprecated
|
||||
* sinceVersion="1.6.5"
|
||||
* This function is deprecated, but will not be removed in the 1.x lifecycle.
|
||||
* There are edge cases (see {@link angular.merge#known-issues known issues}) that are not
|
||||
* supported by this function. We suggest
|
||||
* using [lodash's merge()](https://lodash.com/docs/4.17.4#merge) instead.
|
||||
*
|
||||
* @knownIssue
|
||||
* This is a list of (known) object types that are not handled correctly by this function:
|
||||
* - [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob)
|
||||
* - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream)
|
||||
* - [`CanvasGradient`](https://developer.mozilla.org/docs/Web/API/CanvasGradient)
|
||||
* - AngularJS {@link $rootScope.Scope scopes};
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
* @param {...Object} src Source object(s).
|
||||
* @returns {Object} Reference to `dst`.
|
||||
@@ -688,6 +660,24 @@ function isDate(value) {
|
||||
*/
|
||||
var isArray = Array.isArray;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Determines if a reference is an `Error`.
|
||||
* Loosely based on https://www.npmjs.com/package/iserror
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Error`.
|
||||
*/
|
||||
function isError(value) {
|
||||
var tag = toString.call(value);
|
||||
switch (tag) {
|
||||
case '[object Error]': return true;
|
||||
case '[object Exception]': return true;
|
||||
case '[object DOMException]': return true;
|
||||
default: return value instanceof Error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.isFunction
|
||||
@@ -1040,6 +1030,10 @@ function copy(source, destination, maxDepth) {
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line no-self-compare
|
||||
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.equals
|
||||
@@ -1120,7 +1114,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();
|
||||
@@ -1364,7 +1358,7 @@ function fromJson(json) {
|
||||
|
||||
var ALL_COLONS = /:/g;
|
||||
function timezoneToOffset(timezone, fallback) {
|
||||
// Support: IE 9-11 only, Edge 13-14+
|
||||
// Support: IE 9-11 only, Edge 13-15+
|
||||
// IE/Edge do not "understand" colon (`:`) in timezone
|
||||
timezone = timezone.replace(ALL_COLONS, '');
|
||||
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
|
||||
@@ -1391,12 +1385,7 @@ function convertTimezoneToLocal(date, timezone, reverse) {
|
||||
* @returns {string} Returns the string representation of the element.
|
||||
*/
|
||||
function startingTag(element) {
|
||||
element = jqLite(element).clone();
|
||||
try {
|
||||
// turns out IE does not let you set .html() on elements which
|
||||
// are not allowed to have children. So we just ignore it.
|
||||
element.empty();
|
||||
} catch (e) { /* empty */ }
|
||||
element = jqLite(element).clone().empty();
|
||||
var elemHtml = jqLite('<div>').append(element).html();
|
||||
try {
|
||||
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
|
||||
@@ -1534,6 +1523,7 @@ function allowAutoBootstrap(document) {
|
||||
var script = document.currentScript;
|
||||
|
||||
if (!script) {
|
||||
// Support: IE 9-11 only
|
||||
// IE does not have `document.currentScript`
|
||||
return true;
|
||||
}
|
||||
|
||||
+7
-12
@@ -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;
|
||||
});
|
||||
|
||||
@@ -5,7 +5,31 @@
|
||||
*/
|
||||
'use strict';
|
||||
(function() {
|
||||
// NOTE:
|
||||
// These functions are copied here from `src/Angular.js`, because they are needed inside the
|
||||
// `angular-loader.js` closure and need to be available before the main `angular.js` script has
|
||||
// been loaded.
|
||||
function isFunction(value) {return typeof value === 'function';}
|
||||
function isDefined(value) {return typeof value !== 'undefined';}
|
||||
function isNumber(value) {return typeof value === 'number';}
|
||||
function isObject(value) {return value !== null && typeof value === 'object';}
|
||||
function isScope(obj) {return obj && obj.$evalAsync && obj.$watch;}
|
||||
function isUndefined(value) {return typeof value === 'undefined';}
|
||||
function isWindow(obj) {return obj && obj.window === obj;}
|
||||
function sliceArgs(args, startIndex) {return Array.prototype.slice.call(args, startIndex || 0);}
|
||||
function toJsonReplacer(key, value) {
|
||||
var val = value;
|
||||
|
||||
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
|
||||
val = undefined;
|
||||
} else if (isWindow(value)) {
|
||||
val = '$WINDOW';
|
||||
} else if (value && window.document === value) {
|
||||
val = '$DOCUMENT';
|
||||
} else if (isScope(value)) {
|
||||
val = '$SCOPE';
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
/* exported
|
||||
minErrConfig,
|
||||
errorHandlingConfig,
|
||||
isValidObjectMaxDepth
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
|
||||
@@ -180,6 +180,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
|
||||
var $AnimateProvider = ['$provide', /** @this */ function($provide) {
|
||||
var provider = this;
|
||||
var classNameFilter = null;
|
||||
var customFilter = null;
|
||||
|
||||
this.$$registeredAnimations = Object.create(null);
|
||||
|
||||
@@ -232,6 +233,51 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
|
||||
$provide.factory(key, factory);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $animateProvider#customFilter
|
||||
*
|
||||
* @description
|
||||
* Sets and/or returns the custom filter function that is used to "filter" animations, i.e.
|
||||
* determine if an animation is allowed or not. When no filter is specified (the default), no
|
||||
* animation will be blocked. Setting the `customFilter` value will only allow animations for
|
||||
* which the filter function's return value is truthy.
|
||||
*
|
||||
* This allows to easily create arbitrarily complex rules for filtering animations, such as
|
||||
* allowing specific events only, or enabling animations on specific subtrees of the DOM, etc.
|
||||
* Filtering animations can also boost performance for low-powered devices, as well as
|
||||
* applications containing a lot of structural operations.
|
||||
*
|
||||
* <div class="alert alert-success">
|
||||
* **Best Practice:**
|
||||
* Keep the filtering function as lean as possible, because it will be called for each DOM
|
||||
* action (e.g. insertion, removal, class change) performed by "animation-aware" directives.
|
||||
* See {@link guide/animations#which-directives-support-animations- here} for a list of built-in
|
||||
* directives that support animations.
|
||||
* Performing computationally expensive or time-consuming operations on each call of the
|
||||
* filtering function can make your animations sluggish.
|
||||
* </div>
|
||||
*
|
||||
* **Note:** If present, `customFilter` will be checked before
|
||||
* {@link $animateProvider#classNameFilter classNameFilter}.
|
||||
*
|
||||
* @param {Function=} filterFn - The filter function which will be used to filter all animations.
|
||||
* If a falsy value is returned, no animation will be performed. The function will be called
|
||||
* with the following arguments:
|
||||
* - **node** `{DOMElement}` - The DOM element to be animated.
|
||||
* - **event** `{String}` - The name of the animation event (e.g. `enter`, `leave`, `addClass`
|
||||
* etc).
|
||||
* - **options** `{Object}` - A collection of options/styles used for the animation.
|
||||
* @return {Function} The current filter function or `null` if there is none set.
|
||||
*/
|
||||
this.customFilter = function(filterFn) {
|
||||
if (arguments.length === 1) {
|
||||
customFilter = isFunction(filterFn) ? filterFn : null;
|
||||
}
|
||||
|
||||
return customFilter;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $animateProvider#classNameFilter
|
||||
@@ -243,6 +289,11 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
|
||||
* When setting the `classNameFilter` value, animations will only be performed on elements
|
||||
* that successfully match the filter expression. This in turn can boost performance
|
||||
* for low-powered devices as well as applications containing a lot of structural operations.
|
||||
*
|
||||
* **Note:** If present, `classNameFilter` will be checked after
|
||||
* {@link $animateProvider#customFilter customFilter}. If `customFilter` is present and returns
|
||||
* false, `classNameFilter` will not be checked.
|
||||
*
|
||||
* @param {RegExp=} expression The className expression which will be checked against all animations
|
||||
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
|
||||
*/
|
||||
|
||||
+61
-11
@@ -375,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.
|
||||
@@ -1144,7 +1144,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @ngdoc method
|
||||
* @name $compileProvider#component
|
||||
* @module ng
|
||||
* @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
|
||||
* @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
|
||||
* or an object map of components where the keys are the names and the values are the component definition objects.
|
||||
* @param {Object} options Component definition object (a simplified
|
||||
* {@link ng.$compile#directive-definition-object directive definition object}),
|
||||
* with the following properties (all optional):
|
||||
@@ -1227,6 +1228,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
this.component = function registerComponent(name, options) {
|
||||
if (!isString(name)) {
|
||||
forEach(name, reverseParams(bind(this, registerComponent)));
|
||||
return this;
|
||||
}
|
||||
|
||||
var controller = options.controller || function() {};
|
||||
|
||||
function factory($injector) {
|
||||
@@ -1390,7 +1396,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) {
|
||||
@@ -1401,6 +1414,31 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return preAssignBindingsEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $compileProvider#strictComponentBindingsEnabled
|
||||
*
|
||||
* @param {boolean=} enabled update the strictComponentBindingsEnabled state if provided, otherwise just return the
|
||||
* current strictComponentBindingsEnabled state
|
||||
* @returns {*} current value if used as getter or itself (chaining) if used as setter
|
||||
*
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Call this method to enable/disable strict component bindings check. If enabled, the compiler will enforce that
|
||||
* for all bindings of a component that are not set as optional with `?`, an attribute needs to be provided
|
||||
* on the component's HTML tag.
|
||||
*
|
||||
* The default value is false.
|
||||
*/
|
||||
var strictComponentBindingsEnabled = false;
|
||||
this.strictComponentBindingsEnabled = function(enabled) {
|
||||
if (isDefined(enabled)) {
|
||||
strictComponentBindingsEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
return strictComponentBindingsEnabled;
|
||||
};
|
||||
|
||||
var TTL = 10;
|
||||
/**
|
||||
@@ -3155,7 +3193,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
linkQueue = null;
|
||||
}).catch(function(error) {
|
||||
if (error instanceof Error) {
|
||||
if (isError(error)) {
|
||||
$exceptionHandler(error);
|
||||
}
|
||||
});
|
||||
@@ -3428,12 +3466,20 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
function strictBindingsCheck(attrName, directiveName) {
|
||||
if (strictComponentBindingsEnabled) {
|
||||
throw $compileMinErr('missingattr',
|
||||
'Attribute \'{0}\' of \'{1}\' is non-optional and must be set!',
|
||||
attrName, directiveName);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up $watches for isolate scope and controller bindings.
|
||||
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
|
||||
var removeWatchCollection = [];
|
||||
var initialChanges = {};
|
||||
var changes;
|
||||
|
||||
forEach(bindings, function initializeBinding(definition, scopeName) {
|
||||
var attrName = definition.attrName,
|
||||
optional = definition.optional,
|
||||
@@ -3445,7 +3491,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
case '@':
|
||||
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
|
||||
strictBindingsCheck(attrName, directive.name);
|
||||
destination[scopeName] = attrs[attrName] = undefined;
|
||||
|
||||
}
|
||||
removeWatch = attrs.$observe(attrName, function(value) {
|
||||
if (isString(value) || isBoolean(value)) {
|
||||
@@ -3472,6 +3520,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
case '=':
|
||||
if (!hasOwnProperty.call(attrs, attrName)) {
|
||||
if (optional) break;
|
||||
strictBindingsCheck(attrName, directive.name);
|
||||
attrs[attrName] = undefined;
|
||||
}
|
||||
if (optional && !attrs[attrName]) break;
|
||||
@@ -3480,8 +3529,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
|
||||
@@ -3517,6 +3565,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
case '<':
|
||||
if (!hasOwnProperty.call(attrs, attrName)) {
|
||||
if (optional) break;
|
||||
strictBindingsCheck(attrName, directive.name);
|
||||
attrs[attrName] = undefined;
|
||||
}
|
||||
if (optional && !attrs[attrName]) break;
|
||||
@@ -3542,6 +3591,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
break;
|
||||
|
||||
case '&':
|
||||
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
|
||||
strictBindingsCheck(attrName, directive.name);
|
||||
}
|
||||
// Don't assign Object.prototype method to scope
|
||||
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
|
||||
|
||||
@@ -3556,9 +3608,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);
|
||||
|
||||
@@ -317,15 +317,20 @@
|
||||
*
|
||||
* ## A note about browser compatibility
|
||||
*
|
||||
* Edge, Firefox, and Internet Explorer do not support the `details` element, it is
|
||||
* Internet Explorer and Edge do not support the `details` element, it is
|
||||
* recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
|
||||
*
|
||||
* @example
|
||||
<example name="ng-open">
|
||||
<file name="index.html">
|
||||
<label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
|
||||
<label>Toggle details: <input type="checkbox" ng-model="open"></label><br/>
|
||||
<details id="details" ng-open="open">
|
||||
<summary>Show/Hide me</summary>
|
||||
<summary>List</summary>
|
||||
<ul>
|
||||
<li>Apple</li>
|
||||
<li>Orange</li>
|
||||
<li>Durian</li>
|
||||
</ul>
|
||||
</details>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
@@ -26,17 +26,23 @@ function nullFormRenameControl(control, name) {
|
||||
* @property {boolean} $dirty True if user has already interacted with the form.
|
||||
* @property {boolean} $valid True if all of the containing forms and controls are valid.
|
||||
* @property {boolean} $invalid True if at least one containing control or form is invalid.
|
||||
* @property {boolean} $pending True if at least one containing control or form is pending.
|
||||
* @property {boolean} $submitted True if user has submitted the form even if its invalid.
|
||||
*
|
||||
* @property {Object} $error Is an object hash, containing references to controls or
|
||||
* forms with failing validators, where:
|
||||
* @property {Object} $pending An object hash, containing references to controls or forms with
|
||||
* pending validators, where:
|
||||
*
|
||||
* - keys are validations tokens (error names).
|
||||
* - values are arrays of controls or forms that have a pending validator for the given error name.
|
||||
*
|
||||
* See {@link form.FormController#$error $error} for a list of built-in validation tokens.
|
||||
*
|
||||
* @property {Object} $error An object hash, containing references to controls or forms with failing
|
||||
* validators, where:
|
||||
*
|
||||
* - keys are validation tokens (error names),
|
||||
* - values are arrays of controls or forms that have a failing validator for given error name.
|
||||
* - values are arrays of controls or forms that have a failing validator for the given error name.
|
||||
*
|
||||
* Built-in validation tokens:
|
||||
*
|
||||
* - `email`
|
||||
* - `max`
|
||||
* - `maxlength`
|
||||
@@ -282,9 +288,24 @@ FormController.prototype = {
|
||||
* @name form.FormController#$setValidity
|
||||
*
|
||||
* @description
|
||||
* Sets the validity of a form control.
|
||||
* Change the validity state of the form, and notify the parent form (if any).
|
||||
*
|
||||
* This method will also propagate to parent forms.
|
||||
* Application developers will rarely need to call this method directly. It is used internally, by
|
||||
* {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
|
||||
* control's validity state to the parent `FormController`.
|
||||
*
|
||||
* @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
|
||||
* assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
|
||||
* unfulfilled `$asyncValidators`), so that it is available for data-binding. The
|
||||
* `validationErrorKey` should be in camelCase and will get converted into dash-case for
|
||||
* class name. Example: `myError` will result in `ng-valid-my-error` and
|
||||
* `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
|
||||
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
|
||||
* (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
|
||||
* Skipped is used by AngularJS when validators do not run because of parse errors and when
|
||||
* `$asyncValidators` do not run because any of the `$validators` failed.
|
||||
* @param {NgModelController | FormController} controller - The controller whose validity state is
|
||||
* triggering the change.
|
||||
*/
|
||||
addSetValidityMethod({
|
||||
clazz: FormController,
|
||||
|
||||
@@ -281,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;
|
||||
@@ -890,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?
|
||||
@@ -940,7 +942,7 @@ function setupModelWatcher(ctrl) {
|
||||
* (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
|
||||
* The `validationErrorKey` should be in camelCase and will get converted into dash-case
|
||||
* for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
|
||||
* class and can be bound to as `{{someForm.someControl.$error.myError}}` .
|
||||
* classes and can be bound to as `{{ someForm.someControl.$error.myError }}`.
|
||||
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
|
||||
* or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
|
||||
* Skipped is used by Angular when validators do not run because of parse errors and
|
||||
|
||||
@@ -407,7 +407,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
}
|
||||
|
||||
|
||||
// we can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// Support: IE 9 only
|
||||
// We can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
var optionTemplate = window.document.createElement('option'),
|
||||
optGroupTemplate = window.document.createElement('optgroup');
|
||||
@@ -428,6 +429,9 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
}
|
||||
}
|
||||
|
||||
// The empty option will be compiled and rendered before we first generate the options
|
||||
selectElement.empty();
|
||||
|
||||
var providedEmptyOption = !!selectCtrl.emptyOption;
|
||||
|
||||
var unknownOption = jqLite(optionTemplate.cloneNode(false));
|
||||
@@ -449,12 +453,15 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
if (!multiple) {
|
||||
|
||||
selectCtrl.writeValue = function writeNgOptionsValue(value) {
|
||||
var selectedOption = options.selectValueMap[selectElement.val()];
|
||||
// The options might not be defined yet when ngModel tries to render
|
||||
if (!options) return;
|
||||
|
||||
var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
|
||||
var option = options.getOptionFromViewValue(value);
|
||||
|
||||
// Make sure to remove the selected attribute from the previously selected option
|
||||
// Otherwise, screen readers might get confused
|
||||
if (selectedOption) selectedOption.element.removeAttribute('selected');
|
||||
if (selectedOption) selectedOption.removeAttribute('selected');
|
||||
|
||||
if (option) {
|
||||
// Don't update the option when it is already selected.
|
||||
@@ -464,7 +471,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
if (selectElement[0].value !== option.selectValue) {
|
||||
selectCtrl.removeUnknownOption();
|
||||
selectCtrl.unselectEmptyOption();
|
||||
|
||||
selectElement[0].value = option.selectValue;
|
||||
option.element.selected = true;
|
||||
@@ -472,14 +478,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
option.element.setAttribute('selected', 'selected');
|
||||
} else {
|
||||
|
||||
if (providedEmptyOption) {
|
||||
selectCtrl.selectEmptyOption();
|
||||
} else if (selectCtrl.unknownOption.parent().length) {
|
||||
selectCtrl.updateUnknownOption(value);
|
||||
} else {
|
||||
selectCtrl.renderUnknownOption(value);
|
||||
}
|
||||
selectCtrl.selectUnknownOrEmptyOption(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -508,9 +507,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
} else {
|
||||
|
||||
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
|
||||
// The options might not be defined yet when ngModel tries to render
|
||||
if (!options) return;
|
||||
|
||||
// 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.
|
||||
|
||||
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
|
||||
|
||||
options.items.forEach(function(option) {
|
||||
@@ -552,13 +553,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
if (providedEmptyOption) {
|
||||
|
||||
// we need to remove it before calling selectElement.empty() because otherwise IE will
|
||||
// remove the label from the element. wtf?
|
||||
selectCtrl.emptyOption.remove();
|
||||
|
||||
// compile the element since there might be bindings in it
|
||||
$compile(selectCtrl.emptyOption)(scope);
|
||||
|
||||
selectElement.prepend(selectCtrl.emptyOption);
|
||||
|
||||
if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
|
||||
// This means the empty option has currently no actual DOM node, probably because
|
||||
// it has been modified by a transclusion directive.
|
||||
@@ -576,8 +575,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
ngModelCtrl.$render();
|
||||
|
||||
optionEl.on('$destroy', function() {
|
||||
var needsRerender = selectCtrl.$isEmptyOptionSelected();
|
||||
|
||||
selectCtrl.hasEmptyOption = false;
|
||||
selectCtrl.emptyOption = undefined;
|
||||
|
||||
if (needsRerender) ngModelCtrl.$render();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -590,12 +593,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
}
|
||||
|
||||
selectElement.empty();
|
||||
|
||||
// We need to do this here to ensure that the options object is defined
|
||||
// when we first hit it in writeNgOptionsValue
|
||||
updateOptions();
|
||||
|
||||
// We will re-render the option elements if the option values or labels change
|
||||
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
|
||||
|
||||
@@ -619,7 +616,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
function updateOptionElement(option, element) {
|
||||
option.element = element;
|
||||
element.disabled = option.disabled;
|
||||
// NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
|
||||
// Support: IE 11 only, Edge 12-13 only
|
||||
// NOTE: The label must be set before the value, otherwise IE 11 & Edge create unresponsive
|
||||
// selects in certain circumstances when multiple selects are next to each other and display
|
||||
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
|
||||
// See https://github.com/angular/angular.js/issues/11314 for more info.
|
||||
@@ -655,11 +653,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
var groupElementMap = {};
|
||||
|
||||
// Ensure that the empty option is always there if it was explicitly provided
|
||||
if (providedEmptyOption) {
|
||||
selectElement.prepend(selectCtrl.emptyOption);
|
||||
}
|
||||
|
||||
options.items.forEach(function addOption(option) {
|
||||
var groupElement;
|
||||
|
||||
@@ -704,7 +697,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
ngModelCtrl.$render();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,9 @@
|
||||
* more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
|
||||
* mapped to the same DOM element, which is not possible.)
|
||||
*
|
||||
* Note that the tracking expression must come last, after any filters, and the alias expression.
|
||||
* <div class="alert alert-warning">
|
||||
* <strong>Note:</strong> the `track by` expression must come last - after any filters, and the alias expression.
|
||||
* </div>
|
||||
*
|
||||
* For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
|
||||
* will be associated by item identity in the array.
|
||||
@@ -429,7 +431,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
|
||||
// Store a list of elements from previous run. This is a hash where key is the item from the
|
||||
// iterator, and the value is objects with following properties.
|
||||
// - scope: bound scope
|
||||
// - element: previous element.
|
||||
// - clone: previous element.
|
||||
// - index: position
|
||||
//
|
||||
// We are using no-proto object so that we don't need to guard against inherited props via
|
||||
|
||||
+196
-20
@@ -5,7 +5,7 @@
|
||||
var noopNgModelController = { $setViewValue: noop, $render: noop };
|
||||
|
||||
function setOptionSelectedStatus(optionEl, value) {
|
||||
optionEl.prop('selected', value); // needed for IE
|
||||
optionEl.prop('selected', value);
|
||||
/**
|
||||
* When unselecting an option, setting the property to null / false should be enough
|
||||
* However, screenreaders might react to the selected attribute instead, see
|
||||
@@ -19,10 +19,120 @@ function setOptionSelectedStatus(optionEl, value) {
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name select.SelectController
|
||||
*
|
||||
* @description
|
||||
* The controller for the `<select>` directive. This provides support for reading
|
||||
* and writing the selected value(s) of the control and also coordinates dynamically
|
||||
* added `<option>` elements, perhaps by an `ngRepeat` directive.
|
||||
* The controller for the {@link ng.select select} directive. The controller exposes
|
||||
* a few utility methods that can be used to augment the behavior of a regular or an
|
||||
* {@link ng.ngOptions ngOptions} select element.
|
||||
*
|
||||
* @example
|
||||
* ### Set a custom error when the unknown option is selected
|
||||
*
|
||||
* This example sets a custom error "unknownValue" on the ngModelController
|
||||
* when the select element's unknown option is selected, i.e. when the model is set to a value
|
||||
* that is not matched by any option.
|
||||
*
|
||||
* <example name="select-unknown-value-error" module="staticSelect">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="ExampleController">
|
||||
* <form name="myForm">
|
||||
* <label for="testSelect"> Single select: </label><br>
|
||||
* <select name="testSelect" ng-model="selected" unknown-value-error>
|
||||
* <option value="option-1">Option 1</option>
|
||||
* <option value="option-2">Option 2</option>
|
||||
* </select><br>
|
||||
* <span ng-if="myForm.testSelect.$error.unknownValue">Error: The current model doesn't match any option</span>
|
||||
*
|
||||
* <button ng-click="forceUnknownOption()">Force unknown option</button><br>
|
||||
* </form>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="app.js">
|
||||
* angular.module('staticSelect', [])
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.selected = null;
|
||||
*
|
||||
* $scope.forceUnknownOption = function() {
|
||||
* $scope.selected = 'nonsense';
|
||||
* };
|
||||
* }])
|
||||
* .directive('unknownValueError', function() {
|
||||
* return {
|
||||
* require: ['ngModel', 'select'],
|
||||
* link: function(scope, element, attrs, ctrls) {
|
||||
* var ngModelCtrl = ctrls[0];
|
||||
* var selectCtrl = ctrls[1];
|
||||
*
|
||||
* ngModelCtrl.$validators.unknownValue = function(modelValue, viewValue) {
|
||||
* if (selectCtrl.$isUnknownOptionSelected()) {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* return true;
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* };
|
||||
* });
|
||||
* </file>
|
||||
*</example>
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ### Set the "required" error when the unknown option is selected.
|
||||
*
|
||||
* By default, the "required" error on the ngModelController is only set on a required select
|
||||
* when the empty option is selected. This example adds a custom directive that also sets the
|
||||
* error when the unknown option is selected.
|
||||
*
|
||||
* <example name="select-unknown-value-required" module="staticSelect">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="ExampleController">
|
||||
* <form name="myForm">
|
||||
* <label for="testSelect"> Select: </label><br>
|
||||
* <select name="testSelect" ng-model="selected" unknown-value-required>
|
||||
* <option value="option-1">Option 1</option>
|
||||
* <option value="option-2">Option 2</option>
|
||||
* </select><br>
|
||||
* <span ng-if="myForm.testSelect.$error.required">Error: Please select a value</span><br>
|
||||
*
|
||||
* <button ng-click="forceUnknownOption()">Force unknown option</button><br>
|
||||
* </form>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="app.js">
|
||||
* angular.module('staticSelect', [])
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.selected = null;
|
||||
*
|
||||
* $scope.forceUnknownOption = function() {
|
||||
* $scope.selected = 'nonsense';
|
||||
* };
|
||||
* }])
|
||||
* .directive('unknownValueRequired', function() {
|
||||
* return {
|
||||
* priority: 1, // This directive must run after the required directive has added its validator
|
||||
* require: ['ngModel', 'select'],
|
||||
* link: function(scope, element, attrs, ctrls) {
|
||||
* var ngModelCtrl = ctrls[0];
|
||||
* var selectCtrl = ctrls[1];
|
||||
*
|
||||
* var originalRequiredValidator = ngModelCtrl.$validators.required;
|
||||
*
|
||||
* ngModelCtrl.$validators.required = function() {
|
||||
* if (attrs.required && selectCtrl.$isUnknownOptionSelected()) {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* return originalRequiredValidator.apply(this, arguments);
|
||||
* };
|
||||
* }
|
||||
* };
|
||||
* });
|
||||
* </file>
|
||||
*</example>
|
||||
*
|
||||
*
|
||||
*/
|
||||
var SelectController =
|
||||
['$element', '$scope', /** @this */ function($element, $scope) {
|
||||
@@ -40,15 +150,18 @@ var SelectController =
|
||||
// does not match any of the options. When it is rendered the value of the unknown
|
||||
// option is '? XXX ?' where XXX is the hashKey of the value that is not known.
|
||||
//
|
||||
// Support: IE 9 only
|
||||
// We can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
self.unknownOption = jqLite(window.document.createElement('option'));
|
||||
|
||||
// The empty option is an option with the value '' that te application developer can
|
||||
// provide inside the select. When the model changes to a value that doesn't match an option,
|
||||
// it is selected - so if an empty option is provided, no unknown option is generated.
|
||||
// However, the empty option is not removed when the model matches an option. It is always selectable
|
||||
// and indicates that a "null" selection has been made.
|
||||
// The empty option is an option with the value '' that the application developer can
|
||||
// provide inside the select. It is always selectable and indicates that a "null" selection has
|
||||
// been made by the user.
|
||||
// If the select has an empty option, and the model of the select is set to "undefined" or "null",
|
||||
// the empty option is selected.
|
||||
// If the model is set to a different unmatched value, the unknown option is rendered and
|
||||
// selected, i.e both are present, because a "null" selection and an unknown value are different.
|
||||
self.hasEmptyOption = false;
|
||||
self.emptyOption = undefined;
|
||||
|
||||
@@ -84,7 +197,7 @@ var SelectController =
|
||||
|
||||
self.unselectEmptyOption = function() {
|
||||
if (self.hasEmptyOption) {
|
||||
self.emptyOption.removeAttr('selected');
|
||||
setOptionSelectedStatus(self.emptyOption, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,14 +239,7 @@ var SelectController =
|
||||
var selectedOption = $element[0].options[$element[0].selectedIndex];
|
||||
setOptionSelectedStatus(jqLite(selectedOption), true);
|
||||
} else {
|
||||
if (value == null && self.emptyOption) {
|
||||
self.removeUnknownOption();
|
||||
self.selectEmptyOption();
|
||||
} else if (self.unknownOption.parent().length) {
|
||||
self.updateUnknownOption(value);
|
||||
} else {
|
||||
self.renderUnknownOption(value);
|
||||
}
|
||||
self.selectUnknownOrEmptyOption(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,6 +282,59 @@ var SelectController =
|
||||
return !!optionsMap.get(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name select.SelectController#$hasEmptyOption
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Returns `true` if the select element currently has an empty option
|
||||
* element, i.e. an option that signifies that the select is empty / the selection is null.
|
||||
*
|
||||
*/
|
||||
self.$hasEmptyOption = function() {
|
||||
return self.hasEmptyOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name select.SelectController#$isUnknownOptionSelected
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Returns `true` if the select element's unknown option is selected. The unknown option is added
|
||||
* and automatically selected whenever the select model doesn't match any option.
|
||||
*
|
||||
*/
|
||||
self.$isUnknownOptionSelected = function() {
|
||||
// Presence of the unknown option means it is selected
|
||||
return $element[0].options[0] === self.unknownOption[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name select.SelectController#$isEmptyOptionSelected
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Returns `true` if the select element has an empty option and this empty option is currently
|
||||
* selected. Returns `false` if the select element has no empty option or it is not selected.
|
||||
*
|
||||
*/
|
||||
self.$isEmptyOptionSelected = function() {
|
||||
return self.hasEmptyOption && $element[0].options[$element[0].selectedIndex] === self.emptyOption[0];
|
||||
};
|
||||
|
||||
self.selectUnknownOrEmptyOption = function(value) {
|
||||
if (value == null && self.emptyOption) {
|
||||
self.removeUnknownOption();
|
||||
self.selectEmptyOption();
|
||||
} else if (self.unknownOption.parent().length) {
|
||||
self.updateUnknownOption(value);
|
||||
} else {
|
||||
self.renderUnknownOption(value);
|
||||
}
|
||||
};
|
||||
|
||||
var renderScheduled = false;
|
||||
function scheduleRender() {
|
||||
@@ -324,6 +483,9 @@ var SelectController =
|
||||
* the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
|
||||
* Value and textContent can be interpolated.
|
||||
*
|
||||
* The {@link select.SelectController select controller} exposes utility functions that can be used
|
||||
* to manipulate the select's behavior.
|
||||
*
|
||||
* ## Matching model and option values
|
||||
*
|
||||
* In general, the match between the model and an option is evaluated by strictly comparing the model
|
||||
@@ -376,6 +538,19 @@ var SelectController =
|
||||
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
|
||||
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
|
||||
*
|
||||
*
|
||||
* @knownIssue
|
||||
*
|
||||
* In Firefox, the select model is only updated when the select element is blurred. For example,
|
||||
* when switching between options with the keyboard, the select model is only set to the
|
||||
* currently selected option when the select is blurred, e.g via tab key or clicking the mouse
|
||||
* outside the select.
|
||||
*
|
||||
* This is due to an ambiguity in the select element specification. See the
|
||||
* [issue on the Firefox bug tracker](https://bugzilla.mozilla.org/show_bug.cgi?id=126379)
|
||||
* for more information, and this
|
||||
* [Github comment for a workaround](https://github.com/angular/angular.js/issues/9134#issuecomment-130800488)
|
||||
*
|
||||
* @example
|
||||
* ### Simple `select` elements with static options
|
||||
*
|
||||
@@ -620,10 +795,11 @@ var selectDirective = function() {
|
||||
includes(value, selectCtrl.selectValueMap[option.value]));
|
||||
var currentlySelected = option.selected;
|
||||
|
||||
// IE and Edge, adding options to the selection via shift+click/UP/DOWN,
|
||||
// Support: IE 9-11 only, Edge 12-15+
|
||||
// In 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.
|
||||
// So we only modify the selected property if necessary.
|
||||
// Note: this behavior cannot be replicated via unit tests because it only shows in the
|
||||
// actual user interface.
|
||||
if (shouldBeSelected !== currentlySelected) {
|
||||
|
||||
@@ -45,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 expected value (from the filter expression) and actual
|
||||
* value (from the object in the array).
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
|
||||
@@ -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
|
||||
@@ -696,6 +698,9 @@ function jsonFilter() {
|
||||
* @kind function
|
||||
* @description
|
||||
* Converts string to lowercase.
|
||||
*
|
||||
* See the {@link ng.uppercase uppercase filter documentation} for a functionally identical example.
|
||||
*
|
||||
* @see angular.lowercase
|
||||
*/
|
||||
var lowercaseFilter = valueFn(lowercase);
|
||||
@@ -707,6 +712,22 @@ var lowercaseFilter = valueFn(lowercase);
|
||||
* @kind function
|
||||
* @description
|
||||
* Converts string to uppercase.
|
||||
* @see angular.uppercase
|
||||
* @example
|
||||
<example module="uppercaseFilterExample" name="filter-uppercase">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('uppercaseFilterExample', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.title = 'This is a title';
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
<!-- This title should be formatted normally -->
|
||||
<h1>{{title}}</h1>
|
||||
<!-- This title should be capitalized -->
|
||||
<h1>{{title | uppercase}}</h1>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var uppercaseFilter = valueFn(uppercase);
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
* dummy predicate that returns the item's index as `value`.
|
||||
* (If you are using a custom comparator, make sure it can handle this predicate as well.)
|
||||
*
|
||||
* If a custom comparator still can't distinguish between two items, then they will be sorted based
|
||||
* on their index using the built-in comparator.
|
||||
*
|
||||
* Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
|
||||
* value for an item, `orderBy` will try to convert that object to a primitive value, before passing
|
||||
* it to the comparator. The following rules govern the conversion:
|
||||
@@ -599,7 +602,7 @@ function orderByFilter($parse) {
|
||||
}
|
||||
}
|
||||
|
||||
return compare(v1.tieBreaker, v2.tieBreaker) * descending;
|
||||
return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+55
-23
@@ -41,7 +41,7 @@ function $HttpParamSerializerProvider() {
|
||||
if (!params) return '';
|
||||
var parts = [];
|
||||
forEachSorted(params, function(value, key) {
|
||||
if (value === null || isUndefined(value)) return;
|
||||
if (value === null || isUndefined(value) || isFunction(value)) return;
|
||||
if (isArray(value)) {
|
||||
forEach(value, function(v) {
|
||||
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
|
||||
@@ -137,8 +137,18 @@ function defaultHttpResponseTransform(data, headers) {
|
||||
|
||||
if (tempData) {
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
|
||||
data = fromJson(tempData);
|
||||
var hasJsonContentType = contentType && (contentType.indexOf(APPLICATION_JSON) === 0);
|
||||
|
||||
if (hasJsonContentType || isJsonLike(tempData)) {
|
||||
try {
|
||||
data = fromJson(tempData);
|
||||
} catch (e) {
|
||||
if (!hasJsonContentType) {
|
||||
return data;
|
||||
}
|
||||
throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
|
||||
'Parse error: "{1}"', data, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,12 +271,6 @@ function $HttpProvider() {
|
||||
* {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
|
||||
* by default. See {@link $http#caching $http Caching} for more information.
|
||||
*
|
||||
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
|
||||
* Defaults value is `'XSRF-TOKEN'`.
|
||||
*
|
||||
* - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
|
||||
* XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
|
||||
*
|
||||
* - **`defaults.headers`** - {Object} - Default headers for all $http requests.
|
||||
* Refer to {@link ng.$http#setting-http-headers $http} for documentation on
|
||||
* setting default headers.
|
||||
@@ -275,15 +279,38 @@ function $HttpProvider() {
|
||||
* - **`defaults.headers.put`**
|
||||
* - **`defaults.headers.patch`**
|
||||
*
|
||||
* - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
|
||||
* callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
|
||||
* {@link $jsonpCallbacks} service. Defaults to `'callback'`.
|
||||
*
|
||||
* - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
|
||||
* used to the prepare string representation of request parameters (specified as an object).
|
||||
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
|
||||
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
|
||||
*
|
||||
* - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
|
||||
* callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
|
||||
* {@link $jsonpCallbacks} service. Defaults to `'callback'`.
|
||||
* - **`defaults.transformRequest`** -
|
||||
* `{Array<function(data, headersGetter)>|function(data, headersGetter)}` -
|
||||
* An array of functions (or a single function) which are applied to the request data.
|
||||
* By default, this is an array with one request transformation function:
|
||||
*
|
||||
* - If the `data` property of the request configuration object contains an object, serialize it
|
||||
* into JSON format.
|
||||
*
|
||||
* - **`defaults.transformResponse`** -
|
||||
* `{Array<function(data, headersGetter, status)>|function(data, headersGetter, status)}` -
|
||||
* An array of functions (or a single function) which are applied to the response data. By default,
|
||||
* this is an array which applies one response transformation function that does two things:
|
||||
*
|
||||
* - If XSRF prefix is detected, strip it
|
||||
* (see {@link ng.$http#security-considerations Security Considerations in the $http docs}).
|
||||
* - If the `Content-Type` is `application/json` or the response looks like JSON,
|
||||
* deserialize it using a JSON parser.
|
||||
*
|
||||
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
|
||||
* Defaults value is `'XSRF-TOKEN'`.
|
||||
*
|
||||
* - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
|
||||
* XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
|
||||
*
|
||||
**/
|
||||
var defaults = this.defaults = {
|
||||
@@ -431,6 +458,7 @@ function $HttpProvider() {
|
||||
* - **headers** – `{function([headerName])}` – Header getter function.
|
||||
* - **config** – `{Object}` – The configuration object that was used to generate the request.
|
||||
* - **statusText** – `{string}` – HTTP status text of the response.
|
||||
* - **xhrStatus** – `{string}` – Status of the XMLHttpRequest (`complete`, `error`, `timeout` or `abort`).
|
||||
*
|
||||
* A response status code between 200 and 299 is considered a success status and will result in
|
||||
* the success callback being called. Any response status code outside of that range is
|
||||
@@ -547,15 +575,18 @@ function $HttpProvider() {
|
||||
*
|
||||
* Angular provides the following default transformations:
|
||||
*
|
||||
* Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
|
||||
* Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`) is
|
||||
* an array with one function that does the following:
|
||||
*
|
||||
* - If the `data` property of the request configuration object contains an object, serialize it
|
||||
* into JSON format.
|
||||
*
|
||||
* Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
|
||||
* Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`) is
|
||||
* an array with one function that does the following:
|
||||
*
|
||||
* - If XSRF prefix is detected, strip it (see Security Considerations section below).
|
||||
* - If JSON response is detected, deserialize it using a JSON parser.
|
||||
* - If the `Content-Type` is `application/json` or the response looks like JSON,
|
||||
* deserialize it using a JSON parser.
|
||||
*
|
||||
*
|
||||
* ### Overriding the Default Transformations Per Request
|
||||
@@ -1269,9 +1300,9 @@ function $HttpProvider() {
|
||||
} else {
|
||||
// serving from cache
|
||||
if (isArray(cachedResp)) {
|
||||
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
|
||||
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]);
|
||||
} else {
|
||||
resolvePromise(cachedResp, 200, {}, 'OK');
|
||||
resolvePromise(cachedResp, 200, {}, 'OK', 'complete');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1328,10 +1359,10 @@ function $HttpProvider() {
|
||||
* - resolves the raw $http promise
|
||||
* - calls $apply
|
||||
*/
|
||||
function done(status, response, headersString, statusText) {
|
||||
function done(status, response, headersString, statusText, xhrStatus) {
|
||||
if (cache) {
|
||||
if (isSuccess(status)) {
|
||||
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
|
||||
cache.put(url, [status, response, parseHeaders(headersString), statusText, xhrStatus]);
|
||||
} else {
|
||||
// remove promise from the cache
|
||||
cache.remove(url);
|
||||
@@ -1339,7 +1370,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
function resolveHttpPromise() {
|
||||
resolvePromise(response, status, headersString, statusText);
|
||||
resolvePromise(response, status, headersString, statusText, xhrStatus);
|
||||
}
|
||||
|
||||
if (useApplyAsync) {
|
||||
@@ -1354,7 +1385,7 @@ function $HttpProvider() {
|
||||
/**
|
||||
* Resolves the raw $http promise.
|
||||
*/
|
||||
function resolvePromise(response, status, headers, statusText) {
|
||||
function resolvePromise(response, status, headers, statusText, xhrStatus) {
|
||||
//status: HTTP response status code, 0, -1 (aborted by timeout / promise)
|
||||
status = status >= -1 ? status : 0;
|
||||
|
||||
@@ -1363,12 +1394,13 @@ function $HttpProvider() {
|
||||
status: status,
|
||||
headers: headersGetter(headers),
|
||||
config: config,
|
||||
statusText: statusText
|
||||
statusText: statusText,
|
||||
xhrStatus: xhrStatus
|
||||
});
|
||||
}
|
||||
|
||||
function resolvePromiseWithResult(result) {
|
||||
resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
|
||||
resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText, result.xhrStatus);
|
||||
}
|
||||
|
||||
function removePendingReq() {
|
||||
|
||||
+18
-7
@@ -64,7 +64,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
|
||||
// jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
|
||||
var response = (status === 200) && callbacks.getResponse(callbackPath);
|
||||
completeRequest(callback, status, response, '', text);
|
||||
completeRequest(callback, status, response, '', text, 'complete');
|
||||
callbacks.removeCallback(callbackPath);
|
||||
});
|
||||
} else {
|
||||
@@ -99,18 +99,29 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
status,
|
||||
response,
|
||||
xhr.getAllResponseHeaders(),
|
||||
statusText);
|
||||
statusText,
|
||||
'complete');
|
||||
};
|
||||
|
||||
var requestError = function() {
|
||||
// The response is always empty
|
||||
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
|
||||
completeRequest(callback, -1, null, null, '');
|
||||
completeRequest(callback, -1, null, null, '', 'error');
|
||||
};
|
||||
|
||||
var requestAborted = function() {
|
||||
completeRequest(callback, -1, null, null, '', 'abort');
|
||||
};
|
||||
|
||||
var requestTimeout = function() {
|
||||
// The response is always empty
|
||||
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
|
||||
completeRequest(callback, -1, null, null, '', 'timeout');
|
||||
};
|
||||
|
||||
xhr.onerror = requestError;
|
||||
xhr.onabort = requestError;
|
||||
xhr.ontimeout = requestError;
|
||||
xhr.onabort = requestAborted;
|
||||
xhr.ontimeout = requestTimeout;
|
||||
|
||||
forEach(eventHandlers, function(value, key) {
|
||||
xhr.addEventListener(key, value);
|
||||
@@ -160,14 +171,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
}
|
||||
}
|
||||
|
||||
function completeRequest(callback, status, response, headersString, statusText) {
|
||||
function completeRequest(callback, status, response, headersString, statusText, xhrStatus) {
|
||||
// cancel timeout and subsequent timeout promise resolution
|
||||
if (isDefined(timeoutId)) {
|
||||
$browserDefer.cancel(timeoutId);
|
||||
}
|
||||
jsonpDone = xhr = null;
|
||||
|
||||
callback(status, response, headersString, statusText);
|
||||
callback(status, response, headersString, statusText, xhrStatus);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ function $IntervalProvider() {
|
||||
* @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">
|
||||
@@ -190,7 +190,7 @@ function $IntervalProvider() {
|
||||
interval.cancel = function(promise) {
|
||||
if (promise && promise.$$intervalId in intervals) {
|
||||
// Interval cancels should not report as unhandled promise.
|
||||
intervals[promise.$$intervalId].promise.catch(noop);
|
||||
markQExceptionHandled(intervals[promise.$$intervalId].promise);
|
||||
intervals[promise.$$intervalId].reject('canceled');
|
||||
$window.clearInterval(promise.$$intervalId);
|
||||
delete intervals[promise.$$intervalId];
|
||||
|
||||
+19
-23
@@ -11,6 +11,14 @@
|
||||
*
|
||||
* The main purpose of this service is to simplify debugging and troubleshooting.
|
||||
*
|
||||
* To reveal the location of the calls to `$log` in the JavaScript console,
|
||||
* you can "blackbox" the AngularJS source in your browser:
|
||||
*
|
||||
* [Mozilla description of blackboxing](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Black_box_a_source).
|
||||
* [Chrome description of blackboxing](https://developer.chrome.com/devtools/docs/blackboxing).
|
||||
*
|
||||
* Note: Not all browsers support blackboxing.
|
||||
*
|
||||
* The default is to log `debug` messages. You can use
|
||||
* {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
|
||||
*
|
||||
@@ -132,7 +140,7 @@ function $LogProvider() {
|
||||
};
|
||||
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (isError(arg)) {
|
||||
if (arg.stack && formatStackTrace) {
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
|
||||
? 'Error: ' + arg.message + '\n' + arg.stack
|
||||
@@ -146,29 +154,17 @@ function $LogProvider() {
|
||||
|
||||
function consoleLog(type) {
|
||||
var console = $window.console || {},
|
||||
logFn = console[type] || console.log || noop,
|
||||
hasApply = false;
|
||||
logFn = console[type] || console.log || noop;
|
||||
|
||||
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
|
||||
// The reason behind this is that console.log has type "object" in IE8...
|
||||
try {
|
||||
hasApply = !!logFn.apply;
|
||||
} catch (e) { /* empty */ }
|
||||
|
||||
if (hasApply) {
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg) {
|
||||
args.push(formatError(arg));
|
||||
});
|
||||
return logFn.apply(console, args);
|
||||
};
|
||||
}
|
||||
|
||||
// we are IE which either doesn't have window.console => this is noop and we do nothing,
|
||||
// or we are IE where console.log doesn't have apply so we log at least first 2 args
|
||||
return function(arg1, arg2) {
|
||||
logFn(arg1, arg2 == null ? '' : arg2);
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg) {
|
||||
args.push(formatError(arg));
|
||||
});
|
||||
// Support: IE 9 only
|
||||
// console methods don't inherit from Function.prototype in IE 9 so we can't
|
||||
// call `logFn.apply(console, args)` directly.
|
||||
return Function.prototype.apply.call(logFn, console, args);
|
||||
};
|
||||
}
|
||||
}];
|
||||
|
||||
+99
-68
@@ -622,15 +622,47 @@ function isStateless($filter, filterName) {
|
||||
return !fn.$stateful;
|
||||
}
|
||||
|
||||
function findConstantAndWatchExpressions(ast, $filter) {
|
||||
var PURITY_ABSOLUTE = 1;
|
||||
var PURITY_RELATIVE = 2;
|
||||
|
||||
// Detect nodes which could depend on non-shallow state of objects
|
||||
function isPure(node, parentIsPure) {
|
||||
switch (node.type) {
|
||||
// Computed members might invoke a stateful toString()
|
||||
case AST.MemberExpression:
|
||||
if (node.computed) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unary always convert to primative
|
||||
case AST.UnaryExpression:
|
||||
return PURITY_ABSOLUTE;
|
||||
|
||||
// The binary + operator can invoke a stateful toString().
|
||||
case AST.BinaryExpression:
|
||||
return node.operator !== '+' ? PURITY_ABSOLUTE : false;
|
||||
|
||||
// Functions / filters probably read state from within objects
|
||||
case AST.CallExpression:
|
||||
return false;
|
||||
}
|
||||
|
||||
return (undefined === parentIsPure) ? PURITY_RELATIVE : parentIsPure;
|
||||
}
|
||||
|
||||
function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
||||
var allConstants;
|
||||
var argsToWatch;
|
||||
var isStatelessFilter;
|
||||
|
||||
var astIsPure = ast.isPure = isPure(ast, parentIsPure);
|
||||
|
||||
switch (ast.type) {
|
||||
case AST.Program:
|
||||
allConstants = true;
|
||||
forEach(ast.body, function(expr) {
|
||||
findConstantAndWatchExpressions(expr.expression, $filter);
|
||||
findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
|
||||
allConstants = allConstants && expr.expression.constant;
|
||||
});
|
||||
ast.constant = allConstants;
|
||||
@@ -640,26 +672,26 @@ function findConstantAndWatchExpressions(ast, $filter) {
|
||||
ast.toWatch = [];
|
||||
break;
|
||||
case AST.UnaryExpression:
|
||||
findConstantAndWatchExpressions(ast.argument, $filter);
|
||||
findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
|
||||
ast.constant = ast.argument.constant;
|
||||
ast.toWatch = ast.argument.toWatch;
|
||||
break;
|
||||
case AST.BinaryExpression:
|
||||
findConstantAndWatchExpressions(ast.left, $filter);
|
||||
findConstantAndWatchExpressions(ast.right, $filter);
|
||||
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
||||
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
||||
ast.constant = ast.left.constant && ast.right.constant;
|
||||
ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
|
||||
break;
|
||||
case AST.LogicalExpression:
|
||||
findConstantAndWatchExpressions(ast.left, $filter);
|
||||
findConstantAndWatchExpressions(ast.right, $filter);
|
||||
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
||||
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
||||
ast.constant = ast.left.constant && ast.right.constant;
|
||||
ast.toWatch = ast.constant ? [] : [ast];
|
||||
break;
|
||||
case AST.ConditionalExpression:
|
||||
findConstantAndWatchExpressions(ast.test, $filter);
|
||||
findConstantAndWatchExpressions(ast.alternate, $filter);
|
||||
findConstantAndWatchExpressions(ast.consequent, $filter);
|
||||
findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
|
||||
findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
|
||||
findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
|
||||
ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
|
||||
ast.toWatch = ast.constant ? [] : [ast];
|
||||
break;
|
||||
@@ -668,30 +700,28 @@ function findConstantAndWatchExpressions(ast, $filter) {
|
||||
ast.toWatch = [ast];
|
||||
break;
|
||||
case AST.MemberExpression:
|
||||
findConstantAndWatchExpressions(ast.object, $filter);
|
||||
findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
|
||||
if (ast.computed) {
|
||||
findConstantAndWatchExpressions(ast.property, $filter);
|
||||
findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
|
||||
}
|
||||
ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
|
||||
ast.toWatch = [ast];
|
||||
ast.toWatch = ast.constant ? [] : [ast];
|
||||
break;
|
||||
case AST.CallExpression:
|
||||
isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
|
||||
allConstants = isStatelessFilter;
|
||||
argsToWatch = [];
|
||||
forEach(ast.arguments, function(expr) {
|
||||
findConstantAndWatchExpressions(expr, $filter);
|
||||
findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
||||
allConstants = allConstants && expr.constant;
|
||||
if (!expr.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
||||
}
|
||||
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
||||
});
|
||||
ast.constant = allConstants;
|
||||
ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
|
||||
break;
|
||||
case AST.AssignmentExpression:
|
||||
findConstantAndWatchExpressions(ast.left, $filter);
|
||||
findConstantAndWatchExpressions(ast.right, $filter);
|
||||
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
||||
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
||||
ast.constant = ast.left.constant && ast.right.constant;
|
||||
ast.toWatch = [ast];
|
||||
break;
|
||||
@@ -699,11 +729,9 @@ function findConstantAndWatchExpressions(ast, $filter) {
|
||||
allConstants = true;
|
||||
argsToWatch = [];
|
||||
forEach(ast.elements, function(expr) {
|
||||
findConstantAndWatchExpressions(expr, $filter);
|
||||
findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
||||
allConstants = allConstants && expr.constant;
|
||||
if (!expr.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
||||
}
|
||||
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
||||
});
|
||||
ast.constant = allConstants;
|
||||
ast.toWatch = argsToWatch;
|
||||
@@ -712,18 +740,15 @@ function findConstantAndWatchExpressions(ast, $filter) {
|
||||
allConstants = true;
|
||||
argsToWatch = [];
|
||||
forEach(ast.properties, function(property) {
|
||||
findConstantAndWatchExpressions(property.value, $filter);
|
||||
allConstants = allConstants && property.value.constant && !property.computed;
|
||||
if (!property.value.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
|
||||
}
|
||||
findConstantAndWatchExpressions(property.value, $filter, astIsPure);
|
||||
allConstants = allConstants && 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);
|
||||
}
|
||||
//`{[key]: value}` implicitly does `key.toString()` which may be non-pure
|
||||
findConstantAndWatchExpressions(property.key, $filter, /*parentIsPure=*/false);
|
||||
allConstants = allConstants && property.key.constant;
|
||||
argsToWatch.push.apply(argsToWatch, property.key.toWatch);
|
||||
}
|
||||
|
||||
});
|
||||
ast.constant = allConstants;
|
||||
ast.toWatch = argsToWatch;
|
||||
@@ -769,15 +794,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: {},
|
||||
@@ -805,7 +828,7 @@ ASTCompiler.prototype = {
|
||||
var intoId = self.nextId();
|
||||
self.recurse(watch, intoId);
|
||||
self.return_(intoId);
|
||||
self.state.inputs.push(fnKey);
|
||||
self.state.inputs.push({name: fnKey, isPure: watch.isPure});
|
||||
watch.watchId = key;
|
||||
});
|
||||
this.state.computing = 'fn';
|
||||
@@ -832,8 +855,6 @@ ASTCompiler.prototype = {
|
||||
ifDefined,
|
||||
plusFn);
|
||||
this.state = this.stage = undefined;
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -843,13 +864,16 @@ ASTCompiler.prototype = {
|
||||
|
||||
watchFns: function() {
|
||||
var result = [];
|
||||
var fns = this.state.inputs;
|
||||
var inputs = this.state.inputs;
|
||||
var self = this;
|
||||
forEach(fns, function(name) {
|
||||
result.push('var ' + name + '=' + self.generateFunction(name, 's'));
|
||||
forEach(inputs, function(input) {
|
||||
result.push('var ' + input.name + '=' + self.generateFunction(input.name, 's'));
|
||||
if (input.isPure) {
|
||||
result.push(input.name, '.isPure=' + JSON.stringify(input.isPure) + ';');
|
||||
}
|
||||
});
|
||||
if (fns.length) {
|
||||
result.push('fn.inputs=[' + fns.join(',') + '];');
|
||||
if (inputs.length) {
|
||||
result.push('fn.inputs=[' + inputs.map(function(i) { return i.name; }).join(',') + '];');
|
||||
}
|
||||
return result.join('');
|
||||
},
|
||||
@@ -1236,15 +1260,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;
|
||||
@@ -1257,6 +1279,7 @@ ASTInterpreter.prototype = {
|
||||
inputs = [];
|
||||
forEach(toWatch, function(watch, key) {
|
||||
var input = self.recurse(watch);
|
||||
input.isPure = watch.isPure;
|
||||
watch.input = input;
|
||||
inputs.push(input);
|
||||
watch.watchId = key;
|
||||
@@ -1283,8 +1306,6 @@ ASTInterpreter.prototype = {
|
||||
if (inputs) {
|
||||
fn.inputs = inputs;
|
||||
}
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -1613,20 +1634,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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1795,14 +1817,14 @@ function $ParseProvider() {
|
||||
return newValue === oldValueOfValue;
|
||||
}
|
||||
|
||||
if (typeof newValue === 'object' && !compareObjectIdentity) {
|
||||
if (typeof newValue === 'object') {
|
||||
|
||||
// attempt to convert the value to a primitive type
|
||||
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
|
||||
// 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;
|
||||
}
|
||||
@@ -1824,7 +1846,7 @@ function $ParseProvider() {
|
||||
inputExpressions = inputExpressions[0];
|
||||
return scope.$watch(function expressionInputWatch(scope) {
|
||||
var newInputValue = inputExpressions(scope);
|
||||
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
|
||||
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, inputExpressions.isPure)) {
|
||||
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
|
||||
oldInputValueOf = newInputValue && getValueOf(newInputValue);
|
||||
}
|
||||
@@ -1844,7 +1866,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], parsedExpression.literal))) {
|
||||
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], inputExpressions[i].isPure))) {
|
||||
oldInputValues[i] = newInputValue;
|
||||
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
|
||||
}
|
||||
@@ -1942,17 +1964,26 @@ function $ParseProvider() {
|
||||
|
||||
// Propagate $$watchDelegates other then inputsWatchDelegate
|
||||
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
|
||||
// we treat filters - it is assumed to be a pure function unless flagged with $stateful
|
||||
// Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate
|
||||
fn.$$watchDelegate = inputsWatchDelegate;
|
||||
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
|
||||
}
|
||||
|
||||
if (fn.inputs) {
|
||||
fn.inputs = fn.inputs.map(function(e) {
|
||||
// Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
|
||||
// potentially non-pure interceptor function.
|
||||
if (e.isPure === PURITY_RELATIVE) {
|
||||
return function depurifier(s) { return e(s); };
|
||||
}
|
||||
return e;
|
||||
});
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
}];
|
||||
|
||||
+16
-6
@@ -280,7 +280,7 @@ function $$QProvider() {
|
||||
* @param {function(function)} nextTick Function for executing functions in the next turn.
|
||||
* @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
|
||||
* debugging purposes.
|
||||
@ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled
|
||||
* @param {boolean=} errorOnUnhandledRejections Whether an error should be generated on unhandled
|
||||
* promises rejections.
|
||||
* @returns {object} Promise manager.
|
||||
*/
|
||||
@@ -351,7 +351,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
|
||||
state.pending = undefined;
|
||||
try {
|
||||
for (var i = 0, ii = pending.length; i < ii; ++i) {
|
||||
state.pur = true;
|
||||
markQStateExceptionHandled(state);
|
||||
promise = pending[i][0];
|
||||
fn = pending[i][state.status];
|
||||
try {
|
||||
@@ -378,10 +378,10 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
|
||||
// eslint-disable-next-line no-unmodified-loop-condition
|
||||
while (!queueSize && checkQueue.length) {
|
||||
var toCheck = checkQueue.shift();
|
||||
if (!toCheck.pur) {
|
||||
toCheck.pur = true;
|
||||
if (!isStateExceptionHandled(toCheck)) {
|
||||
markQStateExceptionHandled(toCheck);
|
||||
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
|
||||
if (toCheck.value instanceof Error) {
|
||||
if (isError(toCheck.value)) {
|
||||
exceptionHandler(toCheck.value, errorMessage);
|
||||
} else {
|
||||
exceptionHandler(errorMessage);
|
||||
@@ -391,7 +391,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
|
||||
}
|
||||
|
||||
function scheduleProcessQueue(state) {
|
||||
if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !state.pur) {
|
||||
if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !isStateExceptionHandled(state)) {
|
||||
if (queueSize === 0 && checkQueue.length === 0) {
|
||||
nextTick(processChecks);
|
||||
}
|
||||
@@ -671,3 +671,13 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
|
||||
|
||||
return $Q;
|
||||
}
|
||||
|
||||
function isStateExceptionHandled(state) {
|
||||
return !!state.pur;
|
||||
}
|
||||
function markQStateExceptionHandled(state) {
|
||||
state.pur = true;
|
||||
}
|
||||
function markQExceptionHandled(q) {
|
||||
markQStateExceptionHandled(q.$$state);
|
||||
}
|
||||
|
||||
+34
-1
@@ -449,6 +449,12 @@ function $RootScopeProvider() {
|
||||
* values are examined for changes on every call to `$digest`.
|
||||
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
|
||||
*
|
||||
* `$watchGroup` is more performant than watching each expression individually, and should be
|
||||
* used when the listener does not need to know which expression has changed.
|
||||
* If the listener needs to know which expression has changed,
|
||||
* {@link ng.$rootScope.Scope#$watch $watch()} or
|
||||
* {@link ng.$rootScope.Scope#$watchCollection $watchCollection()} should be used.
|
||||
*
|
||||
* @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
|
||||
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
|
||||
*
|
||||
@@ -457,7 +463,34 @@ function $RootScopeProvider() {
|
||||
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
|
||||
* those of `watchExpression`
|
||||
* and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
|
||||
* those of `watchExpression`
|
||||
* those of `watchExpression`.
|
||||
*
|
||||
* Note that `newValues` and `oldValues` reflect the differences in each **individual**
|
||||
* expression, and not the difference of the values between each call of the listener.
|
||||
* That means the difference between `newValues` and `oldValues` cannot be used to determine
|
||||
* which expression has changed / remained stable:
|
||||
*
|
||||
* ```js
|
||||
*
|
||||
* $scope.$watchGroup(['v1', 'v2'], function(newValues, oldValues) {
|
||||
* console.log(newValues, oldValues);
|
||||
* });
|
||||
*
|
||||
* // newValues, oldValues initially
|
||||
* // [undefined, undefined], [undefined, undefined]
|
||||
*
|
||||
* $scope.v1 = 'a';
|
||||
* $scope.v2 = 'a';
|
||||
*
|
||||
* // ['a', 'a'], [undefined, undefined]
|
||||
*
|
||||
* $scope.v2 = 'b'
|
||||
*
|
||||
* // v1 hasn't changed since it became `'a'`, therefore its oldValue is still `undefined`
|
||||
* // ['a', 'b'], [undefined, 'a']
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* The `scope` refers to the current scope.
|
||||
* @returns {function()} Returns a de-registration function for all listeners.
|
||||
*/
|
||||
|
||||
+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.
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ function $TimeoutProvider() {
|
||||
timeout.cancel = function(promise) {
|
||||
if (promise && promise.$$timeoutId in deferreds) {
|
||||
// Timeout cancels should not report an unhandled promise.
|
||||
deferreds[promise.$$timeoutId].promise.catch(noop);
|
||||
markQExceptionHandled(deferreds[promise.$$timeoutId].promise);
|
||||
deferreds[promise.$$timeoutId].reject('canceled');
|
||||
delete deferreds[promise.$$timeoutId];
|
||||
return $browser.defer.cancel(promise.$$timeoutId);
|
||||
|
||||
@@ -160,14 +160,17 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
var callbackRegistry = Object.create(null);
|
||||
|
||||
// remember that the classNameFilter is set during the provider/config
|
||||
// stage therefore we can optimize here and setup a helper function
|
||||
// remember that the `customFilter`/`classNameFilter` are set during the
|
||||
// provider/config stage therefore we can optimize here and setup helper functions
|
||||
var customFilter = $animateProvider.customFilter();
|
||||
var classNameFilter = $animateProvider.classNameFilter();
|
||||
var isAnimatableClassName = !classNameFilter
|
||||
? function() { return true; }
|
||||
: function(className) {
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
var returnTrue = function() { return true; };
|
||||
|
||||
var isAnimatableByFilter = customFilter || returnTrue;
|
||||
var isAnimatableClassName = !classNameFilter ? returnTrue : function(node, options) {
|
||||
var className = [node.getAttribute('class'), options.addClass, options.removeClass].join(' ');
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
|
||||
|
||||
@@ -345,16 +348,13 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
options.to = null;
|
||||
}
|
||||
|
||||
// there are situations where a directive issues an animation for
|
||||
// a jqLite wrapper that contains only comment nodes... If this
|
||||
// happens then there is no way we can perform an animation
|
||||
if (!node) {
|
||||
close();
|
||||
return runner;
|
||||
}
|
||||
|
||||
var className = [node.getAttribute('class'), options.addClass, options.removeClass].join(' ');
|
||||
if (!isAnimatableClassName(className)) {
|
||||
// If animations are hard-disabled for the whole application there is no need to continue.
|
||||
// There are also situations where a directive issues an animation for a jqLite wrapper that
|
||||
// contains only comment nodes. In this case, there is no way we can perform an animation.
|
||||
if (!animationsEnabled ||
|
||||
!node ||
|
||||
!isAnimatableByFilter(node, event, initialOptions) ||
|
||||
!isAnimatableClassName(node, options)) {
|
||||
close();
|
||||
return runner;
|
||||
}
|
||||
@@ -363,12 +363,11 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
var documentHidden = $$isDocumentHidden();
|
||||
|
||||
// this is a hard disable of all animations for the application or on
|
||||
// the element itself, therefore there is no need to continue further
|
||||
// past this point if not enabled
|
||||
// This is a hard disable of all animations the element itself, therefore there is no need to
|
||||
// continue further past this point if not enabled
|
||||
// Animations are also disabled if the document is currently hidden (page is not visible
|
||||
// to the user), because browsers slow down or do not flush calls to requestAnimationFrame
|
||||
var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
|
||||
var skipAnimations = documentHidden || disabledElementsLookup.get(node);
|
||||
var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
|
||||
var hasExistingAnimation = !!existingAnimation.state;
|
||||
|
||||
|
||||
Vendored
+7
-7
@@ -80,14 +80,14 @@ $provide.value("$locale", {
|
||||
5,
|
||||
6
|
||||
],
|
||||
"fullDate": "EEEE d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y h:mm:ss a",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "y-MM-dd h:mm a",
|
||||
"fullDate": "EEEE, dd MMMM y",
|
||||
"longDate": "dd MMMM y",
|
||||
"medium": "dd MMM y HH:mm:ss",
|
||||
"mediumDate": "dd MMM y",
|
||||
"mediumTime": "HH:mm:ss",
|
||||
"short": "y-MM-dd HH:mm",
|
||||
"shortDate": "y-MM-dd",
|
||||
"shortTime": "h:mm a"
|
||||
"shortTime": "HH:mm"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "$",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user