Compare commits
37 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 |
@@ -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
|
||||
|
||||
+59
-13
@@ -7,11 +7,10 @@ cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- 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:
|
||||
@@ -36,22 +36,68 @@ addons:
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
before_install:
|
||||
- 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)
|
||||
|
||||
|
||||
+50
-15
@@ -1,3 +1,38 @@
|
||||
<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)
|
||||
|
||||
@@ -131,7 +166,7 @@
|
||||
|
||||
|
||||
## 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
|
||||
@@ -700,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:
|
||||
|
||||
@@ -1902,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:**
|
||||
@@ -1990,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:
|
||||
@@ -5104,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.
|
||||
|
||||
@@ -5220,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.
|
||||
|
||||
@@ -5439,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();
|
||||
}));
|
||||
|
||||
@@ -6915,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">
|
||||
@@ -6924,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`:
|
||||
|
||||
@@ -15498,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']);
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1684,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.
|
||||
|
||||
|
||||
+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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+25
-4
@@ -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,6 +134,13 @@ 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 6S',
|
||||
|
||||
+7
-1
@@ -30,6 +30,7 @@
|
||||
"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",
|
||||
@@ -69,6 +70,7 @@
|
||||
"karma-ng-scenario": "^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",
|
||||
@@ -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,23 +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"
|
||||
|
||||
# FIXME: We gave up publishing to code.angularjs.org because the GCE automatically removes firewall
|
||||
# rules that allow access to port 8003.
|
||||
|
||||
# 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
|
||||
|
||||
@@ -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
|
||||
@@ -1414,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;
|
||||
/**
|
||||
@@ -3441,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,
|
||||
@@ -3458,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)) {
|
||||
@@ -3485,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;
|
||||
@@ -3529,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;
|
||||
@@ -3554,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;
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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.
|
||||
|
||||
+17
-10
@@ -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,10 +137,15 @@ function defaultHttpResponseTransform(data, headers) {
|
||||
|
||||
if (tempData) {
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(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);
|
||||
}
|
||||
@@ -453,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
|
||||
@@ -1294,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 {
|
||||
@@ -1353,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);
|
||||
@@ -1364,7 +1370,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
function resolveHttpPromise() {
|
||||
resolvePromise(response, status, headersString, statusText);
|
||||
resolvePromise(response, status, headersString, statusText, xhrStatus);
|
||||
}
|
||||
|
||||
if (useApplyAsync) {
|
||||
@@ -1379,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;
|
||||
|
||||
@@ -1388,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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+9
-16
@@ -705,7 +705,7 @@ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
||||
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;
|
||||
@@ -714,9 +714,7 @@ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
||||
forEach(ast.arguments, function(expr) {
|
||||
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];
|
||||
@@ -733,9 +731,7 @@ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
||||
forEach(ast.elements, function(expr) {
|
||||
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;
|
||||
@@ -745,17 +741,14 @@ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
||||
argsToWatch = [];
|
||||
forEach(ast.properties, function(property) {
|
||||
findConstantAndWatchExpressions(property.value, $filter, astIsPure);
|
||||
allConstants = allConstants && property.value.constant && !property.computed;
|
||||
if (!property.value.constant) {
|
||||
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
|
||||
}
|
||||
allConstants = allConstants && property.value.constant;
|
||||
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
|
||||
if (property.computed) {
|
||||
findConstantAndWatchExpressions(property.key, $filter, astIsPure);
|
||||
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;
|
||||
|
||||
Vendored
+4
-4
@@ -1354,8 +1354,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
|
||||
return function() {
|
||||
return angular.isNumber(status)
|
||||
? [status, data, headers, statusText]
|
||||
: [200, status, data, headers];
|
||||
? [status, data, headers, statusText, 'complete']
|
||||
: [200, status, data, headers, 'complete'];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1391,14 +1391,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||
var response = wrapped.response(method, url, data, headers, wrapped.params(url));
|
||||
xhr.$$respHeaders = response[2];
|
||||
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
|
||||
copy(response[3] || ''));
|
||||
copy(response[3] || ''), copy(response[4]));
|
||||
}
|
||||
|
||||
function handleTimeout() {
|
||||
for (var i = 0, ii = responses.length; i < ii; i++) {
|
||||
if (responses[i] === handleResponse) {
|
||||
responses.splice(i, 1);
|
||||
callback(-1, undefined, '');
|
||||
callback(-1, undefined, '', undefined, 'timeout');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,12 @@ function shallowClearAndCopy(src, dst) {
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
|
||||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
|
||||
* with `http response` object. See {@link ng.$http $http interceptors}.
|
||||
* with `http response` object. See {@link ng.$http $http interceptors}. In addition, the
|
||||
* resource instance or array object is accessible by the `resource` property of the
|
||||
* `http response` object.
|
||||
* Keep in mind that the associated promise will be resolved with the value returned by the
|
||||
* response interceptor, if one is specified. The default response interceptor returns
|
||||
* `response.resource` (i.e. the resource instance or array).
|
||||
* - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not.
|
||||
* If not specified only POST, PUT and PATCH requests will have a body.
|
||||
*
|
||||
@@ -267,8 +272,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
|
||||
* rendering until the resource(s) are loaded.
|
||||
*
|
||||
* On failure, the promise is rejected with the {@link ng.$http http response} object, without
|
||||
* the `resource` property.
|
||||
* On failure, the promise is rejected with the {@link ng.$http http response} object.
|
||||
*
|
||||
* If an interceptor object was provided, the promise will instead be resolved with the value
|
||||
* returned by the interceptor.
|
||||
@@ -488,7 +492,7 @@ angular.module('ngResource', ['ng']).
|
||||
* $resourceProvider.defaults.actions.update = {
|
||||
* method: 'PUT'
|
||||
* };
|
||||
* });
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* Or you can even overwrite the whole `actions` list and specify your own:
|
||||
@@ -776,6 +780,9 @@ angular.module('ngResource', ['ng']).
|
||||
response.resource = value;
|
||||
|
||||
return response;
|
||||
}, function(response) {
|
||||
response.resource = value;
|
||||
return $q.reject(response);
|
||||
});
|
||||
|
||||
promise = promise['finally'](function() {
|
||||
@@ -823,7 +830,9 @@ angular.module('ngResource', ['ng']).
|
||||
|
||||
function cancelRequest(value) {
|
||||
promise.catch(noop);
|
||||
timeoutDeferred.resolve(value);
|
||||
if (timeoutDeferred !== null) {
|
||||
timeoutDeferred.resolve(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+22
-4
@@ -1731,8 +1731,7 @@ describe('angular', function() {
|
||||
};
|
||||
}
|
||||
|
||||
it('should bootstrap from an extension into an extension document for same-origin documents only', function() {
|
||||
|
||||
describe('from extensions into extension documents', function() {
|
||||
// Extension URLs are browser-specific, so we must choose a scheme that is supported by the browser to make
|
||||
// sure that the URL is properly parsed.
|
||||
var protocol;
|
||||
@@ -1749,10 +1748,29 @@ describe('angular', function() {
|
||||
protocol = 'browserext:'; // Upcoming standard scheme.
|
||||
}
|
||||
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something'}, protocol))).toBe(true);
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something-else'}, protocol))).toBe(false);
|
||||
|
||||
if (protocol === 'ms-browser-extension:') {
|
||||
// Support: Edge 13-15
|
||||
// In Edge, URLs with protocol 'ms-browser-extension:' return "null" for the origin,
|
||||
// therefore it's impossible to know if a script is same-origin.
|
||||
it('should not bootstrap for same-origin documents', function() {
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something'}, protocol))).toBe(false);
|
||||
});
|
||||
|
||||
} else {
|
||||
it('should bootstrap for same-origin documents', function() {
|
||||
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something'}, protocol))).toBe(true);
|
||||
});
|
||||
}
|
||||
|
||||
it('should not bootstrap for cross-origin documents', function() {
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something-else'}, protocol))).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should bootstrap from a script with no source (e.g. src, href or xlink:href attributes)', function() {
|
||||
|
||||
expect(allowAutoBootstrap(createFakeDoc({src: null}))).toBe(true);
|
||||
|
||||
@@ -329,7 +329,7 @@ describe('$$AnimateRunner', function() {
|
||||
expect(status).toBe(true);
|
||||
}));
|
||||
|
||||
it('should break the chian when a function evaluates to false',
|
||||
it('should break the chain when a function evaluates to false',
|
||||
inject(function($$rAF, $$AnimateRunner) {
|
||||
|
||||
var runner1 = new $$AnimateRunner();
|
||||
|
||||
@@ -180,6 +180,15 @@ describe('$compile', function() {
|
||||
inject();
|
||||
});
|
||||
|
||||
it('should allow strictComponentBindingsEnabled to be configured', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.strictComponentBindingsEnabled()).toBe(false); // the default
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
expect($compileProvider.strictComponentBindingsEnabled()).toBe(true);
|
||||
});
|
||||
inject();
|
||||
});
|
||||
|
||||
it('should allow onChangesTtl to be configured', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.onChangesTtl()).toBe(10); // the default
|
||||
@@ -2557,6 +2566,16 @@ describe('$compile', function() {
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
directive('prototypeMethodNameAsScopeVarD', function() {
|
||||
return {
|
||||
scope: {
|
||||
'constructor': '<?',
|
||||
'valueOf': '<'
|
||||
},
|
||||
restrict: 'AE',
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
directive('watchAsScopeVar', function() {
|
||||
return {
|
||||
scope: {
|
||||
@@ -2865,6 +2884,57 @@ describe('$compile', function() {
|
||||
})
|
||||
);
|
||||
|
||||
it('should throw an error for undefined non-optional "=" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-a></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).toThrowMinErr('$compile',
|
||||
'missingattr',
|
||||
'Attribute \'valueOf\' of \'prototypeMethodNameAs' +
|
||||
'ScopeVarA\' is non-optional and must be set!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for set non-optional "=" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-a constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for undefined optional "=" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-a value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle "@" bindings with same method names in Object.prototype correctly when not present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
@@ -2902,6 +2972,57 @@ describe('$compile', function() {
|
||||
})
|
||||
);
|
||||
|
||||
it('should throw an error for undefined non-optional "@" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-b></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).toThrowMinErr('$compile',
|
||||
'missingattr',
|
||||
'Attribute \'valueOf\' of \'prototypeMethodNameAs' +
|
||||
'ScopeVarB\' is non-optional and must be set!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for set non-optional "@" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-b constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for undefined optional "@" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-b value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle "&" bindings with same method names in Object.prototype correctly when not present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
@@ -2934,6 +3055,108 @@ describe('$compile', function() {
|
||||
})
|
||||
);
|
||||
|
||||
it('should throw an error for undefined non-optional "&" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-c></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).toThrowMinErr('$compile',
|
||||
'missingattr',
|
||||
'Attribute \'valueOf\' of \'prototypeMethodNameAs' +
|
||||
'ScopeVarC\' is non-optional and must be set!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for set non-optional "&" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-c constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for undefined optional "&" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-c value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error for undefined non-optional "<" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-d></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).toThrowMinErr('$compile',
|
||||
'missingattr',
|
||||
'Attribute \'valueOf\' of \'prototypeMethodNameAs' +
|
||||
'ScopeVarD\' is non-optional and must be set!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for set non-optional "<" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-d constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw an error for undefined optional "<" bindings when ' +
|
||||
'strictComponentBindingsEnabled is true', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.strictComponentBindingsEnabled(true);
|
||||
});
|
||||
inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-d value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not throw exception when using "watch" as binding in Firefox', inject(
|
||||
function($rootScope, $compile) {
|
||||
$rootScope.watch = 'watch';
|
||||
|
||||
@@ -386,6 +386,9 @@ describe('form', function() {
|
||||
doc = jqLite('<form ng-submit="submitMe()">' +
|
||||
'<input type="submit" value="submit">' +
|
||||
'</form>');
|
||||
// Support: Chrome 60+ (on Windows)
|
||||
// We need to add the form to the DOM in order for `submit` events to be properly fired.
|
||||
window.document.body.appendChild(doc[0]);
|
||||
|
||||
var assertPreventDefaultListener = function(e) {
|
||||
reloadPrevented = e.defaultPrevented || (e.returnValue === false);
|
||||
@@ -420,15 +423,18 @@ describe('form', function() {
|
||||
inject(function($timeout) {
|
||||
doc = jqLite('<div>' +
|
||||
'<form ng-submit="submitMe()">' +
|
||||
'<button ng-click="destroy()"></button>' +
|
||||
'<button type="submit" ng-click="destroy()"></button>' +
|
||||
'</form>' +
|
||||
'</div>');
|
||||
// Support: Chrome 60+ (on Windows)
|
||||
// We need to add the form to the DOM in order for `submit` events to be properly fired.
|
||||
window.document.body.appendChild(doc[0]);
|
||||
|
||||
var form = doc.find('form'),
|
||||
destroyed = false,
|
||||
nextTurn = false,
|
||||
submitted = false,
|
||||
reloadPrevented;
|
||||
reloadPrevented = 'never called';
|
||||
|
||||
scope.destroy = function() {
|
||||
// yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep
|
||||
@@ -466,6 +472,12 @@ describe('form', function() {
|
||||
// the issue in the wild, I'm not going to bother to do it
|
||||
// now. (i)
|
||||
|
||||
// Support: Chrome 60+ (on Windows)
|
||||
// Chrome 60+ on Windows does not fire `submit` events when the form is not attached to
|
||||
// the DOM. Verify that the `submit` listener was either never fired or (if fired) the
|
||||
// reload was prevented.
|
||||
expect(reloadPrevented).not.toBe(false);
|
||||
|
||||
// prevent mem leak in test
|
||||
form[0].removeEventListener('submit', assertPreventDefaultListener);
|
||||
})
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
describe('input', function() {
|
||||
var helper = {}, $compile, $rootScope, $browser, $sniffer, $timeout, $q;
|
||||
|
||||
// UA sniffing to exclude Edge from some date input tests
|
||||
var isEdge = /\bEdge\//.test(window.navigator.userAgent);
|
||||
|
||||
generateInputCompilerHelper(helper);
|
||||
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_, _$timeout_, _$q_) {
|
||||
@@ -688,18 +691,20 @@ describe('input', function() {
|
||||
expect($rootScope.form.alias.$error.month).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
|
||||
|
||||
helper.changeInputValueTo('10123-03');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0));
|
||||
if (!isEdge) {
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 3, 1, 0, 0, 0));
|
||||
helper.changeInputValueTo('10123-03');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0));
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 3, 1, 0, 0, 0));
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-04');
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-04');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
it('should only change the month of a bound date', function() {
|
||||
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
|
||||
@@ -899,17 +904,19 @@ describe('input', function() {
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
|
||||
if (!isEdge) {
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
|
||||
|
||||
helper.changeInputValueTo('10123-W03');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21));
|
||||
helper.changeInputValueTo('10123-W03');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21));
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 0, 28));
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 0, 28));
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-W04');
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-W04');
|
||||
});
|
||||
}
|
||||
|
||||
it('should use UTC if specified in the options', function() {
|
||||
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
|
||||
@@ -1195,18 +1202,22 @@ describe('input', function() {
|
||||
expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 1, 2, 0));
|
||||
});
|
||||
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" />');
|
||||
|
||||
helper.changeInputValueTo('10123-01-01T01:02');
|
||||
expect(+$rootScope.value).toBe(+new Date(10123, 0, 1, 1, 2, 0));
|
||||
if (!isEdge) {
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" />');
|
||||
|
||||
helper.changeInputValueTo('10123-01-01T01:02');
|
||||
expect(+$rootScope.value).toBe(+new Date(10123, 0, 1, 1, 2, 0));
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(20456, 1, 1, 1, 2, 0);
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-02-01T01:02:00.000');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(20456, 1, 1, 1, 2, 0);
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-02-01T01:02:00.000');
|
||||
}
|
||||
);
|
||||
|
||||
it('should label parse errors as `datetimelocal`', function() {
|
||||
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="val" name="alias" />', {
|
||||
@@ -1800,19 +1811,20 @@ describe('input', function() {
|
||||
}
|
||||
);
|
||||
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
|
||||
if (!isEdge) {
|
||||
it('should allow four or more digits in year', function() {
|
||||
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
|
||||
|
||||
helper.changeInputValueTo('10123-01-01');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0));
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 1, 1, 0, 0, 0));
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-02-01');
|
||||
}
|
||||
);
|
||||
helper.changeInputValueTo('10123-01-01');
|
||||
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0));
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.value = new Date(Date.UTC(20456, 1, 1, 0, 0, 0));
|
||||
});
|
||||
expect(inputElm.val()).toBe('20456-02-01');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
it('should label parse errors as `date`', function() {
|
||||
var inputElm = helper.compileInput('<input type="date" ng-model="val" name="alias" />', {
|
||||
@@ -4224,10 +4236,14 @@ describe('input', function() {
|
||||
});
|
||||
|
||||
expect(inputElm[0].value).toBe('');
|
||||
// Support: IE 9-11
|
||||
// Support: IE 9-11, Edge
|
||||
// In IE it is not possible to remove the `value` attribute from an input element.
|
||||
if (!msie) {
|
||||
if (!msie && !isEdge) {
|
||||
expect(inputElm[0].getAttribute('value')).toBeNull();
|
||||
} else {
|
||||
// Support: IE 9-11, Edge
|
||||
// This will fail if the Edge bug gets fixed
|
||||
expect(inputElm[0].getAttribute('value')).toBe('something');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -12,15 +12,20 @@ describe('event directives', function() {
|
||||
describe('ngSubmit', function() {
|
||||
|
||||
it('should get called on form submit', inject(function($rootScope, $compile) {
|
||||
element = $compile('<form action="/foo" ng-submit="submitted = true">' +
|
||||
'<input type="submit"/>' +
|
||||
element = $compile(
|
||||
'<form action="/foo" ng-submit="submitted = true">' +
|
||||
'<input type="submit" />' +
|
||||
'</form>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// Support: Chrome 60+
|
||||
// We need to add the form to the DOM in order for `submit` events to be properly fired.
|
||||
window.document.body.appendChild(element[0]);
|
||||
|
||||
// prevent submit within the test harness
|
||||
element.on('submit', function(e) { e.preventDefault(); });
|
||||
|
||||
expect($rootScope.submitted).not.toBeDefined();
|
||||
expect($rootScope.submitted).toBeUndefined();
|
||||
|
||||
browserTrigger(element.children()[0]);
|
||||
expect($rootScope.submitted).toEqual(true);
|
||||
@@ -33,15 +38,20 @@ describe('event directives', function() {
|
||||
}
|
||||
};
|
||||
|
||||
element = $compile('<form action="/foo" ng-submit="formSubmission($event)">' +
|
||||
'<input type="submit"/>' +
|
||||
element = $compile(
|
||||
'<form action="/foo" ng-submit="formSubmission($event)">' +
|
||||
'<input type="submit" />' +
|
||||
'</form>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// Support: Chrome 60+ (on Windows)
|
||||
// We need to add the form to the DOM in order for `submit` events to be properly fired.
|
||||
window.document.body.appendChild(element[0]);
|
||||
|
||||
// prevent submit within the test harness
|
||||
element.on('submit', function(e) { e.preventDefault(); });
|
||||
|
||||
expect($rootScope.formSubmitted).not.toBeDefined();
|
||||
expect($rootScope.formSubmitted).toBeUndefined();
|
||||
|
||||
browserTrigger(element.children()[0]);
|
||||
expect($rootScope.formSubmitted).toEqual('foo');
|
||||
|
||||
@@ -2922,84 +2922,137 @@ describe('ngOptions', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not re-set the `selected` property if it already has the correct value', function() {
|
||||
scope.values = [{name: 'A'}, {name: 'B'}];
|
||||
createMultiSelect();
|
||||
// Support: Safari 9
|
||||
// This test relies defining a getter/setter `selected` property on either `<option>` elements
|
||||
// or their prototype. Some browsers (including Safari 9) are very flakey when the
|
||||
// getter/setter is not defined on the prototype (probably due to some bug). On Safari 9, the
|
||||
// getter/setter that is already defined on the `<option>` element's prototype is not
|
||||
// configurable, so we can't overwrite it with our spy.
|
||||
if (!/\b9(?:\.\d+)+ safari/i.test(window.navigator.userAgent)) {
|
||||
it('should not re-set the `selected` property if it already has the correct value', function() {
|
||||
scope.values = [{name: 'A'}, {name: 'B'}];
|
||||
createMultiSelect();
|
||||
|
||||
var options = element.find('option');
|
||||
var optionsSetSelected = [];
|
||||
var _selected = [];
|
||||
var options = element.find('option');
|
||||
var optionsSetSelected = [];
|
||||
var _selected = [];
|
||||
|
||||
// Set up spies
|
||||
forEach(options, function(option, i) {
|
||||
optionsSetSelected[i] = jasmine.createSpy('optionSetSelected' + i);
|
||||
_selected[i] = option.selected;
|
||||
Object.defineProperty(option, 'selected', {
|
||||
get: function() { return _selected[i]; },
|
||||
set: optionsSetSelected[i].and.callFake(function(value) { _selected[i] = value; })
|
||||
// Set up spies
|
||||
var optionProto = Object.getPrototypeOf(options[0]);
|
||||
var originalSelectedDescriptor = isFunction(Object.getOwnPropertyDescriptor) &&
|
||||
Object.getOwnPropertyDescriptor(optionProto, 'selected');
|
||||
var addSpiesOnProto = originalSelectedDescriptor && originalSelectedDescriptor.configurable;
|
||||
|
||||
forEach(options, function(option, i) {
|
||||
var setSelected = function(value) { _selected[i] = value; };
|
||||
optionsSetSelected[i] = jasmine.createSpy('optionSetSelected' + i).and.callFake(setSelected);
|
||||
setSelected(option.selected);
|
||||
});
|
||||
|
||||
if (!addSpiesOnProto) {
|
||||
forEach(options, function(option, i) {
|
||||
Object.defineProperty(option, 'selected', {
|
||||
get: function() { return _selected[i]; },
|
||||
set: optionsSetSelected[i]
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Support: Firefox 54+
|
||||
// We cannot use the above (simpler) method on all browsers because of Firefox 54+, which
|
||||
// is very flaky when the getter/setter property is defined on the element itself and not
|
||||
// the prototype. (Possibly the result of some (buggy?) optimization.)
|
||||
var getSelected = function(index) { return _selected[index]; };
|
||||
var setSelected = function(index, value) { optionsSetSelected[index](value); };
|
||||
var getSelectedOriginal = function(option) {
|
||||
return originalSelectedDescriptor.get.call(option);
|
||||
};
|
||||
var setSelectedOriginal = function(option, value) {
|
||||
originalSelectedDescriptor.set.call(option, value);
|
||||
};
|
||||
var getIndexAndCall = function(option, foundFn, notFoundFn, value) {
|
||||
for (var i = 0, ii = options.length; i < ii; ++i) {
|
||||
if (options[i] === option) return foundFn(i, value);
|
||||
}
|
||||
return notFoundFn(option, value);
|
||||
};
|
||||
|
||||
Object.defineProperty(optionProto, 'selected', {
|
||||
get: function() {
|
||||
return getIndexAndCall(this, getSelected, getSelectedOriginal);
|
||||
},
|
||||
set: function(value) {
|
||||
return getIndexAndCall(this, setSelected, setSelectedOriginal, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Select `optionA`
|
||||
scope.$apply('selected = [values[0]]');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Select `optionB` (`optionA` remains selected)
|
||||
scope.$apply('selected.push(values[1])');
|
||||
|
||||
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
|
||||
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(true);
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionA` (`optionB` remains selected)
|
||||
scope.$apply('selected.shift()');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(false);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Reselect `optionA` (`optionB` remains selected)
|
||||
scope.$apply('selected.push(values[0])');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionB` (`optionA` remains selected)
|
||||
scope.$apply('selected.shift()');
|
||||
|
||||
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
|
||||
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(false);
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionA`
|
||||
scope.$apply('selected.length = 0');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(false);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Support: Firefox 54+
|
||||
// Restore `originalSelectedDescriptor`
|
||||
if (addSpiesOnProto) {
|
||||
Object.defineProperty(optionProto, 'selected', originalSelectedDescriptor);
|
||||
}
|
||||
});
|
||||
|
||||
// Select `optionA`
|
||||
scope.$apply('selected = [values[0]]');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Select `optionB` (`optionA` remains selected)
|
||||
scope.$apply('selected.push(values[1])');
|
||||
|
||||
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
|
||||
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(true);
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionA` (`optionB` remains selected)
|
||||
scope.$apply('selected.shift()');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(false);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Reselect `optionA` (`optionB` remains selected)
|
||||
scope.$apply('selected.push(values[0])');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(true);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionB` (`optionA` remains selected)
|
||||
scope.$apply('selected.shift()');
|
||||
|
||||
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
|
||||
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(false);
|
||||
expect(options[0].selected).toBe(true);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
|
||||
// Unselect `optionA`
|
||||
scope.$apply('selected.length = 0');
|
||||
|
||||
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
|
||||
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
|
||||
expect(options[0].selected).toBe(false);
|
||||
expect(options[1].selected).toBe(false);
|
||||
optionsSetSelected[0].calls.reset();
|
||||
optionsSetSelected[1].calls.reset();
|
||||
});
|
||||
}
|
||||
|
||||
if (window.MutationObserver) {
|
||||
//IE9 and IE10 do not support MutationObserver
|
||||
|
||||
@@ -174,11 +174,12 @@ describe('$httpBackend', function() {
|
||||
});
|
||||
|
||||
it('should complete the request on timeout', function() {
|
||||
callback.and.callFake(function(status, response, headers, statusText) {
|
||||
callback.and.callFake(function(status, response, headers, statusText, xhrStatus) {
|
||||
expect(status).toBe(-1);
|
||||
expect(response).toBe(null);
|
||||
expect(headers).toBe(null);
|
||||
expect(statusText).toBe('');
|
||||
expect(xhrStatus).toBe('timeout');
|
||||
});
|
||||
$backend('GET', '/url', null, callback, {});
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
@@ -189,6 +190,60 @@ describe('$httpBackend', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should complete the request on abort', function() {
|
||||
callback.and.callFake(function(status, response, headers, statusText, xhrStatus) {
|
||||
expect(status).toBe(-1);
|
||||
expect(response).toBe(null);
|
||||
expect(headers).toBe(null);
|
||||
expect(statusText).toBe('');
|
||||
expect(xhrStatus).toBe('abort');
|
||||
});
|
||||
$backend('GET', '/url', null, callback, {});
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
xhr.onabort();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should complete the request on error', function() {
|
||||
callback.and.callFake(function(status, response, headers, statusText, xhrStatus) {
|
||||
expect(status).toBe(-1);
|
||||
expect(response).toBe(null);
|
||||
expect(headers).toBe(null);
|
||||
expect(statusText).toBe('');
|
||||
expect(xhrStatus).toBe('error');
|
||||
});
|
||||
$backend('GET', '/url', null, callback, {});
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
xhr.onerror();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should complete the request on success', function() {
|
||||
callback.and.callFake(function(status, response, headers, statusText, xhrStatus) {
|
||||
expect(status).toBe(200);
|
||||
expect(response).toBe('response');
|
||||
expect(headers).toBe('');
|
||||
expect(statusText).toBe('');
|
||||
expect(xhrStatus).toBe('complete');
|
||||
});
|
||||
$backend('GET', '/url', null, callback, {});
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
xhr.statusText = '';
|
||||
xhr.response = 'response';
|
||||
xhr.status = 200;
|
||||
xhr.onload();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should abort request on timeout', function() {
|
||||
callback.and.callFake(function(status, response) {
|
||||
expect(status).toBe(-1);
|
||||
|
||||
+76
-2
@@ -448,6 +448,28 @@ describe('$http', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should pass xhrStatus in response object when a request is successful', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(200, 'SUCCESS', {}, 'OK');
|
||||
$http({url: '/url', method: 'GET'}).then(function(response) {
|
||||
expect(response.xhrStatus).toBe('complete');
|
||||
callback();
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should pass xhrStatus in response object when a request fails', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(404, 'ERROR', {}, 'Not Found');
|
||||
$http({url: '/url', method: 'GET'}).then(null, function(response) {
|
||||
expect(response.xhrStatus).toBe('complete');
|
||||
callback();
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should pass in the response object when a request failed', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'});
|
||||
@@ -1380,8 +1402,45 @@ describe('$http', function() {
|
||||
expect(errCallback.calls.mostRecent().args[0]).toEqualMinErr('$http', 'baddata');
|
||||
});
|
||||
|
||||
});
|
||||
it('should not throw an error if JSON is invalid but content-type is not application/json', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('{abcd}', {'Content-Type': 'text/plain'});
|
||||
|
||||
$http.get('/url').then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should not throw an error if JSON is invalid but content-type is not specified', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('{abcd}');
|
||||
|
||||
$http.get('/url').then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should return response unprocessed if JSON is invalid but content-type is not application/json', function() {
|
||||
var response = '{abcd}';
|
||||
$httpBackend.expect('GET', '/url').respond(response, {'Content-Type': 'text/plain'});
|
||||
|
||||
$http.get('/url').then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback.calls.mostRecent().args[0].data).toBe(response);
|
||||
});
|
||||
|
||||
it('should return response unprocessed if JSON is invalid but content-type is not specified', function() {
|
||||
var response = '{abcd}';
|
||||
$httpBackend.expect('GET', '/url').respond(response);
|
||||
|
||||
$http.get('/url').then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback.calls.mostRecent().args[0].data).toBe(response);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should have access to response headers', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(200, 'response', {h1: 'header1'});
|
||||
@@ -1623,6 +1682,17 @@ describe('$http', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
}));
|
||||
|
||||
it('should cache xhrStatus as well', inject(function($rootScope) {
|
||||
doFirstCacheRequest('GET', 201, null);
|
||||
callback.and.callFake(function(response) {
|
||||
expect(response.xhrStatus).toBe('complete');
|
||||
});
|
||||
|
||||
$http({method: 'get', url: '/url', cache: cache}).then(callback);
|
||||
$rootScope.$digest();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
}));
|
||||
|
||||
|
||||
it('should use cache even if second request was made before the first returned', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(201, 'fake-response');
|
||||
@@ -1788,6 +1858,7 @@ describe('$http', function() {
|
||||
function(response) {
|
||||
expect(response.data).toBeUndefined();
|
||||
expect(response.status).toBe(-1);
|
||||
expect(response.xhrStatus).toBe('timeout');
|
||||
expect(response.headers()).toEqual(Object.create(null));
|
||||
expect(response.config.url).toBe('/some');
|
||||
callback();
|
||||
@@ -2273,7 +2344,6 @@ describe('$http param serializers', function() {
|
||||
expect(defSer({someDate: new Date('2014-07-15T17:30:00.000Z')})).toEqual('someDate=2014-07-15T17:30:00.000Z');
|
||||
expect(jqrSer({someDate: new Date('2014-07-15T17:30:00.000Z')})).toEqual('someDate=2014-07-15T17:30:00.000Z');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('default array serialization', function() {
|
||||
@@ -2281,6 +2351,10 @@ describe('$http param serializers', function() {
|
||||
it('should serialize arrays by repeating param name', function() {
|
||||
expect(defSer({a: 'b', foo: ['bar', 'baz']})).toEqual('a=b&foo=bar&foo=baz');
|
||||
});
|
||||
|
||||
it('should NOT serialize functions', function() {
|
||||
expect(defSer({foo: 'foov', bar: function() {}})).toEqual('foo=foov');
|
||||
});
|
||||
});
|
||||
|
||||
describe('jquery array and objects serialization', function() {
|
||||
|
||||
+33
-8
@@ -3568,35 +3568,55 @@ describe('parser', function() {
|
||||
|
||||
it('should watch ES6 object computed property changes', function() {
|
||||
var count = 0;
|
||||
var values = [];
|
||||
var lastValue;
|
||||
|
||||
scope.$watch('{[a]: true}', function(val) {
|
||||
count++;
|
||||
values.push(val);
|
||||
}, true);
|
||||
lastValue = val;
|
||||
});
|
||||
|
||||
scope.$digest();
|
||||
expect(count).toBe(1);
|
||||
expect(values[0]).toEqual({'undefined': true});
|
||||
expect(lastValue).toEqual({'undefined': true});
|
||||
|
||||
scope.$digest();
|
||||
expect(count).toBe(1);
|
||||
expect(values[0]).toEqual({'undefined': true});
|
||||
expect(lastValue).toEqual({'undefined': true});
|
||||
|
||||
scope.a = true;
|
||||
scope.$digest();
|
||||
expect(count).toBe(2);
|
||||
expect(values[1]).toEqual({'true': true});
|
||||
expect(lastValue).toEqual({'true': true});
|
||||
|
||||
scope.a = 'abc';
|
||||
scope.$digest();
|
||||
expect(count).toBe(3);
|
||||
expect(values[2]).toEqual({'abc': true});
|
||||
expect(lastValue).toEqual({'abc': true});
|
||||
|
||||
scope.a = undefined;
|
||||
scope.$digest();
|
||||
expect(count).toBe(4);
|
||||
expect(values[3]).toEqual({'undefined': true});
|
||||
expect(lastValue).toEqual({'undefined': true});
|
||||
});
|
||||
|
||||
it('should not shallow-watch ES6 object computed properties in case of stateful toString', function() {
|
||||
var count = 0;
|
||||
var lastValue;
|
||||
|
||||
scope.$watch('{[a]: true}', function(val) {
|
||||
count++;
|
||||
lastValue = val;
|
||||
});
|
||||
|
||||
scope.a = {toString: function() { return this.b; }};
|
||||
scope.a.b = 1;
|
||||
|
||||
//TODO: would be great if it didn't throw!
|
||||
expect(function() { scope.$apply(); }).toThrowMinErr('$rootScope', 'infdig');
|
||||
expect(lastValue).toEqual({1: true});
|
||||
|
||||
expect(function() { scope.$apply('a.b = 2'); }).toThrowMinErr('$rootScope', 'infdig');
|
||||
expect(lastValue).toEqual({2: true});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3772,6 +3792,11 @@ describe('parser', function() {
|
||||
expect($parse('5 != null').constant).toBe(true);
|
||||
expect($parse('{standard: 4/3, wide: 16/9}').constant).toBe(true);
|
||||
expect($parse('{[standard]: 4/3, wide: 16/9}').constant).toBe(false);
|
||||
expect($parse('{["key"]: 1}').constant).toBe(true);
|
||||
expect($parse('[0].length').constant).toBe(true);
|
||||
expect($parse('[0][0]').constant).toBe(true);
|
||||
expect($parse('{x: 1}.x').constant).toBe(true);
|
||||
expect($parse('{x: 1}["x"]').constant).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not mark any expression involving variables or function calls as constant', inject(function($parse) {
|
||||
|
||||
Vendored
+32
-20
@@ -1343,8 +1343,8 @@ describe('ngMock', function() {
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
expect(callback.calls.argsFor(0)).toEqual([201, 'second', '', '']);
|
||||
expect(callback.calls.argsFor(1)).toEqual([200, 'first', '', '']);
|
||||
expect(callback.calls.argsFor(0)).toEqual([201, 'second', '', '', 'complete']);
|
||||
expect(callback.calls.argsFor(1)).toEqual([200, 'first', '', '', 'complete']);
|
||||
});
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@ describe('ngMock', function() {
|
||||
hb('GET', '/url1', undefined, callback);
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK', 'complete');
|
||||
});
|
||||
|
||||
it('should default status code to 200', function() {
|
||||
@@ -1377,7 +1377,19 @@ describe('ngMock', function() {
|
||||
hb('GET', '/url1', null, callback);
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK', 'complete');
|
||||
});
|
||||
|
||||
it('should default xhrStatus to complete', function() {
|
||||
callback.and.callFake(function(status, response, headers, x, xhrStatus) {
|
||||
expect(xhrStatus).toBe('complete');
|
||||
});
|
||||
|
||||
hb.expect('GET', '/url1').respond('some-data');
|
||||
hb('GET', '/url1', null, callback);
|
||||
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should take function', function() {
|
||||
@@ -1388,7 +1400,7 @@ describe('ngMock', function() {
|
||||
hb('GET', '/some?q=s', 'data', callback, {a: 'b'});
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some?q=s;data;a=b;q=s', 'Connection: keep-alive', 'Moved Permanently');
|
||||
expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some?q=s;data;a=b;q=s', 'Connection: keep-alive', 'Moved Permanently', undefined);
|
||||
});
|
||||
|
||||
it('should decode query parameters in respond() function', function() {
|
||||
@@ -1400,7 +1412,7 @@ describe('ngMock', function() {
|
||||
hb('GET', '/url?query=l%E2%80%A2ng%20string%20w%2F%20spec%5Eal%20char%24&id=1234&orderBy=-name', null, callback);
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;orderBy=-name;query=l•ng string w/ spec^al char$', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;orderBy=-name;query=l•ng string w/ spec^al char$', '', '', undefined);
|
||||
});
|
||||
|
||||
it('should include regex captures in respond() params when keys provided', function() {
|
||||
@@ -1412,7 +1424,7 @@ describe('ngMock', function() {
|
||||
hb('GET', '/1234/article/cool-angular-article', null, callback);
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;name=cool-angular-article', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;name=cool-angular-article', '', '', undefined);
|
||||
});
|
||||
|
||||
it('should default response headers to ""', function() {
|
||||
@@ -1425,8 +1437,8 @@ describe('ngMock', function() {
|
||||
hb.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
expect(callback.calls.argsFor(0)).toEqual([200, 'first', '', '']);
|
||||
expect(callback.calls.argsFor(1)).toEqual([200, 'second', '', '']);
|
||||
expect(callback.calls.argsFor(0)).toEqual([200, 'first', '', '', 'complete']);
|
||||
expect(callback.calls.argsFor(1)).toEqual([200, 'second', '', '', 'complete']);
|
||||
});
|
||||
|
||||
it('should be able to override response of expect definition', function() {
|
||||
@@ -1436,7 +1448,7 @@ describe('ngMock', function() {
|
||||
|
||||
hb('GET', '/url1', null, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete');
|
||||
});
|
||||
|
||||
it('should be able to override response of when definition', function() {
|
||||
@@ -1446,7 +1458,7 @@ describe('ngMock', function() {
|
||||
|
||||
hb('GET', '/url1', null, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete');
|
||||
});
|
||||
|
||||
it('should be able to override response of expect definition with chaining', function() {
|
||||
@@ -1455,7 +1467,7 @@ describe('ngMock', function() {
|
||||
|
||||
hb('GET', '/url1', null, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete');
|
||||
});
|
||||
|
||||
it('should be able to override response of when definition with chaining', function() {
|
||||
@@ -1464,7 +1476,7 @@ describe('ngMock', function() {
|
||||
|
||||
hb('GET', '/url1', null, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1657,7 +1669,7 @@ describe('ngMock', function() {
|
||||
|
||||
canceler(); // simulate promise resolution
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(-1, undefined, '');
|
||||
expect(callback).toHaveBeenCalledWith(-1, undefined, '', undefined, 'timeout');
|
||||
hb.verifyNoOutstandingExpectation();
|
||||
hb.verifyNoOutstandingRequest();
|
||||
});
|
||||
@@ -1669,7 +1681,7 @@ describe('ngMock', function() {
|
||||
hb('GET', '/url1', null, callback, null, 200);
|
||||
$timeout.flush(300);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(-1, undefined, '');
|
||||
expect(callback).toHaveBeenCalledWith(-1, undefined, '', undefined, 'timeout');
|
||||
hb.verifyNoOutstandingExpectation();
|
||||
hb.verifyNoOutstandingRequest();
|
||||
}));
|
||||
@@ -1831,7 +1843,7 @@ describe('ngMock', function() {
|
||||
hb[shortcut]('/foo').respond('bar');
|
||||
hb(method, '/foo', undefined, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', '', 'complete');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1846,7 +1858,7 @@ describe('ngMock', function() {
|
||||
hb[routeShortcut](this, '/route').respond('path');
|
||||
hb(this, '/route', undefined, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete');
|
||||
}
|
||||
);
|
||||
they('should match colon delimited parameters in ' + routeShortcut + ' $prop method', methods,
|
||||
@@ -1854,7 +1866,7 @@ describe('ngMock', function() {
|
||||
hb[routeShortcut](this, '/route/:id/path/:s_id').respond('path');
|
||||
hb(this, '/route/123/path/456', undefined, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete');
|
||||
}
|
||||
);
|
||||
they('should ignore query param when matching in ' + routeShortcut + ' $prop method', methods,
|
||||
@@ -1862,7 +1874,7 @@ describe('ngMock', function() {
|
||||
hb[routeShortcut](this, '/route/:id').respond('path');
|
||||
hb(this, '/route/123?q=str&foo=bar', undefined, callback);
|
||||
hb.flush();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete');
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -2545,7 +2557,7 @@ describe('ngMockE2E', function() {
|
||||
$browser.defer.flush();
|
||||
|
||||
expect(realHttpBackend).not.toHaveBeenCalled();
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'passThrough override', '', '');
|
||||
expect(callback).toHaveBeenCalledOnceWith(200, 'passThrough override', '', '', 'complete');
|
||||
}));
|
||||
|
||||
it('should pass through to an httpBackend that uses the same $browser service', inject(function($browser) {
|
||||
|
||||
@@ -1205,6 +1205,7 @@ describe('basic usage', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
var response = callback.calls.mostRecent().args[0];
|
||||
expect(response.resource).toBe(ccs);
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.config).toBeDefined();
|
||||
});
|
||||
@@ -2101,6 +2102,25 @@ describe('cancelling requests', function() {
|
||||
|
||||
expect(creditCard.$cancelRequest).toBe(noop);
|
||||
});
|
||||
|
||||
it('should not break when calling old `$cancelRequest` after the response arrives', function() {
|
||||
$httpBackend.whenGET('/CreditCard').respond({});
|
||||
|
||||
var CreditCard = $resource('/CreditCard', {}, {
|
||||
get: {
|
||||
method: 'GET',
|
||||
cancellable: true
|
||||
}
|
||||
});
|
||||
|
||||
var creditCard = CreditCard.get();
|
||||
var cancelRequest = creditCard.$cancelRequest;
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(cancelRequest).not.toBe(noop);
|
||||
expect(cancelRequest).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('configuring `cancellable` on the provider', function() {
|
||||
|
||||
@@ -191,7 +191,7 @@ describe('ngView', function() {
|
||||
it('should use inline content route changes', function() {
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/foo', {template: '<div>{{1+3}}</div>'});
|
||||
$routeProvider.when('/bar', {template: 'angular is da best'});
|
||||
$routeProvider.when('/bar', {template: 'AngularJS is da best'});
|
||||
$routeProvider.when('/blank', {template: ''});
|
||||
});
|
||||
|
||||
@@ -204,7 +204,7 @@ describe('ngView', function() {
|
||||
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('angular is da best');
|
||||
expect(element.text()).toEqual('AngularJS is da best');
|
||||
|
||||
$location.path('/blank');
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -265,18 +265,21 @@ describe('ngClick (touch)', function() {
|
||||
expect($rootScope.tapped).toBe(true);
|
||||
}));
|
||||
|
||||
it('should click when target element is an SVG', inject(
|
||||
function($rootScope, $compile, $rootElement) {
|
||||
element = $compile('<svg ng-click="tapped = true"></svg>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
$rootScope.$digest();
|
||||
if (!/\bEdge\//.test(window.navigator.userAgent)) {
|
||||
// Edge cannot blur svg elements
|
||||
it('should click when target element is an SVG', inject(
|
||||
function($rootScope, $compile, $rootElement) {
|
||||
element = $compile('<svg ng-click="tapped = true"></svg>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
$rootScope.$digest();
|
||||
|
||||
browserTrigger(element, 'touchstart');
|
||||
browserTrigger(element, 'touchend');
|
||||
browserTrigger(element, 'click', {x:1, y:1});
|
||||
browserTrigger(element, 'touchstart');
|
||||
browserTrigger(element, 'touchend');
|
||||
browserTrigger(element, 'click', {x:1, y:1});
|
||||
|
||||
expect($rootScope.tapped).toEqual(true);
|
||||
}));
|
||||
expect($rootScope.tapped).toEqual(true);
|
||||
}));
|
||||
}
|
||||
|
||||
describe('the clickbuster', function() {
|
||||
var element1, element2;
|
||||
|
||||
@@ -1093,7 +1093,7 @@ colors@1.0.x:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||
|
||||
colors@^1.1.0, colors@~1.1.2:
|
||||
colors@^1.1.0, colors@^1.1.2, colors@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
|
||||
|
||||
@@ -1166,6 +1166,16 @@ commitizen@^2.3.0:
|
||||
shelljs "0.7.5"
|
||||
strip-json-comments "2.0.1"
|
||||
|
||||
commitplease@^2.7.10:
|
||||
version "2.7.10"
|
||||
resolved "https://registry.yarnpkg.com/commitplease/-/commitplease-2.7.10.tgz#129af5abb365b46f25e652020c5d1548c947f163"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
git-tools "^0.2.1"
|
||||
ini "^1.3.4"
|
||||
object-assign "^4.1.0"
|
||||
semver "^5.1.0"
|
||||
|
||||
commondir@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-0.0.1.tgz#89f00fdcd51b519c578733fec563e6a6da7f5be2"
|
||||
@@ -2475,6 +2485,12 @@ getpass@^0.1.1:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
git-tools@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/git-tools/-/git-tools-0.2.1.tgz#6e1846af2c0e91ab59258b48f9b53c1279b3b273"
|
||||
dependencies:
|
||||
spawnback "~1.0.0"
|
||||
|
||||
glob-base@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
|
||||
@@ -3649,6 +3665,12 @@ karma-script-launcher@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz#cd017c4de5ef09e5a9da793276176108dd4b542d"
|
||||
|
||||
karma-spec-reporter@^0.0.31:
|
||||
version "0.0.31"
|
||||
resolved "https://registry.yarnpkg.com/karma-spec-reporter/-/karma-spec-reporter-0.0.31.tgz#4830dc7148a155c7d7a186e632339a0d80fadec3"
|
||||
dependencies:
|
||||
colors "^1.1.2"
|
||||
|
||||
karma@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.0.tgz#6f7a1a406446fa2e187ec95398698f4cee476269"
|
||||
@@ -5906,6 +5928,10 @@ spawn-sync@^1.0.15:
|
||||
concat-stream "^1.4.7"
|
||||
os-shim "^0.1.2"
|
||||
|
||||
spawnback@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/spawnback/-/spawnback-1.0.0.tgz#f73662f7e54d95367eca74d6426c677dd7ea686f"
|
||||
|
||||
spdx-correct@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
|
||||
|
||||
Reference in New Issue
Block a user