Compare commits
143 Commits
2.0.0-rc.7
...
2.0.x
Author | SHA1 | Date | |
---|---|---|---|
91f7aa3b15 | |||
58b8091097 | |||
0fde7ecd0f | |||
d25cd244af | |||
e00de0c606 | |||
205103bdaa | |||
0528dcb9dc | |||
6f7ed32154 | |||
1bd8ba80a7 | |||
adb17fed98 | |||
3067ce6eb4 | |||
e102cd4757 | |||
db00ba7ae7 | |||
5a8f116687 | |||
02a862f8af | |||
7578d8573b | |||
de56e31cf0 | |||
eb85a7709a | |||
c99ef4938f | |||
7395400066 | |||
d985cc0253 | |||
aca117ac56 | |||
2c3825f2c8 | |||
ea6defcf11 | |||
975aca95bb | |||
5cb78566c2 | |||
9bacb32ec5 | |||
6970991546 | |||
bd012efcc4 | |||
2dd399658d | |||
d0dea574cb | |||
f7864edd4b | |||
9cc0a4ed10 | |||
3ee8c75eff | |||
b39d3a173e | |||
a4af1561b7 | |||
0851238e78 | |||
d2d98dad61 | |||
826c98e50c | |||
830e6352dd | |||
5911c3bd43 | |||
51e1994af0 | |||
faf5d90bf6 | |||
7e5413da89 | |||
e82a78e641 | |||
c13e55c8c3 | |||
a153504212 | |||
b8a75818ee | |||
f633826e99 | |||
0a8887240a | |||
6d606dd02b | |||
5c4215ccb4 | |||
85489a166e | |||
cf750e17ed | |||
712d1a7c37 | |||
16601f9359 | |||
b81e2e7a31 | |||
98fac36706 | |||
3e780c032e | |||
e09882180e | |||
0e18c57a17 | |||
51e2b9c073 | |||
f218e240d3 | |||
af6b219f8e | |||
20addf5f9f | |||
2860418a3c | |||
39e251eea7 | |||
d7d716d5db | |||
a95d65241c | |||
fdb22bd185 | |||
51d73d3e4e | |||
bf81b06a28 | |||
0621f07a2c | |||
1225ecfb14 | |||
5509453e72 | |||
70488ed382 | |||
03aedbe54b | |||
8395aab25d | |||
0dc15eb64a | |||
cba885a1fb | |||
fa4723a208 | |||
5bf08b886f | |||
89802316b9 | |||
2300c23332 | |||
fa39965a37 | |||
115f0fa842 | |||
734b8b8c13 | |||
54b41f57be | |||
df4254ae89 | |||
14ee75924b | |||
bd4045b6e7 | |||
255099aa61 | |||
1c24096650 | |||
32aeb1052d | |||
838d4bbf6c | |||
c4114c2f66 | |||
37b8691c8c | |||
93054d4e3d | |||
cfc12c6539 | |||
c0bdd89b5d | |||
d5515473bf | |||
ffe5c49c3e | |||
ae1dd5bfd0 | |||
cb657c4b55 | |||
42f60ca303 | |||
e33037a2f1 | |||
9cee8bcc83 | |||
003294d5df | |||
785292f44f | |||
15c2912527 | |||
096ae7c404 | |||
5972fdc817 | |||
2c42a50fc3 | |||
caa1cd2470 | |||
5fad37df69 | |||
727c2b38a4 | |||
b6287ccc51 | |||
69e8ace884 | |||
85d9db6bc4 | |||
0a2132ef10 | |||
d299ce4bcf | |||
0b9425bbb4 | |||
1a035a0dc7 | |||
84b4338ab5 | |||
b847257b16 | |||
c65d139081 | |||
57f0269491 | |||
4e6c41b3a1 | |||
7105021c41 | |||
f7313db0be | |||
1d2e70e3a4 | |||
21516c32e6 | |||
00a24b63da | |||
e71558ba89 | |||
7ac47acc1c | |||
60e49a7e4b | |||
c71e35cbf5 | |||
1348c65b0c | |||
ff03d87cdd | |||
a2bf334e6e | |||
f8690caa98 | |||
aa713d1dd9 | |||
a2519c6164 |
20
.github/ISSUE_TEMPLATE.md
vendored
20
.github/ISSUE_TEMPLATE.md
vendored
@ -1,3 +1,7 @@
|
||||
<!--
|
||||
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
|
||||
-->
|
||||
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
```
|
||||
[ ] bug report => search github for a similar issue or PR before submitting
|
||||
@ -5,14 +9,18 @@
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
|
||||
**Current behavior**
|
||||
**Current behavior**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Reproduction of the problem**
|
||||
<!-- If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). -->
|
||||
**Minimal reproduction of the problem with instructions**
|
||||
<!--
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example,
|
||||
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
-->
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
<!-- Describe the motivation or the concrete use case -->
|
||||
@ -20,12 +28,12 @@
|
||||
**Please tell us about your environment:**
|
||||
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||
|
||||
* **Angular version:** 2.0.0-rc.X
|
||||
* **Angular version:** 2.0.X
|
||||
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
<!-- All browsers where this could be reproduced -->
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||
|
||||
|
||||
* **Node (for AoT issues):** `node --version` =
|
||||
|
148
.travis.yml
148
.travis.yml
@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '5.4.1'
|
||||
- '6.6.0'
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
@ -20,20 +20,9 @@ cache:
|
||||
directories:
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
# - $HOME/.pub-cache
|
||||
|
||||
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
|
||||
env:
|
||||
global:
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - ARCH=linux-x64
|
||||
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
@ -52,146 +41,11 @@ matrix:
|
||||
- env: "CI_MODE=saucelabs_optional"
|
||||
- env: "CI_MODE=browserstack_optional"
|
||||
|
||||
|
||||
install:
|
||||
- ./scripts/ci-lite/install.sh
|
||||
|
||||
before_script:
|
||||
|
||||
|
||||
script:
|
||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
|
||||
|
||||
#branches:
|
||||
# except:
|
||||
# - g3_v2_0
|
||||
#
|
||||
#cache:
|
||||
# directories:
|
||||
# - $HOME/.pub-cache
|
||||
# - $HOME/.chrome/chromium
|
||||
#
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
#
|
||||
#env:
|
||||
# global:
|
||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
# - CXX=g++-4.8
|
||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - SAUCE_USERNAME=angular-ci
|
||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
# - BROWSER_STACK_USERNAME=angularteam1
|
||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
# - ARCH=linux-x64
|
||||
# - DART_DEV_VERSION=latest
|
||||
# - DART_STABLE_VERSION=latest
|
||||
# - DART_CHANNEL=stable
|
||||
# - DART_VERSION=$DART_STABLE_VERSION
|
||||
# # Token for tsd to increase github rate limit
|
||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# # because those are not visible for pull requests, and those should also be reliable.
|
||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# # (password is in Valentine)
|
||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
# # GITHUB_TOKEN_ANGULAR
|
||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
# matrix:
|
||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
# - MODE=dart
|
||||
# - MODE=dart DART_CHANNEL=dev
|
||||
# - MODE=saucelabs_required
|
||||
# - MODE=browserstack_required
|
||||
# - MODE=saucelabs_optional
|
||||
# - MODE=browserstack_optional
|
||||
# - MODE=dart_ddc
|
||||
# - MODE=js
|
||||
# - MODE=router
|
||||
# - MODE=build_only
|
||||
# - MODE=typescript_next
|
||||
# - MODE=lint
|
||||
#
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - env: "MODE=saucelabs_optional"
|
||||
# - env: "MODE=browserstack_optional"
|
||||
#
|
||||
#addons:
|
||||
# firefox: "38.0"
|
||||
# apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
# packages:
|
||||
# - g++-4.8
|
||||
#
|
||||
#before_install:
|
||||
# - node tools/analytics/build-analytics start ci job
|
||||
# - node tools/analytics/build-analytics start ci before_install
|
||||
# - echo ${TSDRC} > .tsdrc
|
||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
||||
# - export DISPLAY=:99.0
|
||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
||||
# - ./scripts/ci/init_android.sh
|
||||
# - sh -e /etc/init.d/xvfb start
|
||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
# - node tools/analytics/build-analytics success ci before_install
|
||||
#
|
||||
#install:
|
||||
# - node tools/analytics/build-analytics start ci install
|
||||
# # Install version of npm that we are locked against
|
||||
# - npm install -g npm@3.5.3
|
||||
# # Install version of Chromium that we are locked against
|
||||
# - ./scripts/ci/install_chromium.sh
|
||||
# # Install version of Dart based on the matrix build variables
|
||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
# # Print the size of caches to ease debugging
|
||||
# - du -sh ./node_modules || true
|
||||
# # Install npm dependecies
|
||||
# # check-node-modules will exit(1) if we don't need to install
|
||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
# - node tools/analytics/build-analytics success ci install
|
||||
#
|
||||
#before_script:
|
||||
# - node tools/analytics/build-analytics start ci before_script
|
||||
# - mkdir -p $LOGS_DIR
|
||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
||||
# - node tools/analytics/build-analytics success ci before_script
|
||||
#
|
||||
#script:
|
||||
# - node tools/analytics/build-analytics start ci script
|
||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
||||
# - node tools/analytics/build-analytics success ci script
|
||||
#
|
||||
#after_script:
|
||||
# - node tools/analytics/build-analytics start ci after_script
|
||||
# - ./scripts/ci/print-logs.sh
|
||||
# - ./scripts/ci/after-script.sh
|
||||
# - ./scripts/publish/publish-build-artifacts.sh
|
||||
# - node tools/analytics/build-analytics success ci after_script
|
||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
||||
#
|
||||
#notifications:
|
||||
# webhooks:
|
||||
# urls:
|
||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
||||
# - https://buildtimetrend.herokuapp.com/travis
|
||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
||||
# on_success: always # options: [always|never|change] default: always
|
||||
# on_failure: always # options: [always|never|change] default: always
|
||||
# on_start: never # default: never
|
||||
# slack:
|
||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
53
CHANGELOG.md
53
CHANGELOG.md
@ -1,3 +1,56 @@
|
||||
<a name="2.0.2"></a>
|
||||
## [2.0.2](https://github.com/angular/angular/compare/2.0.1...2.0.2) (2016-10-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** correctly removes styles on IE ([#11953](https://github.com/angular/angular/pull/11953)), closes [#7916](https://github.com/angular/angular/issues/7916)
|
||||
* **compiler:** do not embed templateUrl in view factories in non-debug mode. ([#11818](https://github.com/angular/angular/issues/11818)) ([51e1994](https://github.com/angular/angular/commit/51e1994)), closes [#11117](https://github.com/angular/angular/issues/11117)
|
||||
* **compiler:** move detection of unsafe properties for binding to ElementSchemaRegistry ([#11378](https://github.com/angular/angular/issues/11378)) ([5911c3b](https://github.com/angular/angular/commit/5911c3b))
|
||||
* **forms:** properly validate empty strings with patterns ([#11450](https://github.com/angular/angular/issues/11450)) ([e00de0c](https://github.com/angular/angular/commit/e00de0c))
|
||||
* **http:** preserve case of the first init, `set()` or `append()` ([#12023](https://github.com/angular/angular/issues/12023)) ([adb17fe](https://github.com/angular/angular/commit/adb17fe)), closes [#11624](https://github.com/angular/angular/issues/11624)
|
||||
* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/commit/826c98e))
|
||||
* **router:** do not reset the router state when updating the component ([#11867](https://github.com/angular/angular/issues/11867)) ([cf750e1](https://github.com/angular/angular/commit/cf750e1))
|
||||
* **compiler:** fix `:host(tag)` and `:host-context(tag)` ([a6bb84e0](https://github.com/angular/angular/commit/a6bb84e02b7579f8d957ef6ba5b10d83482ed756)), closes [#11972](https://github.com/angular/angular/issues/11972)
|
||||
* **compiler:** fix attribute selectors in :host and :host-context ([#12056](https://github.com/angular/angular/issues/12056)) ([6f7ed32](https://github.com/angular/angular/commit/6f7ed32)), closes [#11917](https://github.com/angular/angular/issues/11917)
|
||||
* **compiler:** support `[@page](https://github.com/page)` and `[@document](https://github.com/document)` CSS rules ([#11878](https://github.com/angular/angular/issues/11878)) ([c99ef49](https://github.com/angular/angular/commit/c99ef49)), closes [#11860](https://github.com/angular/angular/issues/11860)
|
||||
* **compiler:** support `[attr="value with space"]` ([bd012ef](https://github.com/angular/angular/commit/bd012ef)), closes [#6249](https://github.com/angular/angular/issues/6249)
|
||||
* **compiler:** support quoted attribute values ([7395400](https://github.com/angular/angular/commit/7395400)), closes [#6085](https://github.com/angular/angular/issues/6085)
|
||||
* **upgrade:** bind optional properties when upgrading from ng1 ([#11411](https://github.com/angular/angular/issues/11411)) ([0851238](https://github.com/angular/angular/commit/0851238)), closes [#10181](https://github.com/angular/angular/issues/10181)
|
||||
* **http:** change a behavior when a param value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
|
||||
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
|
||||
|
||||
|
||||
<a name="2.0.1"></a>
|
||||
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759))
|
||||
* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644)
|
||||
* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652))
|
||||
* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590)
|
||||
* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643)
|
||||
* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#11645](https://github.com/angular/angular/issues/11645)
|
||||
* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418))
|
||||
* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719)
|
||||
* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e))
|
||||
* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280)
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **platform-browser:** provide Title service as part of the module ([#11605](https://github.com/angular/angular/issues/11605)) ([85d9db6](https://github.com/angular/angular/commit/85d9db6)), closes [#11600](https://github.com/angular/angular/issues/11600)
|
||||
* **upgrade:** correct the main entry path in package.json ([a2519c6](https://github.com/angular/angular/commit/a2519c6))
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-rc.7"></a>
|
||||
# [2.0.0-rc.7](https://github.com/angular/angular/compare/2.0.0-rc.6...2.0.0-rc.7) (2016-09-13)
|
||||
|
||||
|
@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
## <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
If you find a bug in the source code, you can help us by
|
||||
@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by
|
||||
## <a name="feature"></a> Want a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
||||
and we are not ready to accept major contributions ahead of the full release.
|
||||
a proposal for your work first, to be sure that we can use it.
|
||||
Please consider what kind of change it is:
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
|
@ -114,7 +114,7 @@ You should execute the 3 test suites before submitting a PR to github.
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||
|
||||
- CircleCI fails if your code is not formatted properly,
|
||||
- Travis CI fails if any of the test suite describe above fails.
|
||||
- Travis CI fails if any of the test suites described above fails.
|
||||
|
||||
## Update the public API tests
|
||||
|
||||
|
@ -4,10 +4,9 @@
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][https://www.browserstack.com/].*
|
||||
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||
|
||||
Angular
|
||||
=========
|
||||
@ -17,7 +16,6 @@ repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
||||
|
||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||
|
||||
Angular 2 is currently in **Release Candidate**.
|
||||
|
||||
## Quickstart
|
||||
|
||||
@ -29,10 +27,9 @@ Angular 2 is currently in **Release Candidate**.
|
||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||
|
||||
|
||||
[browserstack]: https://www.browserstack.com/
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[dartium]: http://www.dartlang.org/tools/dartium
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
|
@ -17,9 +17,9 @@ with it.
|
||||
|
||||
* `comp: animations`: `@matsko`
|
||||
* `comp: benchpress`: `@tbosch`
|
||||
* `comp: build/ci`: `@IgorMinar` -- All build and CI scripts
|
||||
* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts
|
||||
* `comp: common`: `@mhevery` -- This includes core components / pipes.
|
||||
* `comp: core/compiler`: `@tbosch` -- Because core and compiler are very
|
||||
* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very
|
||||
intertwined, we will be treating them as one.
|
||||
* `comp: forms`: `@kara`
|
||||
* `comp: http`: `@jeffbcross`
|
||||
@ -29,14 +29,14 @@ with it.
|
||||
* `comp: testing`: `@juliemr`
|
||||
* `comp: upgrade`: `@mhevery`
|
||||
* `comp: web-worker`: `@vicb`
|
||||
* `comp: zone`: `@mhevery`
|
||||
* `comp: zones`: `@mhevery`
|
||||
|
||||
There are few components which are cross-cutting. They don't have
|
||||
a clear location in the source tree. We will treat them as a component
|
||||
even thought no specific source tree is associated with them.
|
||||
|
||||
* `comp: documentation`: `@naomiblack`
|
||||
* `comp: packaging`: `@mhevery`
|
||||
* `comp: packaging`: `@IgorMinar`
|
||||
* `comp: performance`: `@tbosch`
|
||||
* `comp: security`: `@IgorMinar`
|
||||
|
||||
|
@ -1,216 +1,98 @@
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL)
|
||||
// and BrowserStack (BS).
|
||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented
|
||||
// out in Travis configuration.
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Chrome': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
'ChromeBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': {unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||
'ChromeDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.1': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] },
|
||||
'ChromeNoSandbox': {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox'] },
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '52'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_CHROMEDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'dev'
|
||||
},
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '46'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_FIREFOXDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 'dev'
|
||||
},
|
||||
'SL_SAFARI7': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7.0'
|
||||
},
|
||||
'SL_SAFARI8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8.0'
|
||||
},
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
version: '9.0'
|
||||
},
|
||||
'SL_IOS7': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '7.1'
|
||||
},
|
||||
'SL_IOS8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8.4'
|
||||
},
|
||||
'SL_IOS9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '9.3'
|
||||
},
|
||||
'SL_IE9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2008',
|
||||
version: '9'
|
||||
},
|
||||
'DartiumWithWebPlatform':
|
||||
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
|
||||
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
|
||||
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
|
||||
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
|
||||
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
|
||||
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
|
||||
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
|
||||
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
|
||||
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
|
||||
'SL_SAFARI8': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10', version: '8.0'},
|
||||
'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'},
|
||||
'SL_SAFARI10':
|
||||
{base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'},
|
||||
'SL_IOS7': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '7.1'},
|
||||
'SL_IOS8': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'},
|
||||
'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
|
||||
'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
|
||||
'SL_IE9':
|
||||
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
|
||||
'SL_IE10': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2012',
|
||||
version: '10'
|
||||
},
|
||||
'SL_IE11': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
'SL_IE11':
|
||||
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'MicrosoftEdge',
|
||||
platform: 'Windows 10',
|
||||
version: '13.10586'
|
||||
},
|
||||
'SL_ANDROID4.1': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.1'
|
||||
},
|
||||
'SL_ANDROID4.2': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.2'
|
||||
},
|
||||
'SL_ANDROID4.3': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.3'
|
||||
},
|
||||
'SL_ANDROID4.4': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.4'
|
||||
},
|
||||
'SL_ANDROID5': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '5.1'
|
||||
},
|
||||
'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'},
|
||||
'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'},
|
||||
'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'},
|
||||
'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
|
||||
'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'},
|
||||
|
||||
'BS_CHROME': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'OS X',
|
||||
os_version: 'Yosemite'
|
||||
},
|
||||
'BS_FIREFOX': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_SAFARI7': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Mavericks'
|
||||
},
|
||||
'BS_SAFARI8': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Yosemite'
|
||||
},
|
||||
'BS_SAFARI9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'El Capitan'
|
||||
},
|
||||
'BS_IOS7': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 5S',
|
||||
os: 'ios',
|
||||
os_version: '7.0'
|
||||
},
|
||||
'BS_IOS8': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6',
|
||||
os: 'ios',
|
||||
os_version: '8.3'
|
||||
},
|
||||
'BS_IOS9': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6S',
|
||||
os: 'ios',
|
||||
os_version: '9.1'
|
||||
},
|
||||
'BS_IE9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '9.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
|
||||
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
|
||||
'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'},
|
||||
'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'},
|
||||
'BS_SAFARI9': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'El Capitan'},
|
||||
'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'},
|
||||
'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'},
|
||||
'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'},
|
||||
'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'},
|
||||
'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
|
||||
'BS_IE9':
|
||||
{base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
|
||||
'BS_IE10': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
@ -225,58 +107,35 @@ var customLaunchers = {
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_EDGE': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'edge',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_WINDOWSPHONE' : {
|
||||
base: 'BrowserStack',
|
||||
device: 'Nokia Lumia 930',
|
||||
os: 'winphone',
|
||||
os_version: '8.1'
|
||||
},
|
||||
'BS_ANDROID5': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 5',
|
||||
os: 'android',
|
||||
os_version: '5.0'
|
||||
},
|
||||
'BS_ANDROID4.4': {
|
||||
base: 'BrowserStack',
|
||||
device: 'HTC One M8',
|
||||
os: 'android',
|
||||
os_version: '4.4'
|
||||
},
|
||||
'BS_ANDROID4.3': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Samsung Galaxy S4',
|
||||
os: 'android',
|
||||
os_version: '4.3'
|
||||
},
|
||||
'BS_ANDROID4.2': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 4',
|
||||
os: 'android',
|
||||
os_version: '4.2'
|
||||
},
|
||||
'BS_ANDROID4.1': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 7',
|
||||
os: 'android',
|
||||
os_version: '4.1'
|
||||
}
|
||||
'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
|
||||
'BS_WINDOWSPHONE':
|
||||
{base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'},
|
||||
'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'},
|
||||
'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'},
|
||||
'BS_ANDROID4.3':
|
||||
{base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'},
|
||||
'BS_ANDROID4.2':
|
||||
{base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'},
|
||||
'BS_ANDROID4.1':
|
||||
{base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.1'}
|
||||
};
|
||||
|
||||
var sauceAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||
return customLaunchers[item].base == 'SauceLabs';
|
||||
}),
|
||||
'DESKTOP': [
|
||||
'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7',
|
||||
'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'
|
||||
],
|
||||
'MOBILE': [
|
||||
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7',
|
||||
'SL_IOS8', 'SL_IOS9', 'SL_IOS10'
|
||||
],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
@ -284,13 +143,20 @@ var sauceAliases = {
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
||||
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||
return customLaunchers[item].base == 'BrowserStack';
|
||||
}),
|
||||
'DESKTOP': [
|
||||
'BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7',
|
||||
'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'
|
||||
],
|
||||
'MOBILE': [
|
||||
'BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'
|
||||
],
|
||||
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
@ -303,11 +169,9 @@ module.exports = {
|
||||
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => target + '_' + item.toUpperCase());
|
||||
}
|
||||
|
164
build.sh
164
build.sh
@ -4,74 +4,97 @@ set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
PACKAGES=(core
|
||||
compiler
|
||||
common
|
||||
forms
|
||||
platform-browser
|
||||
platform-browser-dynamic
|
||||
platform-server
|
||||
platform-webworker
|
||||
platform-webworker-dynamic
|
||||
http
|
||||
router
|
||||
upgrade
|
||||
compiler-cli
|
||||
benchpress)
|
||||
BUILD_ALL=true
|
||||
BUNDLE=true
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
--packages=*)
|
||||
PACKAGES_STR=${ARG#--packages=}
|
||||
PACKAGES=( ${PACKAGES_STR//,/ } )
|
||||
BUILD_ALL=false
|
||||
;;
|
||||
--bundle=*)
|
||||
BUNDLE=( "${ARG#--bundle=}" )
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $ARG."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
|
||||
|
||||
rm -rf ./dist/all/
|
||||
mkdir -p ./dist/all/
|
||||
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
|
||||
echo "====== Copying files needed for e2e tests ====="
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
echo "====== Copying files needed for benchmarks ====="
|
||||
cp -r ./modules/benchmarks ./dist/all/
|
||||
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
||||
mkdir ./dist/all/benchmarks/vendor
|
||||
cd ./dist/all/benchmarks/vendor
|
||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
ln -s ../../../../bower_components/polymer .
|
||||
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
|
||||
cd -
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||
$TSC -p modules/tsconfig.json
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
echo "====== (tools)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
rm -rf ./dist/tools/
|
||||
mkdir -p ./dist/tools/
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
|
||||
for PACKAGE in \
|
||||
core \
|
||||
compiler \
|
||||
common \
|
||||
forms \
|
||||
platform-browser \
|
||||
platform-browser-dynamic \
|
||||
platform-server \
|
||||
platform-webworker \
|
||||
platform-webworker-dynamic \
|
||||
http \
|
||||
router \
|
||||
upgrade \
|
||||
compiler-cli \
|
||||
benchpress
|
||||
if [[ ${BUILD_ALL} == true ]]; then
|
||||
rm -rf ./dist/all/
|
||||
mkdir -p ./dist/all/
|
||||
|
||||
echo "====== Copying files needed for e2e tests ====="
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
echo "====== Copying files needed for benchmarks ====="
|
||||
cp -r ./modules/benchmarks ./dist/all/
|
||||
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
||||
mkdir ./dist/all/benchmarks/vendor
|
||||
cd ./dist/all/benchmarks/vendor
|
||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
ln -s ../../../../bower_components/polymer .
|
||||
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
|
||||
cd -
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
$TSC -p modules/tsconfig.json
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
fi
|
||||
|
||||
for PACKAGE in ${PACKAGES[@]}
|
||||
do
|
||||
PWD=`pwd`
|
||||
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
||||
@ -81,10 +104,13 @@ do
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig.json ====="
|
||||
rm -rf ${DESTDIR}
|
||||
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||
|
||||
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
|
||||
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
|
||||
@ -103,7 +129,12 @@ do
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
fi
|
||||
|
||||
if [[ ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
||||
if [[ ${PACKAGE} == benchpress ]]; then
|
||||
cp ${SRCDIR}/*.md ${DESTDIR}
|
||||
cp -r ${SRCDIR}/docs ${DESTDIR}
|
||||
fi
|
||||
|
||||
if [[ ${BUNDLE} == true && ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
||||
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
mkdir ${DESTDIR}/bundles
|
||||
@ -131,5 +162,4 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
# TODO(i): uncomment once the protractor typings issue is resolved
|
||||
#./modules/@angular/examples/build.sh
|
||||
./modules/@angular/examples/build.sh
|
||||
|
192
gulpfile.js
192
gulpfile.js
@ -1,31 +1,51 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||
require('./tools/check-environment')(
|
||||
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
|
||||
|
||||
require('./tools/check-environment')({
|
||||
requiredNpmVersion: '>=3.5.3 <4.0.0',
|
||||
requiredNodeVersion: '>=5.4.1 <7.0.0',
|
||||
});
|
||||
|
||||
const gulp = require('gulp');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const srcsToFmt =
|
||||
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
|
||||
'modules/playground/**/*.ts', 'modules/benchmarks/**/*.ts', 'modules/e2e_util/**/*.ts'];
|
||||
// clang-format entry points
|
||||
const srcsToFmt = [
|
||||
'modules/@angular/**/*.{js,ts}',
|
||||
'modules/benchmarks/**/*.{js,ts}',
|
||||
'modules/e2e_util/**/*.{js,ts}',
|
||||
'modules/playground/**/*.{js,ts}',
|
||||
'tools/**/*.{js,ts}',
|
||||
'!tools/public_api_guard/**/*.d.ts',
|
||||
'./*.{js,ts}',
|
||||
'!shims_for_IE.js',
|
||||
];
|
||||
|
||||
// Check source code for formatting errors (clang-format)
|
||||
gulp.task('format:enforce', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
return gulp.src(srcsToFmt).pipe(
|
||||
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
||||
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
||||
});
|
||||
|
||||
// Format the source code with clang-format (see .clang-format)
|
||||
gulp.task('format', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
return gulp.src(srcsToFmt, { base: '.' }).pipe(
|
||||
format.format('file', clangFormat)).pipe(gulp.dest('.'));
|
||||
return gulp.src(srcsToFmt, {base: '.'})
|
||||
.pipe(format.format('file', clangFormat))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
const entrypoints = [
|
||||
@ -49,78 +69,110 @@ const entrypoints = [
|
||||
'dist/packages-dist/http/index.d.ts',
|
||||
'dist/packages-dist/http/testing/index.d.ts',
|
||||
'dist/packages-dist/forms/index.d.ts',
|
||||
'dist/packages-dist/router/index.d.ts'
|
||||
'dist/packages-dist/router/index.d.ts',
|
||||
];
|
||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||
const publicApiArgs = [
|
||||
'--rootDir', 'dist/packages-dist',
|
||||
'--stripExportPattern', '^__',
|
||||
'--allowModuleIdentifiers', 'jasmine',
|
||||
'--allowModuleIdentifiers', 'protractor',
|
||||
'--allowModuleIdentifiers', 'angular',
|
||||
'--onStabilityMissing', 'error'
|
||||
'--rootDir',
|
||||
'dist/packages-dist',
|
||||
'--stripExportPattern',
|
||||
'^__',
|
||||
'--allowModuleIdentifiers',
|
||||
'jasmine',
|
||||
'--allowModuleIdentifiers',
|
||||
'protractor',
|
||||
'--allowModuleIdentifiers',
|
||||
'angular',
|
||||
'--onStabilityMissing',
|
||||
'error',
|
||||
].concat(entrypoints);
|
||||
|
||||
// Build angular
|
||||
gulp.task('build.sh', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess.exec(path.join(__dirname, 'build.sh'), error => done(error));
|
||||
childProcess.exec(path.join(__dirname, 'build.sh'), done);
|
||||
});
|
||||
|
||||
// Enforce that the public API matches the golden files
|
||||
// Note that these two commands work on built d.ts files instead of the source
|
||||
gulp.task('public-api:enforce', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Generate the public API golden files
|
||||
gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
});
|
||||
|
||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
|
||||
// focused tests is found.
|
||||
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
|
||||
// tests in our code base.
|
||||
gulp.task('check-tests', function() {
|
||||
const ddescribeIit = require('gulp-ddescribe-iit');
|
||||
return gulp
|
||||
.src([
|
||||
'modules/**/*.spec.ts',
|
||||
'modules/**/*_spec.ts',
|
||||
])
|
||||
.pipe(ddescribeIit({allowDisabledTests: true}));
|
||||
});
|
||||
|
||||
// Check the coding standards and programming errors
|
||||
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||
const tslint = require('gulp-tslint');
|
||||
// Built-in rules are at
|
||||
// https://github.com/palantir/tslint#supported-rules
|
||||
const tslintConfig = require('./tslint.json');
|
||||
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
rulesDirectory: 'dist/tools/tslint',
|
||||
formatter: 'prose'
|
||||
}))
|
||||
.pipe(tslint.report({emitError: true}));
|
||||
return gulp
|
||||
.src([
|
||||
// todo(vicb): add .js files when supported
|
||||
// see https://github.com/palantir/tslint/pull/1515
|
||||
'modules/@angular/**/*.ts',
|
||||
'modules/benchpress/**/*.ts',
|
||||
'./*.ts',
|
||||
])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
rulesDirectory: 'dist/tools/tslint',
|
||||
formatter: 'prose',
|
||||
}))
|
||||
.pipe(tslint.report({emitError: true}));
|
||||
});
|
||||
|
||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||
|
||||
// Check for circular dependency in the source code
|
||||
gulp.task('check-cycle', (done) => {
|
||||
const madge = require('madge');
|
||||
|
||||
var dependencyObject = madge(['dist/all/'], {
|
||||
const dependencyObject = madge(['dist/all/'], {
|
||||
format: 'cjs',
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
|
||||
});
|
||||
var circularDependencies = dependencyObject.circular().getArray();
|
||||
const circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
console.log('Found circular dependencies!');
|
||||
console.log(circularDependencies);
|
||||
@ -129,47 +181,47 @@ gulp.task('check-cycle', (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
// Serve the built files
|
||||
gulp.task('serve', () => {
|
||||
let connect = require('gulp-connect');
|
||||
let cors = require('cors');
|
||||
const connect = require('gulp-connect');
|
||||
const cors = require('cors');
|
||||
|
||||
connect.server({
|
||||
root: `${__dirname}/dist`,
|
||||
port: 8000,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()]
|
||||
middleware: (connect, opt) => [cors()],
|
||||
});
|
||||
});
|
||||
|
||||
// Serve the examples
|
||||
gulp.task('serve-examples', () => {
|
||||
let connect = require('gulp-connect');
|
||||
let cors = require('cors');
|
||||
const connect = require('gulp-connect');
|
||||
const cors = require('cors');
|
||||
|
||||
connect.server({
|
||||
root: `${__dirname}/dist/examples`,
|
||||
port: 8001,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()]
|
||||
root: `${__dirname}/dist/examples`,
|
||||
port: 8001,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Update the changelog with the latest changes
|
||||
gulp.task('changelog', () => {
|
||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 1
|
||||
}, {
|
||||
// Conventional Changelog Context
|
||||
// We have to manually set version number so it doesn't get prefixed with `v`
|
||||
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
.pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
|
||||
// Conventional Changelog Context
|
||||
// We have to manually set version number so it doesn't get prefixed with `v`
|
||||
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
|
||||
function tsc(projectPath, done) {
|
||||
@ -177,8 +229,12 @@ function tsc(projectPath, done) {
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.normalize(`${__dirname}/node_modules/.bin/tsc`) + (/^win/.test(os.platform()) ? '.cmd' : ''),
|
||||
['-p', path.join(__dirname, projectPath)],
|
||||
{stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
||||
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
}
|
||||
|
||||
// returns the script path for the current platform
|
||||
function platformScriptPath(path) {
|
||||
return /^win/.test(os.platform()) ? `${path}.cmd` : path;
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
var internalAngularReporter = require('./tools/karma/reporter.js');
|
||||
|
||||
@ -17,24 +25,25 @@ module.exports = function(config) {
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/proxy.js',
|
||||
'node_modules/zone.js/dist/sync-test.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/fake-async-test.js',
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
||||
'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js',
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false}, {
|
||||
pattern: 'modules/@angular/platform-browser/test/static_assets/**',
|
||||
included: false,
|
||||
watched: false
|
||||
},
|
||||
{
|
||||
pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**',
|
||||
included: false,
|
||||
watched: false,
|
||||
}
|
||||
],
|
||||
|
||||
exclude: [
|
||||
@ -44,7 +53,7 @@ module.exports = function(config) {
|
||||
'dist/all/@angular/benchpress/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/examples/**/e2e_test/**'
|
||||
'dist/examples/**/e2e_test/**',
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
@ -55,11 +64,11 @@ module.exports = function(config) {
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
internalAngularReporter
|
||||
internalAngularReporter,
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'**/*.js': ['sourcemap']
|
||||
'**/*.js': ['sourcemap'],
|
||||
},
|
||||
|
||||
reporters: ['internal-angular'],
|
||||
@ -73,7 +82,7 @@ module.exports = function(config) {
|
||||
'selenium-version': '2.53.0',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
'max-duration': 5400,
|
||||
}
|
||||
},
|
||||
|
||||
@ -82,20 +91,21 @@ module.exports = function(config) {
|
||||
startTunnel: false,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
pollingTimeout: 10000,
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
browserDisconnectTimeout: 60000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserNoActivityTimeout: 60000,
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
var buildId =
|
||||
'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
@ -1,15 +0,0 @@
|
||||
Angular2
|
||||
=========
|
||||
|
||||
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo. This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you should go to [angular/angular.js](https://github.com/angular/angular.js)
|
||||
|
||||
This package contains different sources for different users:
|
||||
|
||||
1. The files located in the root folder can be consumed using CommonJS.
|
||||
2. The files under `/es6` are es6 compatible files that can be transpiled to
|
||||
es5 using any transpiler. This contains:
|
||||
* `dev/`: a development version that includes runtime type assertions
|
||||
* `prod/`: a production version that does not include runtime type assertions
|
||||
3. The files under `/ts` are the TypeScript source files.
|
||||
|
||||
License: Apache MIT 2.0
|
6
modules/@angular/README.md
Normal file
6
modules/@angular/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
Angular
|
||||
=======
|
||||
|
||||
The sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||
|
||||
License: MIT
|
@ -1,15 +1,13 @@
|
||||
{
|
||||
"name": "@angular/benchpress",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"version": "0.1.0",
|
||||
"description": "Benchpress - a framework for e2e performance tests",
|
||||
"main": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
"dependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER",
|
||||
"@angular/core": "^2.0.0-rc.7",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"rxjs": "5.0.0-beta.12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"rxjs": "5.0.0-beta.12",
|
||||
"jpm": "1.1.4",
|
||||
"firefox-profile": "0.4.0",
|
||||
"selenium-webdriver": "^2.53.3"
|
||||
|
16
modules/@angular/benchpress/publish.sh
Executable file
16
modules/@angular/benchpress/publish.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cd $(dirname $0)/../../..
|
||||
ROOTDIR=$(pwd)
|
||||
SRCDIR=${ROOTDIR}/modules/@angular/benchpress
|
||||
DESTDIR=${ROOTDIR}/dist/packages-dist/benchpress
|
||||
|
||||
rm -fr ${DESTDIR}
|
||||
|
||||
echo "====== BUILDING... ====="
|
||||
./build.sh --packages=core,benchpress --bundle=false
|
||||
|
||||
echo "====== PUBLISHING: ${DESTDIR} ====="
|
||||
npm publish ${DESTDIR} --access public
|
@ -9,8 +9,6 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {DateWrapper} from './facade/lang';
|
||||
|
||||
export class Options {
|
||||
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
|
||||
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
|
||||
@ -34,7 +32,7 @@ export class Options {
|
||||
{provide: Options.FORCE_GC, useValue: false},
|
||||
{provide: Options.PREPARE, useValue: Options.NO_PREPARE},
|
||||
{provide: Options.MICRO_METRICS, useValue: {}}, {provide: Options.USER_METRICS, useValue: {}},
|
||||
{provide: Options.NOW, useValue: () => DateWrapper.now()},
|
||||
{provide: Options.NOW, useValue: () => new Date()},
|
||||
{provide: Options.RECEIVED_DATA, useValue: false},
|
||||
{provide: Options.REQUEST_COUNT, useValue: false},
|
||||
{provide: Options.CAPTURE_FRAMES, useValue: false},
|
||||
|
@ -42,11 +42,11 @@ class Profiler {
|
||||
}
|
||||
|
||||
addStartEvent(name: string, timeStarted: number) {
|
||||
this._markerEvents.push({ph: 'b', ts: timeStarted - this._profilerStartTime, name: name});
|
||||
this._markerEvents.push({ph: 'B', ts: timeStarted - this._profilerStartTime, name: name});
|
||||
}
|
||||
|
||||
addEndEvent(name: string, timeEnded: number) {
|
||||
this._markerEvents.push({ph: 'e', ts: timeEnded - this._profilerStartTime, name: name});
|
||||
this._markerEvents.push({ph: 'E', ts: timeEnded - this._profilerStartTime, name: name});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,18 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Map} from './facade/collection';
|
||||
import {Date, DateWrapper} from './facade/lang';
|
||||
|
||||
export class MeasureValues {
|
||||
constructor(
|
||||
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
||||
'timeStamp': this.timeStamp.toJSON(),
|
||||
'runIndex': this.runIndex,
|
||||
'values': this.values
|
||||
'values': this.values,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Injector, OpaqueToken} from '@angular/core';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
|
||||
import {Metric} from '../metric';
|
||||
|
||||
@ -57,8 +56,7 @@ export class MultiMetric extends Metric {
|
||||
|
||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
||||
var result: {[key: string]: string} = {};
|
||||
maps.forEach(
|
||||
map => { StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); });
|
||||
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {Math, NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {Metric} from '../metric';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
@ -95,8 +93,9 @@ export class PerflogMetric extends Metric {
|
||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||
}
|
||||
}
|
||||
StringMapWrapper.forEach(
|
||||
this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
|
||||
for (let name in this._microMetrics) {
|
||||
res[name] = this._microMetrics[name];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -126,8 +125,8 @@ export class PerflogMetric extends Metric {
|
||||
.then((_) => this._endMeasure(restartMeasure))
|
||||
.then((forceGcMeasureValues) => {
|
||||
this._captureFrames = originalFrameCaptureValue;
|
||||
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
|
||||
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
|
||||
measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime'];
|
||||
measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount'];
|
||||
return measureValues;
|
||||
});
|
||||
});
|
||||
@ -152,7 +151,7 @@ export class PerflogMetric extends Metric {
|
||||
return this._driverExtension.readPerfLog().then((events) => {
|
||||
this._addEvents(events);
|
||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
if (isPresent(result)) {
|
||||
if (result) {
|
||||
this._remainingEvents = events;
|
||||
return result;
|
||||
}
|
||||
@ -166,14 +165,14 @@ export class PerflogMetric extends Metric {
|
||||
private _addEvents(events: PerfLogEvent[]) {
|
||||
var needSort = false;
|
||||
events.forEach(event => {
|
||||
if (StringWrapper.equals(event['ph'], 'X')) {
|
||||
if (event['ph'] === 'X') {
|
||||
needSort = true;
|
||||
var startEvent: PerfLogEvent = {};
|
||||
var endEvent: PerfLogEvent = {};
|
||||
StringMapWrapper.forEach(event, (value, prop) => {
|
||||
(<any>startEvent)[prop] = value;
|
||||
(<any>endEvent)[prop] = value;
|
||||
});
|
||||
for (let prop in event) {
|
||||
startEvent[prop] = event[prop];
|
||||
endEvent[prop] = event[prop];
|
||||
}
|
||||
startEvent['ph'] = 'B';
|
||||
endEvent['ph'] = 'E';
|
||||
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
|
||||
@ -185,7 +184,7 @@ export class PerflogMetric extends Metric {
|
||||
});
|
||||
if (needSort) {
|
||||
// Need to sort because of the ph==='X' events
|
||||
ListWrapper.sort(this._remainingEvents, (a, b) => {
|
||||
this._remainingEvents.sort((a, b) => {
|
||||
var diff = a['ts'] - b['ts'];
|
||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||
});
|
||||
@ -208,7 +207,9 @@ export class PerflogMetric extends Metric {
|
||||
result['frameTime.worst'] = 0;
|
||||
result['frameTime.smooth'] = 0;
|
||||
}
|
||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
|
||||
for (let name in this._microMetrics) {
|
||||
result[name] = 0;
|
||||
}
|
||||
if (this._receivedData) {
|
||||
result['receivedData'] = 0;
|
||||
}
|
||||
@ -218,6 +219,24 @@ export class PerflogMetric extends Metric {
|
||||
|
||||
var markStartEvent: PerfLogEvent = null;
|
||||
var markEndEvent: PerfLogEvent = null;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
if (ph === 'B' && name === markName) {
|
||||
markStartEvent = event;
|
||||
} else if (ph === 'I' && name === 'navigationStart') {
|
||||
// if a benchmark measures reload of a page, use the last
|
||||
// navigationStart as begin event
|
||||
markStartEvent = event;
|
||||
} else if (ph === 'E' && name === markName) {
|
||||
markEndEvent = event;
|
||||
}
|
||||
});
|
||||
if (!markStartEvent || !markEndEvent) {
|
||||
// not all events have been received, no further processing for now
|
||||
return null;
|
||||
}
|
||||
|
||||
var gcTimeInScript = 0;
|
||||
var renderTimeInScript = 0;
|
||||
|
||||
@ -228,120 +247,99 @@ export class PerflogMetric extends Metric {
|
||||
|
||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||
var intervalStartCount: {[key: string]: number} = {};
|
||||
|
||||
var inMeasureRange = false;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
var microIterations = 1;
|
||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||
if (isPresent(microIterationsMatch)) {
|
||||
if (microIterationsMatch) {
|
||||
name = microIterationsMatch[1];
|
||||
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
|
||||
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||
}
|
||||
if (event === markStartEvent) {
|
||||
inMeasureRange = true;
|
||||
} else if (event === markEndEvent) {
|
||||
inMeasureRange = false;
|
||||
}
|
||||
if (!inMeasureRange || event['pid'] !== markStartEvent['pid']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
|
||||
markStartEvent = event;
|
||||
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
|
||||
markEndEvent = event;
|
||||
}
|
||||
|
||||
let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i');
|
||||
if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) {
|
||||
if (this._requestCount && name === 'sendRequest') {
|
||||
result['requestCount'] += 1;
|
||||
} else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) {
|
||||
} else if (this._receivedData && name === 'receivedData' && ph === 'I') {
|
||||
result['receivedData'] += event['args']['encodedDataLength'];
|
||||
} else if (StringWrapper.equals(name, 'navigationStart')) {
|
||||
// We count data + requests since the last navigationStart
|
||||
// (there might be chrome extensions loaded by selenium before our page, so there
|
||||
// will likely be more than one navigationStart).
|
||||
if (this._receivedData) {
|
||||
result['receivedData'] = 0;
|
||||
}
|
||||
if (ph === 'B' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||
if (frameCaptureStartEvent) {
|
||||
throw new Error('can capture frames only once per benchmark run');
|
||||
}
|
||||
if (this._requestCount) {
|
||||
result['requestCount'] = 0;
|
||||
if (!this._captureFrames) {
|
||||
throw new Error(
|
||||
'found start event for frame capture, but frame capture was not requested in benchpress');
|
||||
}
|
||||
frameCaptureStartEvent = event;
|
||||
} else if (ph === 'E' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||
if (!frameCaptureStartEvent) {
|
||||
throw new Error('missing start event for frame capture');
|
||||
}
|
||||
frameCaptureEndEvent = event;
|
||||
}
|
||||
|
||||
if (ph === 'I' && frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') {
|
||||
frameTimestamps.push(event['ts']);
|
||||
if (frameTimestamps.length >= 2) {
|
||||
frameTimes.push(
|
||||
frameTimestamps[frameTimestamps.length - 1] -
|
||||
frameTimestamps[frameTimestamps.length - 2]);
|
||||
}
|
||||
}
|
||||
if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
|
||||
event['pid'] === markStartEvent['pid']) {
|
||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
||||
if (isPresent(frameCaptureStartEvent)) {
|
||||
throw new Error('can capture frames only once per benchmark run');
|
||||
}
|
||||
if (!this._captureFrames) {
|
||||
throw new Error(
|
||||
'found start event for frame capture, but frame capture was not requested in benchpress');
|
||||
}
|
||||
frameCaptureStartEvent = event;
|
||||
} else if (
|
||||
StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
||||
if (isBlank(frameCaptureStartEvent)) {
|
||||
throw new Error('missing start event for frame capture');
|
||||
}
|
||||
frameCaptureEndEvent = event;
|
||||
}
|
||||
|
||||
if (isInstant) {
|
||||
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
|
||||
StringWrapper.equals(name, 'frame')) {
|
||||
frameTimestamps.push(event['ts']);
|
||||
if (frameTimestamps.length >= 2) {
|
||||
frameTimes.push(
|
||||
frameTimestamps[frameTimestamps.length - 1] -
|
||||
frameTimestamps[frameTimestamps.length - 2]);
|
||||
}
|
||||
}
|
||||
if (ph === 'B') {
|
||||
if (!intervalStarts[name]) {
|
||||
intervalStartCount[name] = 1;
|
||||
intervalStarts[name] = event;
|
||||
} else {
|
||||
intervalStartCount[name]++;
|
||||
}
|
||||
|
||||
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
|
||||
if (isBlank(intervalStarts[name])) {
|
||||
intervalStartCount[name] = 1;
|
||||
intervalStarts[name] = event;
|
||||
} else {
|
||||
intervalStartCount[name]++;
|
||||
}
|
||||
} else if (
|
||||
(StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
|
||||
isPresent(intervalStarts[name])) {
|
||||
intervalStartCount[name]--;
|
||||
if (intervalStartCount[name] === 0) {
|
||||
var startEvent = intervalStarts[name];
|
||||
var duration = (event['ts'] - startEvent['ts']);
|
||||
intervalStarts[name] = null;
|
||||
if (StringWrapper.equals(name, 'gc')) {
|
||||
result['gcTime'] += duration;
|
||||
var amount =
|
||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
result['gcAmount'] += amount;
|
||||
var majorGc = event['args']['majorGc'];
|
||||
if (isPresent(majorGc) && majorGc) {
|
||||
result['majorGcTime'] += duration;
|
||||
}
|
||||
if (isPresent(intervalStarts['script'])) {
|
||||
gcTimeInScript += duration;
|
||||
}
|
||||
} else if (StringWrapper.equals(name, 'render')) {
|
||||
result['renderTime'] += duration;
|
||||
if (isPresent(intervalStarts['script'])) {
|
||||
renderTimeInScript += duration;
|
||||
}
|
||||
} else if (StringWrapper.equals(name, 'script')) {
|
||||
result['scriptTime'] += duration;
|
||||
} else if (isPresent(this._microMetrics[name])) {
|
||||
(<any>result)[name] += duration / microIterations;
|
||||
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||
intervalStartCount[name]--;
|
||||
if (intervalStartCount[name] === 0) {
|
||||
var startEvent = intervalStarts[name];
|
||||
var duration = (event['ts'] - startEvent['ts']);
|
||||
intervalStarts[name] = null;
|
||||
if (name === 'gc') {
|
||||
result['gcTime'] += duration;
|
||||
var amount =
|
||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
result['gcAmount'] += amount;
|
||||
var majorGc = event['args']['majorGc'];
|
||||
if (majorGc && majorGc) {
|
||||
result['majorGcTime'] += duration;
|
||||
}
|
||||
if (intervalStarts['script']) {
|
||||
gcTimeInScript += duration;
|
||||
}
|
||||
} else if (name === 'render') {
|
||||
result['renderTime'] += duration;
|
||||
if (intervalStarts['script']) {
|
||||
renderTimeInScript += duration;
|
||||
}
|
||||
} else if (name === 'script') {
|
||||
result['scriptTime'] += duration;
|
||||
} else if (this._microMetrics[name]) {
|
||||
(<any>result)[name] += duration / microIterations;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) {
|
||||
// not all events have been received, no further processing for now
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) &&
|
||||
isBlank(frameCaptureEndEvent)) {
|
||||
if (frameCaptureStartEvent && !frameCaptureEndEvent) {
|
||||
throw new Error('missing end event for frame capture');
|
||||
}
|
||||
if (this._captureFrames && isBlank(frameCaptureStartEvent)) {
|
||||
if (this._captureFrames && !frameCaptureStartEvent) {
|
||||
throw new Error('frame capture requested in benchpress, but no start event was found');
|
||||
}
|
||||
if (frameTimes.length > 0) {
|
||||
|
@ -6,10 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isNumber} from '../facade/lang';
|
||||
import {Metric} from '../metric';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
@ -40,7 +39,7 @@ export class UserMetric extends Metric {
|
||||
reject = rej;
|
||||
});
|
||||
let adapter = this._wdAdapter;
|
||||
let names = StringMapWrapper.keys(this._userMetrics);
|
||||
let names = Object.keys(this._userMetrics);
|
||||
|
||||
function getAndClearValues() {
|
||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||
@ -48,9 +47,9 @@ export class UserMetric extends Metric {
|
||||
if (values.every(isNumber)) {
|
||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||
.then((_: any[]) => {
|
||||
let map = StringMapWrapper.create();
|
||||
let map: {[k: string]: any} = {};
|
||||
for (let i = 0, n = names.length; i < n; i++) {
|
||||
StringMapWrapper.set(map, names[i], values[i]);
|
||||
map[names[i]] = values[i];
|
||||
}
|
||||
resolve(map);
|
||||
}, reject);
|
||||
|
@ -7,10 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {NumberWrapper, isBlank, isPresent, print} from '../facade/lang';
|
||||
import {Math} from '../facade/math';
|
||||
import {print} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {DateWrapper, Json, isBlank, isPresent} from '../facade/lang';
|
||||
import {Json} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
@ -45,8 +45,7 @@ export class JsonFileReporter extends Reporter {
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample,
|
||||
});
|
||||
var filePath =
|
||||
`${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
|
||||
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||
return this._writeFile(filePath, content);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
|
||||
import {NumberWrapper} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Statistic} from '../statistic';
|
||||
@ -17,7 +17,7 @@ export function formatNum(n: number) {
|
||||
|
||||
export function sortedProps(obj: {[key: string]: any}) {
|
||||
var props: string[] = [];
|
||||
StringMapWrapper.forEach(obj, (value, prop) => props.push(prop));
|
||||
props.push(...Object.keys(obj));
|
||||
props.sort();
|
||||
return props;
|
||||
}
|
||||
@ -30,4 +30,4 @@ export function formatStats(validSamples: MeasureValues[], metricName: string):
|
||||
// Note: Don't use the unicode character for +- as it might cause
|
||||
// hickups for consoles...
|
||||
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {Metric} from './metric';
|
||||
import {MultiMetric} from './metric/multi_metric';
|
||||
import {PerflogMetric} from './metric/perflog_metric';
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {StringMapWrapper} from './facade/collection';
|
||||
import {Metric} from './metric';
|
||||
import {Validator} from './validator';
|
||||
|
||||
@ -42,7 +41,7 @@ export class SampleDescription {
|
||||
public metrics: {[key: string]: any}) {
|
||||
this.description = {};
|
||||
descriptions.forEach(description => {
|
||||
StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value);
|
||||
Object.keys(description).forEach(prop => { this.description[prop] = description[prop]; });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {Date, DateWrapper, isBlank, isPresent} from './facade/lang';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {MeasureValues} from './measure_values';
|
||||
import {Metric} from './metric';
|
||||
import {Reporter} from './reporter';
|
||||
@ -49,7 +49,7 @@ export class Sampler {
|
||||
}
|
||||
|
||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||
var resultPromise: Promise<any>;
|
||||
var resultPromise: Promise<SampleState>;
|
||||
if (this._prepare !== Options.NO_PREPARE) {
|
||||
resultPromise = this._driver.waitFor(this._prepare);
|
||||
} else {
|
||||
@ -77,5 +77,5 @@ export class Sampler {
|
||||
}
|
||||
|
||||
export class SampleState {
|
||||
constructor(public completeSample: any[], public validSample: any[]) {}
|
||||
constructor(public completeSample: MeasureValues[], public validSample: MeasureValues[]) {}
|
||||
}
|
||||
|
@ -9,16 +9,22 @@
|
||||
import {Injector, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
|
||||
export type PerfLogEvent = {
|
||||
cat?: string,
|
||||
ph?: 'X' | 'B' | 'E' | 'b' | 'e',
|
||||
[key: string]: any
|
||||
} & {
|
||||
ph?: 'X' | 'B' | 'E' | 'I',
|
||||
ts?: number,
|
||||
dur?: number,
|
||||
name?: string,
|
||||
pid?: string,
|
||||
args?: {encodedDataLength?: number, usedHeapSize?: number, majorGc?: number}
|
||||
args?: {
|
||||
encodedDataLength?: number,
|
||||
usedHeapSize?: number,
|
||||
majorGc?: boolean,
|
||||
url?: string,
|
||||
method?: string
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -43,7 +49,7 @@ export abstract class WebDriverExtension {
|
||||
delegate = extension;
|
||||
}
|
||||
});
|
||||
if (isBlank(delegate)) {
|
||||
if (!delegate) {
|
||||
throw new Error('Could not find a delegate for given capabilities!');
|
||||
}
|
||||
return delegate;
|
||||
@ -64,8 +70,7 @@ export abstract class WebDriverExtension {
|
||||
* Format:
|
||||
* - cat: category of the event
|
||||
* - name: event name: 'script', 'gc', 'render', ...
|
||||
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete
|
||||
*event)
|
||||
* - ph: phase: 'B' (begin), 'E' (end), 'X' (Complete event), 'I' (Instant event)
|
||||
* - ts: timestamp in ms, e.g. 12345
|
||||
* - pid: process id
|
||||
* - args: arguments, e.g. {heapSize: 1234}
|
||||
|
@ -9,16 +9,12 @@
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the following 'traceCategories' to collect metrics in Chrome:
|
||||
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline'
|
||||
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
|
||||
*
|
||||
* In order to collect the frame rate related metrics, add 'benchmark'
|
||||
* to the list above.
|
||||
@ -35,18 +31,18 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
private _parseChromeVersion(userAgent: string): number {
|
||||
if (isBlank(userAgent)) {
|
||||
if (!userAgent) {
|
||||
return -1;
|
||||
}
|
||||
var v = StringWrapper.split(userAgent, /Chrom(e|ium)\//g)[2];
|
||||
if (isBlank(v)) {
|
||||
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||
if (!v) {
|
||||
return -1;
|
||||
}
|
||||
v = v.split('.')[0];
|
||||
if (isBlank(v)) {
|
||||
if (!v) {
|
||||
return -1;
|
||||
}
|
||||
return NumberWrapper.parseInt(v, 10);
|
||||
return parseInt(v, 10);
|
||||
}
|
||||
|
||||
gc() { return this._driver.executeScript('window.gc()'); }
|
||||
@ -57,7 +53,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
if (restartName) {
|
||||
script += `console.time('${restartName}');`;
|
||||
}
|
||||
return this._driver.executeScript(script);
|
||||
@ -74,10 +70,10 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
var events: PerfLogEvent[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
|
||||
if (message['method'] === 'Tracing.dataCollected') {
|
||||
events.push(message['params']);
|
||||
}
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.bufferUsage')) {
|
||||
if (message['method'] === 'Tracing.bufferUsage') {
|
||||
throw new Error('The DevTools trace buffer filled during the test!');
|
||||
}
|
||||
});
|
||||
@ -87,107 +83,64 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
|
||||
private _convertPerfRecordsToEvents(
|
||||
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
|
||||
if (isBlank(normalizedEvents)) {
|
||||
if (!normalizedEvents) {
|
||||
normalizedEvents = [];
|
||||
}
|
||||
var majorGCPids = {};
|
||||
chromeEvents.forEach((event) => {
|
||||
var categories = this._parseCategories(event['cat']);
|
||||
var name = event['name'];
|
||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||
normalizedEvents.push(normalizeEvent(event, {'name': name}));
|
||||
} else if (this._isEvent(
|
||||
categories, name, ['benchmark'],
|
||||
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
||||
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
||||
// following events should be used (if available) for more accurate measurments:
|
||||
// 1st choice: vsync_before - ground truth on Android
|
||||
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
||||
// new surfaces framework (not broadly enabled yet)
|
||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||
// always available if something is rendered
|
||||
var frameCount = event['args']['data']['frame_count'];
|
||||
if (frameCount > 1) {
|
||||
throw new Error('multi-frame render stats not supported');
|
||||
}
|
||||
if (frameCount == 1) {
|
||||
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
|
||||
}
|
||||
} else if (
|
||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
||||
this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));
|
||||
} else if (this._majorChromeVersion < 45) {
|
||||
var normalizedEvent = this._processAsPreChrome45Event(event, categories, majorGCPids);
|
||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
||||
} else {
|
||||
var normalizedEvent = this._processAsPostChrome44Event(event, categories);
|
||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
||||
}
|
||||
const categories = this._parseCategories(event['cat']);
|
||||
const normalizedEvent = this._convertEvent(event, categories);
|
||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
||||
});
|
||||
return normalizedEvents;
|
||||
}
|
||||
|
||||
private _processAsPreChrome45Event(
|
||||
event: {[key: string]: any}, categories: string[], majorGCPids: {[key: string]: any}) {
|
||||
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
var pid = event['pid'];
|
||||
var ph = event['ph'];
|
||||
if (this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'FunctionCall') &&
|
||||
(isBlank(args) || isBlank(args['data']) ||
|
||||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
|
||||
return normalizeEvent(event, {'name': 'script'});
|
||||
} else if (
|
||||
this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'RecalculateStyles') ||
|
||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Layout') ||
|
||||
this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'UpdateLayerTree') ||
|
||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Paint')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||
return normalizeEvent(event, {'name': name});
|
||||
} else if (this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'GCEvent')) {
|
||||
var normArgs: {[key: string]: any} = {
|
||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
if (StringWrapper.equals(ph, 'E')) {
|
||||
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
|
||||
categories, name, ['benchmark'],
|
||||
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
||||
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
||||
// following events should be used (if available) for more accurate measurments:
|
||||
// 1st choice: vsync_before - ground truth on Android
|
||||
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
||||
// new surfaces framework (not broadly enabled yet)
|
||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||
// always available if something is rendered
|
||||
var frameCount = event['args']['data']['frame_count'];
|
||||
if (frameCount > 1) {
|
||||
throw new Error('multi-frame render stats not supported');
|
||||
}
|
||||
if (frameCount == 1) {
|
||||
return normalizeEvent(event, {'name': 'frame'});
|
||||
}
|
||||
majorGCPids[pid] = false;
|
||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||
} else if (
|
||||
this._isEvent(categories, name, ['v8'], 'majorGC') && StringWrapper.equals(ph, 'B')) {
|
||||
majorGCPids[pid] = true;
|
||||
}
|
||||
return null; // nothing useful in this event
|
||||
}
|
||||
|
||||
private _processAsPostChrome44Event(event: {[key: string]: any}, categories: string[]) {
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
||||
this._isEvent(
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||
var normArgs = {
|
||||
'majorGc': true,
|
||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||
var normArgs = {
|
||||
'majorGc': false,
|
||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||
} else if (
|
||||
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
|
||||
(isBlank(args) || isBlank(args['data']) ||
|
||||
(!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript') &&
|
||||
!StringWrapper.equals(args['data']['scriptName'], '')))) {
|
||||
(!args || !args['data'] ||
|
||||
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
|
||||
return normalizeEvent(event, {'name': 'script'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
|
||||
return normalizeEvent(event, {'name': 'script'});
|
||||
} else if (this._isEvent(
|
||||
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
|
||||
@ -205,7 +158,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||
return normalizeEvent(event, {'name': name});
|
||||
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||
}
|
||||
return null; // nothing useful in this event
|
||||
}
|
||||
@ -216,9 +169,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||
expectedName: string = null): boolean {
|
||||
var hasCategories = expectedCategories.reduce(
|
||||
(value, cat) => { return value && ListWrapper.contains(eventCategories, cat); }, true);
|
||||
return isBlank(expectedName) ? hasCategories :
|
||||
hasCategories && StringWrapper.equals(eventName, expectedName);
|
||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||
}
|
||||
|
||||
perfLogFeatures(): PerfLogFeatures {
|
||||
@ -226,28 +178,31 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return this._majorChromeVersion != -1 &&
|
||||
StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
|
||||
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEvent(
|
||||
chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent {
|
||||
var ph = chromeEvent['ph'];
|
||||
if (StringWrapper.equals(ph, 'S')) {
|
||||
ph = 'b';
|
||||
} else if (StringWrapper.equals(ph, 'F')) {
|
||||
ph = 'e';
|
||||
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||
var ph = chromeEvent['ph'].toUpperCase();
|
||||
if (ph === 'S') {
|
||||
ph = 'B';
|
||||
} else if (ph === 'F') {
|
||||
ph = 'E';
|
||||
} else if (ph === 'R') {
|
||||
// mark events from navigation timing
|
||||
ph = 'I';
|
||||
}
|
||||
var result: {[key: string]: any} =
|
||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||
if (chromeEvent['ph'] === 'X') {
|
||||
if (ph === 'X') {
|
||||
var dur = chromeEvent['dur'];
|
||||
if (isBlank(dur)) {
|
||||
if (dur === undefined) {
|
||||
dur = chromeEvent['tdur'];
|
||||
}
|
||||
result['dur'] = isBlank(dur) ? 0.0 : dur / 1000;
|
||||
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||
}
|
||||
for (let prop in data) {
|
||||
result[prop] = data[prop];
|
||||
}
|
||||
StringMapWrapper.forEach(data, (value, prop) => { result[prop] = value; });
|
||||
return result;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
|
||||
/** @internal */
|
||||
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
|
||||
if (isBlank(events)) {
|
||||
if (!events) {
|
||||
events = [];
|
||||
}
|
||||
records.forEach((record) => {
|
||||
@ -97,7 +97,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
function createEvent(
|
||||
ph: 'X' | 'B' | 'E' | 'b' | 'e', name: string, time: number, args: any = null) {
|
||||
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||
var result: PerfLogEvent = {
|
||||
'cat': 'timeline',
|
||||
'name': name,
|
||||
@ -122,9 +122,9 @@ function createEndEvent(name: string, time: number, args: any = null) {
|
||||
}
|
||||
|
||||
function createMarkStartEvent(name: string, time: number) {
|
||||
return createEvent('b', name, time);
|
||||
return createEvent('B', name, time);
|
||||
}
|
||||
|
||||
function createMarkEndEvent(name: string, time: number) {
|
||||
return createEvent('e', name, time);
|
||||
return createEvent('E', name, time);
|
||||
}
|
||||
|
@ -6,14 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids: any[]) {
|
||||
var m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }),
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
|
@ -7,11 +7,10 @@
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
@ -28,12 +27,12 @@ export function main() {
|
||||
requestCount?: boolean
|
||||
} = {}): Metric {
|
||||
commandLog = [];
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
if (!perfLogFeatures) {
|
||||
perfLogFeatures =
|
||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||
}
|
||||
if (isBlank(microMetrics)) {
|
||||
microMetrics = StringMapWrapper.create();
|
||||
if (!microMetrics) {
|
||||
microMetrics = {};
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||
@ -68,7 +67,7 @@ export function main() {
|
||||
|
||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||
var res: string[] = [];
|
||||
StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); });
|
||||
res.push(...Object.keys(stringMap));
|
||||
res.sort();
|
||||
return res;
|
||||
}
|
||||
@ -171,6 +170,22 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark and aggregate events since navigationStart',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]];
|
||||
var metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(data['scriptTime']).toBe(1);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
[
|
||||
|
@ -7,11 +7,9 @@
|
||||
*/
|
||||
|
||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {Injector, Metric, MultiMetric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, UserMetric, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {Json, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||
|
||||
export function main() {
|
||||
var wdAdapter: MockDriverAdapter;
|
||||
@ -19,12 +17,12 @@ export function main() {
|
||||
function createMetric(
|
||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
if (!perfLogFeatures) {
|
||||
perfLogFeatures =
|
||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||
}
|
||||
if (isBlank(userMetrics)) {
|
||||
userMetrics = StringMapWrapper.create();
|
||||
if (!userMetrics) {
|
||||
userMetrics = {};
|
||||
}
|
||||
wdAdapter = new MockDriverAdapter();
|
||||
var providers: Provider[] = [
|
||||
|
@ -7,10 +7,10 @@
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {ConsoleReporter, MeasureValues, ReflectiveInjector, Reporter, SampleDescription, SampleState} from '../../index';
|
||||
import {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
|
||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('console reporter', () => {
|
||||
@ -25,7 +25,7 @@ export function main() {
|
||||
metrics?: {[key: string]: any}
|
||||
}) {
|
||||
log = [];
|
||||
if (isBlank(descriptions)) {
|
||||
if (!descriptions) {
|
||||
descriptions = [];
|
||||
}
|
||||
if (isBlank(sampleId)) {
|
||||
@ -90,5 +90,5 @@ export function main() {
|
||||
}
|
||||
|
||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
return new MeasureValues(runIndex, new Date(time), values);
|
||||
}
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
||||
import {DateWrapper, Json, isPresent} from '../../src/facade/lang';
|
||||
import {Json, isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
@ -27,7 +27,7 @@ export function main() {
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||
},
|
||||
{provide: JsonFileReporter.PATH, useValue: path},
|
||||
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, {
|
||||
{provide: Options.NOW, useValue: () => new Date(1234)}, {
|
||||
provide: Options.WRITE_FILE,
|
||||
useValue: (filename: string, content: string) => {
|
||||
loggedFile = {'filename': filename, 'content': content};
|
||||
@ -77,5 +77,5 @@ export function main() {
|
||||
}
|
||||
|
||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
return new MeasureValues(runIndex, new Date(time), values);
|
||||
}
|
||||
|
@ -6,16 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids: any[]) {
|
||||
var r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => { return {provide: id, useValue: new MockReporter(id)}; }),
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
@ -26,7 +25,7 @@ export function main() {
|
||||
|
||||
it('should reportMeasureValues to all',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var mv = new MeasureValues(0, DateWrapper.now(), {});
|
||||
var mv = new MeasureValues(0, new Date(), {});
|
||||
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||
|
||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||
@ -35,9 +34,8 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var completeSample = [
|
||||
new MeasureValues(0, DateWrapper.now(), {}), new MeasureValues(1, DateWrapper.now(), {})
|
||||
];
|
||||
var completeSample =
|
||||
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||
var validSample = [completeSample[1]];
|
||||
|
||||
createReporters(['m1', 'm2'])
|
||||
|
@ -6,10 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
import {isBlank} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
@ -17,7 +16,7 @@ export function main() {
|
||||
var runner: Runner;
|
||||
|
||||
function createRunner(defaultProviders: any[] = null): Runner {
|
||||
if (isBlank(defaultProviders)) {
|
||||
if (!defaultProviders) {
|
||||
defaultProviders = [];
|
||||
}
|
||||
runner = new Runner([
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
import {Date, DateWrapper, isBlank, isPresent, stringify} from '../src/facade/lang';
|
||||
import {isBlank, isPresent} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
var EMPTY_EXECUTE = () => {};
|
||||
@ -26,10 +26,10 @@ export function main() {
|
||||
execute?: any
|
||||
} = {}) {
|
||||
var time = 1000;
|
||||
if (isBlank(metric)) {
|
||||
if (!metric) {
|
||||
metric = new MockMetric([]);
|
||||
}
|
||||
if (isBlank(reporter)) {
|
||||
if (!reporter) {
|
||||
reporter = new MockReporter([]);
|
||||
}
|
||||
if (isBlank(driver)) {
|
||||
@ -39,7 +39,7 @@ export function main() {
|
||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
||||
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(time++)}
|
||||
{provide: Options.NOW, useValue: () => new Date(time++)}
|
||||
];
|
||||
if (isPresent(prepare)) {
|
||||
providers.push({provide: Options.PREPARE, useValue: prepare});
|
||||
@ -60,8 +60,8 @@ export function main() {
|
||||
createSampler({
|
||||
driver: driver,
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => { return count++; },
|
||||
execute: () => { return count++; }
|
||||
prepare: () => count++,
|
||||
execute: () => count++,
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
expect(count).toBe(4);
|
||||
@ -204,7 +204,7 @@ export function main() {
|
||||
}
|
||||
|
||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
return new MeasureValues(runIndex, new Date(time), values);
|
||||
}
|
||||
|
||||
function createCountingValidator(
|
||||
@ -221,7 +221,7 @@ function createCountingValidator(
|
||||
|
||||
function createCountingMetric(log: any[] = []) {
|
||||
var scriptTime = 0;
|
||||
return new MockMetric(log, () => { return {'script': scriptTime++}; });
|
||||
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import {Statistic} from '../src/statistic';
|
||||
|
||||
export function main() {
|
||||
|
@ -21,16 +21,16 @@ export class TraceEventFactory {
|
||||
return res;
|
||||
}
|
||||
|
||||
markStart(name: string, time: number) { return this.create('b', name, time); }
|
||||
markStart(name: string, time: number) { return this.create('B', name, time); }
|
||||
|
||||
markEnd(name: string, time: number) { return this.create('e', name, time); }
|
||||
markEnd(name: string, time: number) { return this.create('E', name, time); }
|
||||
|
||||
start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); }
|
||||
|
||||
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
|
||||
|
||||
instant(name: string, time: number, args: any = null) {
|
||||
return this.create('i', name, time, args);
|
||||
return this.create('I', name, time, args);
|
||||
}
|
||||
|
||||
complete(name: string, time: number, duration: number, args: any = null) {
|
||||
|
@ -6,11 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('regression slope validator', () => {
|
||||
@ -62,5 +61,5 @@ export function main() {
|
||||
}
|
||||
|
||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
return new MeasureValues(runIndex, new Date(time), values);
|
||||
}
|
||||
|
@ -6,11 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {MeasureValues, ReflectiveInjector, SizeValidator, Validator} from '../../index';
|
||||
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('size validator', () => {
|
||||
@ -47,5 +46,5 @@ export function main() {
|
||||
}
|
||||
|
||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
return new MeasureValues(runIndex, new Date(time), values);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
||||
import {StringWrapper, isPresent} from '../src/facade/lang';
|
||||
@ -17,7 +17,7 @@ export function main() {
|
||||
try {
|
||||
res(ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map((id) => { return {provide: id, useValue: new MockExtension(id)}; }),
|
||||
ids.map((id) => ({provide: id, useValue: new MockExtension(id)})),
|
||||
{provide: Options.CAPABILITIES, useValue: caps},
|
||||
WebDriverExtension.provideFirstSupported(ids)
|
||||
])
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {Json, isBlank} from '../../src/facade/lang';
|
||||
@ -14,8 +14,6 @@ import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('chrome driver extension', () => {
|
||||
var CHROME44_USER_AGENT =
|
||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.0 Safari/537.36"';
|
||||
var CHROME45_USER_AGENT =
|
||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
||||
|
||||
@ -37,11 +35,11 @@ export function main() {
|
||||
function createExtension(
|
||||
perfRecords: any[] = null, userAgent: string = null,
|
||||
messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
|
||||
if (isBlank(perfRecords)) {
|
||||
if (!perfRecords) {
|
||||
perfRecords = [];
|
||||
}
|
||||
if (isBlank(userAgent)) {
|
||||
userAgent = CHROME44_USER_AGENT;
|
||||
userAgent = CHROME45_USER_AGENT;
|
||||
}
|
||||
log = [];
|
||||
extension = ReflectiveInjector
|
||||
@ -89,228 +87,95 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog Chrome44', () => {
|
||||
it('should normalize times to ms and forward ph and pid event properties',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should normalize times to ms and forward ph and pid event properties',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should normalize "tdur" to "dur"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var event: any = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).readPerfLog().then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should normalize "tdur" to "dur"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).readPerfLog().then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report FunctionCall events as "script"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineEvents.start('FunctionCall', 0)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should report FunctionCall events as "script"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
||||
])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should report EvaluateScript events as "script"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore major gc from different processes',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
v8EventsOtherProcess.start('majorGC', 1100, null),
|
||||
v8EventsOtherProcess.end('majorGC', 1200, null),
|
||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
||||
])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([
|
||||
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||
])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events.length).toEqual(2);
|
||||
expect(events[0]).toEqual(
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
||||
expect(events[1]).toEqual(
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
v8Events.start('majorGC', 1100, null),
|
||||
v8Events.end('majorGC', 1200, null),
|
||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
||||
])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||
], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events.length).toEqual(2);
|
||||
expect(events[0]).toEqual(
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
||||
expect(events[1]).toEqual(
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
||||
it(`should report ${recordType} as "render"`,
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start(recordType, 1234),
|
||||
chromeTimelineEvents.end(recordType, 2345)
|
||||
])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 1.234),
|
||||
normEvents.end('render', 2.345),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should ignore FunctionCalls from webdriver',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineEvents.start(
|
||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('readPerfLog Chrome45', () => {
|
||||
it('should normalize times to ms and forward ph and pid event properties',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should normalize "tdur" to "dur"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event], CHROME45_USER_AGENT).readPerfLog().then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report FunctionCall events as "script"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)], CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||
],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events.length).toEqual(2);
|
||||
expect(events[0]).toEqual(
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
||||
expect(events[1]).toEqual(
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||
],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events.length).toEqual(2);
|
||||
expect(events[0]).toEqual(
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
||||
expect(events[1]).toEqual(
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
||||
it(`should report ${recordType} as "render"`,
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chrome45TimelineEvents.start(recordType, 1234),
|
||||
chrome45TimelineEvents.end(recordType, 2345)
|
||||
],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 1.234),
|
||||
normEvents.end('render', 2.345),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it(`should report UpdateLayoutTree as "render"`,
|
||||
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
||||
it(`should report ${recordType} as "render"`,
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
||||
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
||||
],
|
||||
CHROME45_USER_AGENT)
|
||||
chrome45TimelineEvents.start(recordType, 1234),
|
||||
chrome45TimelineEvents.end(recordType, 2345)
|
||||
], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
@ -320,70 +185,80 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
|
||||
it('should ignore FunctionCalls from webdriver',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.start(
|
||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls with empty scriptName',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report navigationStart',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chromeBlinkUserTimingEvents.start('navigationStart', 1234)], CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.start('navigationStart', 1.234)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chrome45TimelineEvents.instant(
|
||||
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual(
|
||||
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chrome45TimelineEvents.instant(
|
||||
'ResourceSendRequest', 1234,
|
||||
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})],
|
||||
CHROME45_USER_AGENT)
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.instant(
|
||||
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it(`should report UpdateLayoutTree as "render"`,
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[
|
||||
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
||||
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
||||
], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 1.234),
|
||||
normEvents.end('render', 2.345),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls from webdriver',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeTimelineV8Events.start(
|
||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls with empty scriptName',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension(
|
||||
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report navigationStart',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chromeBlinkUserTimingEvents.instant('navigationStart', 1234)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.instant('navigationStart', 1.234)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chrome45TimelineEvents.instant(
|
||||
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual(
|
||||
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension([chrome45TimelineEvents.instant(
|
||||
'ResourceSendRequest', 1234,
|
||||
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.instant(
|
||||
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog (common)', () => {
|
||||
|
||||
it('should execute a dummy script before reading them',
|
||||
@ -404,8 +279,7 @@ export function main() {
|
||||
[
|
||||
chromeTimelineEvents.start(recordType, 1234),
|
||||
chromeTimelineEvents.end(recordType, 2345)
|
||||
],
|
||||
CHROME45_USER_AGENT)
|
||||
], )
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
@ -426,7 +300,7 @@ export function main() {
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.create('i', 'frame', 1.1),
|
||||
normEvents.instant('frame', 1.1),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
@ -522,11 +396,11 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
logs(type: string) {
|
||||
this._log.push(['logs', type]);
|
||||
if (type === 'performance') {
|
||||
return Promise.resolve(this._events.map((event) => {
|
||||
return {
|
||||
'message': Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
||||
};
|
||||
}));
|
||||
return Promise.resolve(this._events.map(
|
||||
(event) => ({
|
||||
'message':
|
||||
Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
||||
})));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {Json, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {Json} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
@ -20,7 +20,7 @@ export function main() {
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||
if (isBlank(perfRecords)) {
|
||||
if (!perfRecords) {
|
||||
perfRecords = [];
|
||||
}
|
||||
log = [];
|
||||
@ -156,7 +156,7 @@ function timeEndRecord(name: string, time: number) {
|
||||
}
|
||||
|
||||
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
|
||||
if (isBlank(children)) {
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@angular/common",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "",
|
||||
"description": "Angular - commonly needed directives and services",
|
||||
"main": "bundles/common.umd.js",
|
||||
"module": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
|
@ -1,3 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/testing/index.js',
|
||||
@ -10,4 +17,4 @@ export default {
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/index.js',
|
||||
@ -7,6 +14,6 @@ export default {
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
'rxjs/Subject': 'Rx',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11,65 +11,32 @@ import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableD
|
||||
import {isListLikeIterable} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
||||
* an expression's evaluation result.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* The result of an expression evaluation is interpreted differently depending on type of
|
||||
* the expression evaluation result:
|
||||
* - `string` - all the CSS classes listed in a string (space delimited) are added
|
||||
* - `Array` - all the CSS classes (Array elements) are added
|
||||
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
|
||||
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
|
||||
* is added - otherwise it is removed.
|
||||
*
|
||||
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
|
||||
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
|
||||
* all the CSS class names in a template.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
||||
* @whatItDoes Adds and removes CSS classes on an HTML element.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgClass} from '@angular/common';
|
||||
* <some-element [ngClass]="'first second'">...</some-element>
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'toggle-button',
|
||||
* inputs: ['isDisabled'],
|
||||
* template: `
|
||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
||||
* (click)="toggle(!isOn)">
|
||||
* Click me!
|
||||
* </div>`,
|
||||
* styles: [`
|
||||
* .button {
|
||||
* width: 120px;
|
||||
* border: medium solid black;
|
||||
* }
|
||||
* <some-element [ngClass]="['first', 'second']">...</some-element>
|
||||
*
|
||||
* .active {
|
||||
* background-color: red;
|
||||
* }
|
||||
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
|
||||
*
|
||||
* .disabled {
|
||||
* color: gray;
|
||||
* border: medium solid gray;
|
||||
* }
|
||||
* `],
|
||||
* directives: [NgClass]
|
||||
* })
|
||||
* class ToggleButton {
|
||||
* isOn = false;
|
||||
* isDisabled = false;
|
||||
*
|
||||
* toggle(newState) {
|
||||
* if (!this.isDisabled) {
|
||||
* this.isOn = newState;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* The CSS classes are updated as follow depending on the type of the expression evaluation:
|
||||
* - `string` - the CSS classes listed in a string (space delimited) are added,
|
||||
* - `Array` - the CSS classes (Array elements) are added,
|
||||
* - `Object` - keys are CSS class names that get added when the expression given in the value
|
||||
* evaluates to a truthy value, otherwise class are removed.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngClass]'})
|
||||
|
@ -14,48 +14,35 @@ import {SwitchView} from './ng_switch';
|
||||
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* @whatItDoes Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* <some-element [ngPlural]="value">
|
||||
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
|
||||
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
|
||||
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
|
||||
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
|
||||
* </some-element>
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees
|
||||
* that match the switch expression's pluralization category.
|
||||
*
|
||||
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
||||
* to a
|
||||
* switch expression.
|
||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
||||
* expression.
|
||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly.
|
||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* value matches aren't found and the value maps to its category for the defined locale.
|
||||
* to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their
|
||||
* expression:
|
||||
* - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly,
|
||||
* - otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* value matches aren't found and the value maps to its category for the defined locale.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* // best practice is to define the locale at the application level
|
||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
* See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
*
|
||||
* <div [ngPlural]="value">
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* <template ngPluralCase="other">there is some number</template>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [NgPlural, NgPluralCase]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
@ -74,8 +61,7 @@ export class NgPlural {
|
||||
|
||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
private _updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
const cases = Object.keys(this._caseViews);
|
||||
@ -83,13 +69,11 @@ export class NgPlural {
|
||||
this._activateView(this._caseViews[key]);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
private _clearViews() {
|
||||
if (this._activeView) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
private _activateView(view: SwitchView) {
|
||||
if (view) {
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
@ -98,6 +82,21 @@ export class NgPlural {
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgPlural} when the
|
||||
* given expression matches the plural expression according to CLDR rules.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* <some-element [ngPlural]="value">
|
||||
* <ng-container *ngPluralCase="'=0'">...</ng-container>
|
||||
* <ng-container *ngPluralCase="'other'">...</ng-container>
|
||||
* </some-element>
|
||||
*```
|
||||
*
|
||||
* See {@link NgPlural} for more details and example.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
|
@ -9,65 +9,31 @@
|
||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
||||
* @whatItDoes Update an HTML element styles.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgStyle} from '@angular/common';
|
||||
* <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ngStyle-example',
|
||||
* template: `
|
||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
||||
* Change style of this text!
|
||||
* </h1>
|
||||
* <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element>
|
||||
*
|
||||
* <hr>
|
||||
*
|
||||
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
|
||||
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
|
||||
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
|
||||
* `,
|
||||
* directives: [NgStyle]
|
||||
* })
|
||||
* export class NgStyleExample {
|
||||
* style = 'normal';
|
||||
* weight = 'normal';
|
||||
* size = '20px';
|
||||
*
|
||||
* changeStyle($event: any) {
|
||||
* this.style = $event.target.checked ? 'italic' : 'normal';
|
||||
* }
|
||||
*
|
||||
* changeWeight($event: any) {
|
||||
* this.weight = $event.target.checked ? 'bold' : 'normal';
|
||||
* }
|
||||
* }
|
||||
* <some-element [ngStyle]="objExp">...</some-element>
|
||||
* ```
|
||||
*
|
||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
||||
* based on the `style` property's value changes.
|
||||
* @description
|
||||
*
|
||||
* The styles are updated according to the value of the expression evaluation:
|
||||
* - keys are style names with an option `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||
* - values are the values assigned to those properties (expressed in the given unit).
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngStyle]'})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_ngStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
private _ngStyle: {[key: string]: string};
|
||||
private _differ: KeyValueDiffer;
|
||||
|
||||
constructor(
|
||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
@ -101,7 +67,7 @@ export class NgStyle implements DoCheck {
|
||||
|
||||
private _setStyle(nameAndUnit: string, value: string): void {
|
||||
const [name, unit] = nameAndUnit.split('.');
|
||||
value = value !== null && value !== void(0) && unit ? `${value}${unit}` : value;
|
||||
value = value && unit ? `${value}${unit}` : value;
|
||||
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
|
||||
}
|
||||
|
@ -22,58 +22,44 @@ export class SwitchView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
||||
* preserved at the location as specified in the template.
|
||||
* @whatItDoes Adds / removes DOM sub-trees when the nest match expressions matches the switch
|
||||
* expression.
|
||||
*
|
||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
||||
* (where you place the directive with a switch expression on the
|
||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
||||
* place a `[ngSwitchCase]` attribute per element.
|
||||
*
|
||||
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
|
||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
|
||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngSwitch]="value">
|
||||
* <p *ngSwitchCase="'init'">increment to start</p>
|
||||
* <p *ngSwitchCase="0">0, increment again</p>
|
||||
* <p *ngSwitchCase="1">1, increment again</p>
|
||||
* <p *ngSwitchCase="2">2, stop incrementing</p>
|
||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
||||
* </div>
|
||||
*
|
||||
* <!-- alternate syntax -->
|
||||
*
|
||||
* <p [ngSwitch]="value">
|
||||
* <template ngSwitchCase="init">increment to start</template>
|
||||
* <template [ngSwitchCase]="0">0, increment again</template>
|
||||
* <template [ngSwitchCase]="1">1, increment again</template>
|
||||
* <template [ngSwitchCase]="2">2, stop incrementing</template>
|
||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
||||
* </p>
|
||||
* `,
|
||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
* @howToUse
|
||||
* ```
|
||||
* <container-element [ngSwitch]="switch_expression">
|
||||
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||
* <some-element *ngSwitchCase="match_expression_2">...</some-element>
|
||||
* <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
|
||||
* <ng-container *ngSwitchCase="match_expression_3">
|
||||
* <!-- use a ng-container to group multiple root nodes -->
|
||||
* <inner-element></inner-element>
|
||||
* <inner-other-element></inner-other-element>
|
||||
* </ng-container>
|
||||
* <some-element *ngSwitchDefault>...</p>
|
||||
* </container-element>
|
||||
* ```
|
||||
* @description
|
||||
*
|
||||
* `NgSwitch` stamps out nested views when their match expression value matches the value of the
|
||||
* switch expression.
|
||||
*
|
||||
* In other words:
|
||||
* - you define a container element (where you place the directive with a switch expression on the
|
||||
* `[ngSwitch]="..."` attribute)
|
||||
* - you define inner views inside the `NgSwitch` and place a `*ngSwitchCase` attribute on the view
|
||||
* root elements.
|
||||
*
|
||||
* Elements within `NgSwitch` but outside of a `NgSwitchCase` or `NgSwitchDefault` directives will
|
||||
* be
|
||||
* preserved at the location.
|
||||
*
|
||||
* The `ngSwitchCase` directive informs the parent `NgSwitch` of which view to display when the
|
||||
* expression is evaluated.
|
||||
* When no matching expression is found on a `ngSwitchCase` view, the `ngSwitchDefault` view is
|
||||
* stamped out.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -125,8 +111,7 @@ export class NgSwitch {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_emptyAllActiveViews(): void {
|
||||
private _emptyAllActiveViews(): void {
|
||||
const activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].destroy();
|
||||
@ -134,9 +119,7 @@ export class NgSwitch {
|
||||
this._activeViews = [];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateViews(views: SwitchView[]): void {
|
||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
||||
private _activateViews(views: SwitchView[]): void {
|
||||
if (views) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
@ -155,8 +138,7 @@ export class NgSwitch {
|
||||
views.push(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
private _deregisterView(value: any, view: SwitchView): void {
|
||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||
if (value === _CASE_DEFAULT) return;
|
||||
const views = this._valueViews.get(value);
|
||||
@ -169,10 +151,24 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
|
||||
* enclosing switch expression.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
||||
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgSwitch} when the
|
||||
* given expression evaluate to respectively the same/different value as the switch
|
||||
* expression.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* <container-element [ngSwitch]="switch_expression">
|
||||
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||
* </container-element>
|
||||
*```
|
||||
* @description
|
||||
*
|
||||
* Insert the sub-tree when the expression evaluates to the same value as the enclosing switch
|
||||
* expression.
|
||||
*
|
||||
* If multiple match expressions match the switch expression value, all of them are displayed.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
@ -181,10 +177,8 @@ export class NgSwitch {
|
||||
@Directive({selector: '[ngSwitchCase]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
_value: any = _CASE_DEFAULT;
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
private _value: any = _CASE_DEFAULT;
|
||||
private _view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
constructor(
|
||||
@ -202,8 +196,23 @@ export class NgSwitchCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default case statements are displayed when no match expression matches the switch expression
|
||||
* value.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Creates a view that is added to the parent {@link NgSwitch} when no case expressions
|
||||
* match the
|
||||
* switch expression.
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* <container-element [ngSwitch]="switch_expression">
|
||||
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||
* <some-other-element *ngSwitchDefault>...</some-other-element>
|
||||
* </container-element>
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Insert the sub-tree when no case expressions evaluate to the same value as the enclosing switch
|
||||
* expression.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
|
@ -6,24 +6,28 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||
* available within the `TemplateRef`.
|
||||
* @ngModule CommonModule
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* ### Syntax
|
||||
* @whatItDoes Inserts an embedded view from a prepared `TemplateRef`
|
||||
*
|
||||
* @howToUse
|
||||
* ```
|
||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression">
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||
* available within the `TemplateRef`.
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
@ -40,7 +44,7 @@ export class NgTemplateOutlet implements OnChanges {
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||
|
||||
ngOnChanges() {
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export enum Plural {
|
||||
Two,
|
||||
Few,
|
||||
Many,
|
||||
Other
|
||||
Other,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,16 +49,20 @@ export class Location {
|
||||
_subject: EventEmitter<any> = new EventEmitter();
|
||||
/** @internal */
|
||||
_baseHref: string;
|
||||
|
||||
/** @internal */
|
||||
_platformStrategy: LocationStrategy;
|
||||
|
||||
constructor(platformStrategy: LocationStrategy) {
|
||||
this._platformStrategy = platformStrategy;
|
||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
const browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||
this._platformStrategy.onPopState(
|
||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
||||
this._platformStrategy.onPopState((ev) => {
|
||||
this._subject.emit({
|
||||
'url': this.path(true),
|
||||
'pop': true,
|
||||
'type': ev.type,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
||||
import {EventEmitter, Observable} from '../facade/async';
|
||||
import {isBlank, isPresent, isPromise} from '../facade/lang';
|
||||
import {isPromise} from '../private_import_core';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
interface SubscriptionStrategy {
|
||||
@ -37,63 +37,54 @@ class PromiseStrategy implements SubscriptionStrategy {
|
||||
onDestroy(subscription: any): void {}
|
||||
}
|
||||
|
||||
var _promiseStrategy = new PromiseStrategy();
|
||||
var _observableStrategy = new ObservableStrategy();
|
||||
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
|
||||
const _promiseStrategy = new PromiseStrategy();
|
||||
const _observableStrategy = new ObservableStrategy();
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Unwraps a value from an asynchronous primitive.
|
||||
* @howToUse `observable_or_promise_expression | async`
|
||||
* @description
|
||||
* The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
||||
* emitted.
|
||||
* When a new value is emitted, the `async` pipe marks the component to be checked for changes.
|
||||
* When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
||||
* emitted. When a new value is emitted, the `async` pipe marks the component to be checked for
|
||||
* changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
||||
* potential memory leaks.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* object | async
|
||||
*
|
||||
* where `object` is of type `Observable` or of type `Promise`.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
||||
* promise.
|
||||
*
|
||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipePromise'}
|
||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||
*
|
||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||
* to the view. Every 500ms, the `time` Observable updates the view with the current time.
|
||||
* to the view. The Observable continuesly updates the view with the current time.
|
||||
*
|
||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipeObservable'}
|
||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'async', pure: false})
|
||||
export class AsyncPipe implements OnDestroy {
|
||||
/** @internal */
|
||||
_latestValue: Object = null;
|
||||
/** @internal */
|
||||
_latestReturnedValue: Object = null;
|
||||
private _latestValue: Object = null;
|
||||
private _latestReturnedValue: Object = null;
|
||||
|
||||
/** @internal */
|
||||
_subscription: Object = null;
|
||||
/** @internal */
|
||||
_obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
||||
/** @internal */
|
||||
_ref: ChangeDetectorRef;
|
||||
private _subscription: Object = null;
|
||||
private _obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
||||
private _strategy: SubscriptionStrategy = null;
|
||||
|
||||
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
|
||||
constructor(private _ref: ChangeDetectorRef) {}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (isPresent(this._subscription)) {
|
||||
if (this._subscription) {
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||
if (isBlank(this._obj)) {
|
||||
if (isPresent(obj)) {
|
||||
if (!this._obj) {
|
||||
if (obj) {
|
||||
this._subscribe(obj);
|
||||
}
|
||||
this._latestReturnedValue = this._latestValue;
|
||||
@ -107,33 +98,32 @@ export class AsyncPipe implements OnDestroy {
|
||||
|
||||
if (this._latestValue === this._latestReturnedValue) {
|
||||
return this._latestReturnedValue;
|
||||
} else {
|
||||
this._latestReturnedValue = this._latestValue;
|
||||
return WrappedValue.wrap(this._latestValue);
|
||||
}
|
||||
|
||||
this._latestReturnedValue = this._latestValue;
|
||||
return WrappedValue.wrap(this._latestValue);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
||||
private _subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
||||
this._obj = obj;
|
||||
this._strategy = this._selectStrategy(obj);
|
||||
this._subscription = this._strategy.createSubscription(
|
||||
obj, (value: Object) => this._updateLatestValue(obj, value));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||
private _selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||
if (isPromise(obj)) {
|
||||
return _promiseStrategy;
|
||||
} else if ((<any>obj).subscribe) {
|
||||
return _observableStrategy;
|
||||
} else {
|
||||
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
||||
}
|
||||
|
||||
if ((<any>obj).subscribe) {
|
||||
return _observableStrategy;
|
||||
}
|
||||
|
||||
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_dispose(): void {
|
||||
private _dispose(): void {
|
||||
this._strategy.dispose(this._subscription);
|
||||
this._latestValue = null;
|
||||
this._latestReturnedValue = null;
|
||||
@ -141,8 +131,7 @@ export class AsyncPipe implements OnDestroy {
|
||||
this._obj = null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateLatestValue(async: any, value: Object) {
|
||||
private _updateLatestValue(async: any, value: Object) {
|
||||
if (async === this._obj) {
|
||||
this._latestValue = value;
|
||||
this._ref.markForCheck();
|
||||
|
@ -7,31 +7,31 @@
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {DateFormatter} from '../facade/intl';
|
||||
import {DateWrapper, NumberWrapper, isBlank, isDate, isString} from '../facade/lang';
|
||||
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
|
||||
/**
|
||||
* Formats a date value to a string based on the requested format.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a date according to locale rules.
|
||||
* @howToUse `date_expression | date[:format]`
|
||||
* @description
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||
* which would be an expensive operation).
|
||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||
* browsers.
|
||||
* Where:
|
||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||
* (https://www.w3.org/TR/NOTE-datetime).
|
||||
* - `format` indicates which date/time components to include. The format can be predifined as
|
||||
* shown below or custom as shown in the table.
|
||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. `Friday, September 3, 2010` for `en-US`)
|
||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. `September 3, 2010` for `en-US`)
|
||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. `Sep 3, 2010` for `en-US`)
|
||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. `9/3/2010` for `en-US`)
|
||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. `12:05:08 PM` for `en-US`)
|
||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | date[:format]
|
||||
*
|
||||
* where `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||
* (https://www.w3.org/TR/NOTE-datetime) and `format` indicates which date/time components to
|
||||
* include:
|
||||
*
|
||||
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||
@ -52,19 +52,16 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* In javascript, only the components specified will be respected (not the ordering,
|
||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||
*
|
||||
* `format` can also be one of the following predefined formats:
|
||||
*
|
||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. Sep 3, 2010, 12:05:08 PM for en-US)
|
||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. 9/3/2010, 12:05 PM for en-US)
|
||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. Friday, September 3, 2010 for en-US)
|
||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. September 3, 2010 for en-US)
|
||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. Sep 3, 2010 for en-US)
|
||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. 9/3/2010 for en-US)
|
||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. 12:05:08 PM for en-US)
|
||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. 12:05 PM for en-US)
|
||||
*
|
||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||
* which would be an expensive operation).
|
||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||
* browsers.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
|
||||
@ -77,14 +74,14 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||
* ```
|
||||
*
|
||||
* {@example core/pipes/ts/date_pipe/date_pipe_example.ts region='DatePipe'}
|
||||
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'date', pure: true})
|
||||
export class DatePipe implements PipeTransform {
|
||||
/** @internal */
|
||||
static _ALIASES: {[key: string]: String} = {
|
||||
static _ALIASES: {[key: string]: string} = {
|
||||
'medium': 'yMMMdjms',
|
||||
'short': 'yMdjm',
|
||||
'fullDate': 'yMMMMEEEEd',
|
||||
@ -105,23 +102,15 @@ export class DatePipe implements PipeTransform {
|
||||
}
|
||||
|
||||
if (NumberWrapper.isNumeric(value)) {
|
||||
value = DateWrapper.fromMillis(parseFloat(value));
|
||||
} else if (isString(value)) {
|
||||
value = DateWrapper.fromISOString(value);
|
||||
value = parseFloat(value);
|
||||
}
|
||||
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
|
||||
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
|
||||
}
|
||||
return DateFormatter.format(value, this._locale, pattern);
|
||||
|
||||
return DateFormatter.format(
|
||||
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
|
||||
private supports(obj: any): boolean {
|
||||
if (isDate(obj) || NumberWrapper.isNumeric(obj)) {
|
||||
return true;
|
||||
}
|
||||
if (isString(obj) && isDate(DateWrapper.fromISOString(obj))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||
}
|
||||
}
|
||||
|
@ -7,46 +7,26 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {StringWrapper, isBlank, isStringMap} from '../facade/lang';
|
||||
import {isBlank, isStringMap} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
|
||||
/**
|
||||
* Maps a value to a string that pluralizes the value properly.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Maps a value to a string that pluralizes the value according to locale rules.
|
||||
* @howToUse `expression | i18nPlural:mapping`
|
||||
* @description
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nPlural:mapping
|
||||
*
|
||||
* where `expression` is a number and `mapping` is an object that mimics the ICU format,
|
||||
* see http://userguide.icu-project.org/formatparse/messages
|
||||
* Where:
|
||||
* - `expression` is a number.
|
||||
* - `mapping` is an object that mimics the ICU format, see
|
||||
* http://userguide.icu-project.org/formatparse/messages
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <div>
|
||||
* {{ messages.length | i18nPlural: messageMapping }}
|
||||
* </div>
|
||||
* `,
|
||||
* // best practice is to define the locale at the application level
|
||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
||||
* })
|
||||
*
|
||||
* class MyApp {
|
||||
* messages: any[];
|
||||
* messageMapping: {[k:string]: string} = {
|
||||
* '=0': 'No messages.',
|
||||
* '=1': 'One message.',
|
||||
* 'other': '# messages.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@ -63,6 +43,6 @@ export class I18nPluralPipe implements PipeTransform {
|
||||
|
||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
||||
|
||||
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, value.toString());
|
||||
return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString());
|
||||
}
|
||||
}
|
||||
|
@ -11,33 +11,18 @@ import {isBlank, isStringMap} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Generic selector that displays the string that matches the current value.
|
||||
* @howToUse `expression | i18nSelect:mapping`
|
||||
* @description
|
||||
*
|
||||
* Generic selector that displays the string that matches the current value.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nSelect:mapping
|
||||
*
|
||||
* where `mapping` is an object that indicates the text that should be displayed
|
||||
* Where:
|
||||
* - `mapping`: is an object that indicates the text that should be displayed
|
||||
* for different values of the provided `expression`.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ gender | i18nSelect: inviteMap }}
|
||||
* </div>
|
||||
*
|
||||
* class MyApp {
|
||||
* gender: string = 'male';
|
||||
* inviteMap: any = {
|
||||
* 'male': 'Invite him.',
|
||||
* 'female': 'Invite her.',
|
||||
* 'other': 'Invite them.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
|
@ -13,10 +13,15 @@ import {Json} from '../facade/lang';
|
||||
|
||||
|
||||
/**
|
||||
* Transforms any input value using `JSON.stringify`. Useful for debugging.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Converts value into JSON string.
|
||||
* @howToUse `expression | json`
|
||||
* @description
|
||||
*
|
||||
* Converts value into string using `JSON.stringify`. Useful for debugging.
|
||||
*
|
||||
* ### Example
|
||||
* {@example core/pipes/ts/json_pipe/json_pipe_example.ts region='JsonPipe'}
|
||||
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -7,16 +7,21 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isString} from '../facade/lang';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
|
||||
/**
|
||||
* Transforms text to lowercase.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Transforms string to lowercase.
|
||||
* @howToUse `expression | lowercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
||||
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -24,7 +29,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
export class LowerCasePipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
if (isBlank(value)) return value;
|
||||
if (!isString(value)) {
|
||||
if (typeof value !== 'string') {
|
||||
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
||||
}
|
||||
return value.toLowerCase();
|
||||
|
@ -9,21 +9,23 @@
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||
|
||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, isBlank, isNumber, isPresent, isString} from '../facade/lang';
|
||||
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||
|
||||
function formatNumber(
|
||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||
if (isBlank(value)) return null;
|
||||
|
||||
// Convert strings to numbers
|
||||
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
|
||||
if (!isNumber(value)) {
|
||||
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
||||
if (typeof value !== 'number') {
|
||||
throw new InvalidPipeArgumentError(pipe, value);
|
||||
}
|
||||
|
||||
let minInt: number;
|
||||
let minFraction: number;
|
||||
let maxFraction: number;
|
||||
@ -34,8 +36,8 @@ function formatNumber(
|
||||
maxFraction = 3;
|
||||
}
|
||||
|
||||
if (isPresent(digits)) {
|
||||
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (digits) {
|
||||
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
@ -49,41 +51,40 @@ function formatNumber(
|
||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
|
||||
return NumberFormatter.format(value as number, locale, style, {
|
||||
minimumIntegerDigits: minInt,
|
||||
minimumFractionDigits: minFraction,
|
||||
maximumFractionDigits: maxFraction,
|
||||
currency: currency,
|
||||
currencyAsSymbol: currencyAsSymbol
|
||||
currencyAsSymbol: currencyAsSymbol,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number according to locale rules.
|
||||
* @howToUse `number_expression | number[:digitInfo]`
|
||||
*
|
||||
* Formats a number as local text. i.e. group sizing and separator and other locale-specific
|
||||
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||
* configurations are based on the active locale.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | number[:digitInfo]
|
||||
*
|
||||
* where `expression` is a number and `digitInfo` has the following format:
|
||||
*
|
||||
* {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
|
||||
*
|
||||
* - minIntegerDigits is the minimum number of integer digits to use. Defaults to 1.
|
||||
* - minFractionDigits is the minimum number of digits after fraction. Defaults to 0.
|
||||
* - maxFractionDigits is the maximum number of digits after fraction. Defaults to 3.
|
||||
* where `expression` is a number:
|
||||
* - `digitInfo` is a `string` which has a following format: <br>
|
||||
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
||||
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
||||
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||
*
|
||||
* For more information on the acceptable range for each of these numbers and other
|
||||
* details see your native internationalization library.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
|
||||
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -97,21 +98,22 @@ export class DecimalPipe implements PipeTransform {
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||
* @howToUse `number_expression | percent[:digitInfo]`
|
||||
*
|
||||
* Formats a number as local percent.
|
||||
* @description
|
||||
*
|
||||
* ### Usage
|
||||
* Formats a number as percentage.
|
||||
*
|
||||
* expression | percent[:digitInfo]
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
|
||||
* {@example common/pipes/ts/number_pipe.ts region='PercentPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -125,26 +127,26 @@ export class PercentPipe implements PipeTransform {
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as currency using locale rules.
|
||||
* @howToUse `number_expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]`
|
||||
* @description
|
||||
*
|
||||
* Use `currency` to format a number as currency.
|
||||
*
|
||||
* Formats a number as local currency.
|
||||
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||
* as `USD` for the US dollar and `EUR` for the euro.
|
||||
* - `symbolDisplay` is a boolean indicating whether to use the currency symbol or code.
|
||||
* - `true`: use symbol (e.g. `$`).
|
||||
* - `false`(default): use code (e.g. `USD`).
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]
|
||||
*
|
||||
* where `currencyCode` is the ISO 4217 currency code, such as "USD" for the US dollar and
|
||||
* "EUR" for the euro. `symbolDisplay` is a boolean indicating whether to use the currency
|
||||
* symbol (e.g. $) or the currency code (e.g. USD) in the output. The default for this value
|
||||
* is `false`.
|
||||
* For more information about `digitInfo` see {@link DecimalPipe}.
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
|
||||
* {@example common/pipes/ts/number_pipe.ts region='CurrencyPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -7,53 +7,41 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
* Creates a new List or String containing only a subset (slice) of the
|
||||
* elements.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Creates a new List or String containing a subset (slice) of the elements.
|
||||
* @howToUse `array_or_string_expression | slice:start[:end]`
|
||||
* @description
|
||||
*
|
||||
* The starting index of the subset to return is specified by the `start` parameter.
|
||||
* Where the input expression is a `List` or `String`, and:
|
||||
* - `start`: The starting index of the subset to return.
|
||||
* - **a positive integer**: return the item at `start` index and all items after
|
||||
* in the list or string expression.
|
||||
* - **a negative integer**: return the item at `start` index from the end and all items after
|
||||
* in the list or string expression.
|
||||
* - **if positive and greater than the size of the expression**: return an empty list or string.
|
||||
* - **if negative and greater than the size of the expression**: return entire list or string.
|
||||
* - `end`: The ending index of the subset to return.
|
||||
* - **omitted**: return all items until the end.
|
||||
* - **if positive**: return all items before `end` index of the list or string.
|
||||
* - **if negative**: return all items before `end` index from the end of the list or string.
|
||||
*
|
||||
* The ending index of the subset to return is specified by the optional `end` parameter.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | slice:start[:end]
|
||||
*
|
||||
* All behavior is based on the expected behavior of the JavaScript API
|
||||
* Array.prototype.slice() and String.prototype.slice()
|
||||
*
|
||||
* Where the input expression is a [List] or [String], and `start` is:
|
||||
*
|
||||
* - **a positive integer**: return the item at _start_ index and all items after
|
||||
* in the list or string expression.
|
||||
* - **a negative integer**: return the item at _start_ index from the end and all items after
|
||||
* in the list or string expression.
|
||||
* - **`|start|` greater than the size of the expression**: return an empty list or string.
|
||||
* - **`|start|` negative greater than the size of the expression**: return entire list or
|
||||
* string expression.
|
||||
*
|
||||
* and where `end` is:
|
||||
*
|
||||
* - **omitted**: return all items until the end of the input
|
||||
* - **a positive integer**: return all items before _end_ index of the list or string
|
||||
* expression.
|
||||
* - **a negative integer**: return all items before _end_ index from the end of the list
|
||||
* or string expression.
|
||||
* All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()`
|
||||
* and `String.prototype.slice()`.
|
||||
*
|
||||
* When operating on a [List], the returned list is always a copy even when all
|
||||
* the elements are being returned.
|
||||
*
|
||||
* When operating on a blank value, returns it.
|
||||
* When operating on a blank value, the pipe returns the blank value.
|
||||
*
|
||||
* ## List Example
|
||||
*
|
||||
* This `ngFor` example:
|
||||
*
|
||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_list'}
|
||||
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'}
|
||||
*
|
||||
* produces the following:
|
||||
*
|
||||
@ -62,23 +50,22 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
*
|
||||
* ## String Examples
|
||||
*
|
||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_string'}
|
||||
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
||||
@Pipe({name: 'slice', pure: false})
|
||||
export class SlicePipe implements PipeTransform {
|
||||
transform(value: any, start: number, end: number = null): any {
|
||||
transform(value: any, start: number, end?: number): any {
|
||||
if (isBlank(value)) return value;
|
||||
|
||||
if (!this.supports(value)) {
|
||||
throw new InvalidPipeArgumentError(SlicePipe, value);
|
||||
}
|
||||
if (isString(value)) {
|
||||
return StringWrapper.slice(value, start, end);
|
||||
}
|
||||
return ListWrapper.slice(value, start, end);
|
||||
|
||||
return value.slice(start, end);
|
||||
}
|
||||
|
||||
private supports(obj: any): boolean { return isString(obj) || isArray(obj); }
|
||||
private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); }
|
||||
}
|
||||
|
@ -7,15 +7,20 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isString} from '../facade/lang';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
* Implements uppercase transforms to text.
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Transforms string to uppercase.
|
||||
* @howToUse `expression | uppercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
||||
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -23,7 +28,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
export class UpperCasePipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
if (isBlank(value)) return value;
|
||||
if (!isString(value)) {
|
||||
if (typeof value !== 'string') {
|
||||
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
||||
}
|
||||
return value.toUpperCase();
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export class AnimationOutput {
|
||||
constructor(public name: string, public phase: string, public fullPropertyName: string) {}
|
||||
}
|
||||
|
||||
import {__core_private__ as r} from '@angular/core';
|
||||
|
||||
export const isPromise: typeof r.isPromise = r.isPromise;
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {LOCALE_ID} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
||||
|
@ -8,12 +8,11 @@
|
||||
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {WrappedValue} from '@angular/core';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
|
||||
export function main() {
|
||||
@ -112,7 +111,7 @@ export function main() {
|
||||
var promise: Promise<any>;
|
||||
var ref: SpyChangeDetectorRef;
|
||||
// adds longer timers for passing tests in IE
|
||||
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
|
||||
var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||
|
||||
beforeEach(() => {
|
||||
promise = new Promise((res, rej) => {
|
||||
|
@ -8,11 +8,9 @@
|
||||
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('DatePipe', () => {
|
||||
var date: Date;
|
||||
@ -27,7 +25,7 @@ export function main() {
|
||||
// Tracking issue: https://github.com/angular/angular/issues/11187
|
||||
|
||||
beforeEach(() => {
|
||||
date = DateWrapper.create(2015, 6, 15, 9, 3, 1);
|
||||
date = new Date(2015, 5, 15, 9, 3, 1);
|
||||
pipe = new DatePipe('en-US');
|
||||
});
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {I18nPluralPipe, NgLocalization} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nPluralPipe', () => {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {I18nSelectPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nSelectPipe', () => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {LowerCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('LowerCasePipe', () => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
export function main() {
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {CommonModule, SlicePipe} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {UpperCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('UpperCasePipe', () => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref';
|
||||
import {SpyObject, proxy} from '@angular/core/testing/testing_internal';
|
||||
import {SpyObject} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export class SpyChangeDetectorRef extends SpyObject {
|
||||
constructor() {
|
||||
|
@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core';
|
||||
@Injectable()
|
||||
export class SpyLocation implements Location {
|
||||
urlChanges: string[] = [];
|
||||
/** @internal */
|
||||
private _history: LocationState[] = [new LocationState('', '')];
|
||||
/** @internal */
|
||||
private _historyIndex: number = 0;
|
||||
/** @internal */
|
||||
_subject: EventEmitter<any> = new EventEmitter();
|
||||
|
@ -36,25 +36,27 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<messagebundle>
|
||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||
</messagebundle>`;
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">desc</note>
|
||||
<note priority="1" from="meaning">meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>`;
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">desc</note>
|
||||
<note priority="1" from="meaning">meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
describe('template i18n extraction output', () => {
|
||||
const outDir = '';
|
||||
|
@ -1,10 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
entry: './test/all_spec.js',
|
||||
output: {
|
||||
filename: './all_spec.js'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js']
|
||||
},
|
||||
output: {filename: './all_spec.js'},
|
||||
resolve: {extensions: ['.js']},
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@angular/compiler-cli",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "Execute angular2 template compiler in nodejs.",
|
||||
"description": "Angular - the compiler CLI for Node.js",
|
||||
"main": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
"bin": {
|
||||
@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.3.0",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"parse5": "1.3.2",
|
||||
"parse5": "^2.2.1",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -126,7 +126,7 @@ export class CodeGenerator {
|
||||
static create(
|
||||
options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program,
|
||||
compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext,
|
||||
resourceLoader?: compiler.ResourceLoader): CodeGenerator {
|
||||
resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator {
|
||||
resourceLoader = resourceLoader || {
|
||||
get: (s: string) => {
|
||||
if (!compilerHost.fileExists(s)) {
|
||||
@ -148,10 +148,12 @@ export class CodeGenerator {
|
||||
}
|
||||
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
const reflectorHost = usePathMapping ?
|
||||
new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) :
|
||||
new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||
if (!reflectorHost) {
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
reflectorHost = usePathMapping ?
|
||||
new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) :
|
||||
new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||
}
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser =
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Query, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
|
||||
import {ReflectorReader} from './private_import_core';
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReflectorHost} from '../src/reflector_host';
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
||||
import {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
import {ListWrapper} from '@angular/facade/src/collection';
|
||||
import {isBlank} from '@angular/facade/src/lang';
|
||||
import {MetadataCollector} from '@angular/tsc-wrapped';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@ -454,7 +453,7 @@ class MockReflectorHost implements StaticReflectorHost {
|
||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
|
||||
var result = this.staticTypeCache.get(cacheKey);
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
result = new StaticSymbol(declarationFile, name, members);
|
||||
this.staticTypeCache.set(cacheKey, result);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@angular/compiler",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "Angular2 - compiler",
|
||||
"description": "Angular - the compiler library",
|
||||
"main": "bundles/compiler.umd.js",
|
||||
"module": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
|
@ -1,3 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/compiler/testing/index.js',
|
||||
@ -11,4 +18,4 @@ export default {
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/compiler/index.js',
|
||||
@ -7,9 +14,9 @@ export default {
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
'rxjs/Subject': 'Rx',
|
||||
},
|
||||
plugins: [
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -6,75 +6,26 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ANY_STATE, AnimationOutput, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||
import * as t from '../template_parser/template_ast';
|
||||
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||
|
||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||
import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser';
|
||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||
|
||||
const animationCompilationCache =
|
||||
new Map<CompileDirectiveMetadata, CompiledAnimationTriggerResult[]>();
|
||||
|
||||
export class CompiledAnimationTriggerResult {
|
||||
constructor(
|
||||
public name: string, public statesMapStatement: o.Statement,
|
||||
public statesVariableName: string, public fnStatement: o.Statement,
|
||||
public fnVariable: o.Expression) {}
|
||||
}
|
||||
|
||||
export class CompiledComponentAnimationResult {
|
||||
constructor(
|
||||
public outputs: AnimationOutput[], public triggers: CompiledAnimationTriggerResult[]) {}
|
||||
export class AnimationEntryCompileResult {
|
||||
constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
|
||||
}
|
||||
|
||||
export class AnimationCompiler {
|
||||
compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]):
|
||||
CompiledComponentAnimationResult {
|
||||
var compiledAnimations: CompiledAnimationTriggerResult[] = [];
|
||||
var groupedErrors: string[] = [];
|
||||
var triggerLookup: {[key: string]: CompiledAnimationTriggerResult} = {};
|
||||
var componentName = component.type.name;
|
||||
|
||||
component.template.animations.forEach(entry => {
|
||||
var result = parseAnimationEntry(entry);
|
||||
var triggerName = entry.name;
|
||||
if (result.errors.length > 0) {
|
||||
var errorMessage =
|
||||
`Unable to parse the animation sequence for "${triggerName}" due to the following errors:`;
|
||||
result.errors.forEach(
|
||||
(error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; });
|
||||
groupedErrors.push(errorMessage);
|
||||
}
|
||||
|
||||
if (triggerLookup[triggerName]) {
|
||||
groupedErrors.push(
|
||||
`The animation trigger "${triggerName}" has already been registered on "${componentName}"`);
|
||||
} else {
|
||||
var factoryName = `${componentName}_${entry.name}`;
|
||||
var visitor = new _AnimationBuilder(triggerName, factoryName);
|
||||
var compileResult = visitor.build(result.ast);
|
||||
compiledAnimations.push(compileResult);
|
||||
triggerLookup[entry.name] = compileResult;
|
||||
}
|
||||
compile(factoryNamePrefix: string, parsedAnimations: AnimationEntryAst[]):
|
||||
AnimationEntryCompileResult[] {
|
||||
return parsedAnimations.map(entry => {
|
||||
const factoryName = `${factoryNamePrefix}_${entry.name}`;
|
||||
const visitor = new _AnimationBuilder(entry.name, factoryName);
|
||||
return visitor.build(entry);
|
||||
});
|
||||
|
||||
var validatedProperties = _validateAnimationProperties(compiledAnimations, template);
|
||||
validatedProperties.errors.forEach(error => { groupedErrors.push(error.msg); });
|
||||
|
||||
if (groupedErrors.length > 0) {
|
||||
var errorMessageStr =
|
||||
`Animation parsing for ${component.type.name} has failed due to the following errors:`;
|
||||
groupedErrors.forEach(error => errorMessageStr += `\n- ${error}`);
|
||||
throw new Error(errorMessageStr);
|
||||
}
|
||||
|
||||
animationCompilationCache.set(component, compiledAnimations);
|
||||
return new CompiledComponentAnimationResult(validatedProperties.outputs, compiledAnimations);
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,8 +61,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
}
|
||||
|
||||
ast.styles.forEach(entry => {
|
||||
stylesArr.push(
|
||||
o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])])));
|
||||
stylesArr.push(o.literalMap(Object.keys(entry).map(key => [key, o.literal(entry[key])])));
|
||||
});
|
||||
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
@ -182,9 +132,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
visitAnimationStateDeclaration(
|
||||
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
|
||||
var flatStyles: {[key: string]: string | number} = {};
|
||||
_getStylesArray(ast).forEach(entry => {
|
||||
StringMapWrapper.forEach(entry, (value: string, key: string) => { flatStyles[key] = value; });
|
||||
});
|
||||
_getStylesArray(ast).forEach(
|
||||
entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
|
||||
context.stateMap.registerState(ast.stateName, flatStyles);
|
||||
}
|
||||
|
||||
@ -334,28 +283,27 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
statements);
|
||||
}
|
||||
|
||||
build(ast: AnimationAst): CompiledAnimationTriggerResult {
|
||||
build(ast: AnimationAst): AnimationEntryCompileResult {
|
||||
var context = new _AnimationBuilderContext();
|
||||
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
||||
var fnVariable = o.variable(this._fnVarName);
|
||||
|
||||
var lookupMap: any[] = [];
|
||||
StringMapWrapper.forEach(
|
||||
context.stateMap.states, (value: {[key: string]: string}, stateName: string) => {
|
||||
var variableValue = EMPTY_MAP;
|
||||
if (isPresent(value)) {
|
||||
let styleMap: any[] = [];
|
||||
StringMapWrapper.forEach(value, (value: string, key: string) => {
|
||||
styleMap.push([key, o.literal(value)]);
|
||||
});
|
||||
variableValue = o.literalMap(styleMap);
|
||||
}
|
||||
lookupMap.push([stateName, variableValue]);
|
||||
});
|
||||
Object.keys(context.stateMap.states).forEach(stateName => {
|
||||
const value = context.stateMap.states[stateName];
|
||||
var variableValue = EMPTY_MAP;
|
||||
if (isPresent(value)) {
|
||||
let styleMap: any[] = [];
|
||||
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
|
||||
variableValue = o.literalMap(styleMap);
|
||||
}
|
||||
lookupMap.push([stateName, variableValue]);
|
||||
});
|
||||
|
||||
var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
|
||||
return new CompiledAnimationTriggerResult(
|
||||
this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable);
|
||||
const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
|
||||
const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
|
||||
|
||||
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,7 +319,7 @@ class _AnimationBuilderStateMap {
|
||||
get states() { return this._states; }
|
||||
registerState(name: string, value: {[prop: string]: string | number} = null): void {
|
||||
var existingEntry = this._states[name];
|
||||
if (isBlank(existingEntry)) {
|
||||
if (!existingEntry) {
|
||||
this._states[name] = value;
|
||||
}
|
||||
}
|
||||
@ -397,7 +345,7 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
|
||||
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
|
||||
var styles1 = _getStylesArray(step.keyframes[0])[0];
|
||||
var styles2 = _getStylesArray(step.keyframes[1])[0];
|
||||
return StringMapWrapper.isEmpty(styles1) && StringMapWrapper.isEmpty(styles2);
|
||||
return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -405,99 +353,3 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
|
||||
function _getStylesArray(obj: any): {[key: string]: any}[] {
|
||||
return obj.styles.styles;
|
||||
}
|
||||
|
||||
function _validateAnimationProperties(
|
||||
compiledAnimations: CompiledAnimationTriggerResult[],
|
||||
template: t.TemplateAst[]): AnimationPropertyValidationOutput {
|
||||
var visitor = new _AnimationTemplatePropertyVisitor(compiledAnimations);
|
||||
t.templateVisitAll(visitor, template);
|
||||
return new AnimationPropertyValidationOutput(visitor.outputs, visitor.errors);
|
||||
}
|
||||
|
||||
export class AnimationPropertyValidationOutput {
|
||||
constructor(public outputs: AnimationOutput[], public errors: AnimationParseError[]) {}
|
||||
}
|
||||
|
||||
class _AnimationTemplatePropertyVisitor implements t.TemplateAstVisitor {
|
||||
private _animationRegistry: {[key: string]: boolean};
|
||||
public errors: AnimationParseError[] = [];
|
||||
public outputs: AnimationOutput[] = [];
|
||||
|
||||
constructor(animations: CompiledAnimationTriggerResult[]) {
|
||||
this._animationRegistry = this._buildCompileAnimationLookup(animations);
|
||||
}
|
||||
|
||||
private _buildCompileAnimationLookup(animations: CompiledAnimationTriggerResult[]):
|
||||
{[key: string]: boolean} {
|
||||
var map: {[key: string]: boolean} = {};
|
||||
animations.forEach(entry => { map[entry.name] = true; });
|
||||
return map;
|
||||
}
|
||||
|
||||
private _validateAnimationInputOutputPairs(
|
||||
inputAsts: t.BoundElementPropertyAst[], outputAsts: t.BoundEventAst[],
|
||||
animationRegistry: {[key: string]: any}, isHostLevel: boolean): void {
|
||||
var detectedAnimationInputs: {[key: string]: boolean} = {};
|
||||
inputAsts.forEach(input => {
|
||||
if (input.type == t.PropertyBindingType.Animation) {
|
||||
var triggerName = input.name;
|
||||
if (isPresent(animationRegistry[triggerName])) {
|
||||
detectedAnimationInputs[triggerName] = true;
|
||||
} else {
|
||||
this.errors.push(
|
||||
new AnimationParseError(`Couldn't find an animation entry for ${triggerName}`));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
outputAsts.forEach(output => {
|
||||
if (output.name[0] == '@') {
|
||||
var normalizedOutputData = parseAnimationOutputName(output.name.substr(1), this.errors);
|
||||
let triggerName = normalizedOutputData.name;
|
||||
let triggerEventPhase = normalizedOutputData.phase;
|
||||
if (!animationRegistry[triggerName]) {
|
||||
this.errors.push(new AnimationParseError(
|
||||
`Couldn't find the corresponding ${isHostLevel ? 'host-level ' : '' }animation trigger definition for (@${triggerName})`));
|
||||
} else if (!detectedAnimationInputs[triggerName]) {
|
||||
this.errors.push(new AnimationParseError(
|
||||
`Unable to listen on (@${triggerName}.${triggerEventPhase}) because the animation trigger [@${triggerName}] isn't being used on the same element`));
|
||||
} else {
|
||||
this.outputs.push(normalizedOutputData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
visitElement(ast: t.ElementAst, ctx: any): any {
|
||||
this._validateAnimationInputOutputPairs(
|
||||
ast.inputs, ast.outputs, this._animationRegistry, false);
|
||||
|
||||
var componentOnElement: t.DirectiveAst =
|
||||
ast.directives.find(directive => directive.directive.isComponent);
|
||||
if (componentOnElement) {
|
||||
let cachedComponentAnimations = animationCompilationCache.get(componentOnElement.directive);
|
||||
if (cachedComponentAnimations) {
|
||||
this._validateAnimationInputOutputPairs(
|
||||
componentOnElement.hostProperties, componentOnElement.hostEvents,
|
||||
this._buildCompileAnimationLookup(cachedComponentAnimations), true);
|
||||
}
|
||||
}
|
||||
|
||||
t.templateVisitAll(this, ast.children);
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst, ctx: any): any {
|
||||
t.templateVisitAll(this, ast.children);
|
||||
}
|
||||
|
||||
visitEvent(ast: t.BoundEventAst, ctx: any): any {}
|
||||
visitBoundText(ast: t.BoundTextAst, ctx: any): any {}
|
||||
visitText(ast: t.TextAst, ctx: any): any {}
|
||||
visitNgContent(ast: t.NgContentAst, ctx: any): any {}
|
||||
visitAttr(ast: t.AttrAst, ctx: any): any {}
|
||||
visitDirective(ast: t.DirectiveAst, ctx: any): any {}
|
||||
visitReference(ast: t.ReferenceAst, ctx: any): any {}
|
||||
visitVariable(ast: t.VariableAst, ctx: any): any {}
|
||||
visitDirectiveProperty(ast: t.BoundDirectivePropertyAst, ctx: any): any {}
|
||||
visitElementProperty(ast: t.BoundElementPropertyAst, ctx: any): any {}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata} from '../compile_metadata';
|
||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang';
|
||||
import {Math} from '../facade/math';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||
|
||||
import {ANY_STATE, AnimationOutput, FILL_STYLE_FLAG} from '../private_import_core';
|
||||
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
||||
import {StylesCollection} from './styles_collection';
|
||||
|
||||
@ -20,73 +20,85 @@ const _INITIAL_KEYFRAME = 0;
|
||||
const _TERMINAL_KEYFRAME = 1;
|
||||
const _ONE_SECOND = 1000;
|
||||
|
||||
declare type Styles = {
|
||||
[key: string]: string | number
|
||||
};
|
||||
|
||||
export class AnimationParseError extends ParseError {
|
||||
constructor(message: any /** TODO #9100 */) { super(null, message); }
|
||||
constructor(message: string) { super(null, message); }
|
||||
toString(): string { return `${this.msg}`; }
|
||||
}
|
||||
|
||||
export class ParsedAnimationResult {
|
||||
export class AnimationEntryParseResult {
|
||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||
}
|
||||
|
||||
export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult {
|
||||
var errors: AnimationParseError[] = [];
|
||||
var stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||
var transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||
export class AnimationParser {
|
||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||
const errors: string[] = [];
|
||||
const componentName = component.type.name;
|
||||
const animationTriggerNames = new Set<string>();
|
||||
const asts = component.template.animations.map(entry => {
|
||||
const result = this.parseEntry(entry);
|
||||
const ast = result.ast;
|
||||
const triggerName = ast.name;
|
||||
if (animationTriggerNames.has(triggerName)) {
|
||||
result.errors.push(new AnimationParseError(
|
||||
`The animation trigger "${triggerName}" has already been registered for the ${componentName} component`));
|
||||
} else {
|
||||
animationTriggerNames.add(triggerName);
|
||||
}
|
||||
if (result.errors.length > 0) {
|
||||
let errorMessage =
|
||||
`- Unable to parse the animation sequence for "${triggerName}" on the ${componentName} component due to the following errors:`;
|
||||
result.errors.forEach(
|
||||
(error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; });
|
||||
errors.push(errorMessage);
|
||||
}
|
||||
return ast;
|
||||
});
|
||||
|
||||
var stateDeclarationAsts: any[] /** TODO #9100 */ = [];
|
||||
entry.definitions.forEach(def => {
|
||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
||||
stateDeclarationAsts.push(ast);
|
||||
stateStyles[ast.stateName] = ast.styles;
|
||||
});
|
||||
} else {
|
||||
transitions.push(<CompileAnimationStateTransitionMetadata>def);
|
||||
if (errors.length > 0) {
|
||||
const errorString = errors.join('\n');
|
||||
throw new Error(`Animation parse errors:\n${errorString}`);
|
||||
}
|
||||
});
|
||||
|
||||
var stateTransitionAsts =
|
||||
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
|
||||
|
||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
return new ParsedAnimationResult(ast, errors);
|
||||
}
|
||||
|
||||
export function parseAnimationOutputName(
|
||||
outputName: string, errors: AnimationParseError[]): AnimationOutput {
|
||||
var values = outputName.split('.');
|
||||
var name: string;
|
||||
var phase: string = '';
|
||||
if (values.length > 1) {
|
||||
name = values[0];
|
||||
let parsedPhase = values[1];
|
||||
switch (parsedPhase) {
|
||||
case 'start':
|
||||
case 'done':
|
||||
phase = parsedPhase;
|
||||
break;
|
||||
|
||||
default:
|
||||
errors.push(new AnimationParseError(
|
||||
`The provided animation output phase value "${parsedPhase}" for "@${name}" is not supported (use start or done)`));
|
||||
}
|
||||
} else {
|
||||
name = outputName;
|
||||
errors.push(new AnimationParseError(
|
||||
`The animation trigger output event (@${name}) is missing its phase value name (start or done are currently supported)`));
|
||||
return asts;
|
||||
}
|
||||
|
||||
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
|
||||
var errors: AnimationParseError[] = [];
|
||||
var stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||
var transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||
|
||||
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
||||
entry.definitions.forEach(def => {
|
||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
||||
stateDeclarationAsts.push(ast);
|
||||
stateStyles[ast.stateName] = ast.styles;
|
||||
});
|
||||
} else {
|
||||
transitions.push(<CompileAnimationStateTransitionMetadata>def);
|
||||
}
|
||||
});
|
||||
|
||||
var stateTransitionAsts =
|
||||
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
|
||||
|
||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
return new AnimationEntryParseResult(ast, errors);
|
||||
}
|
||||
return new AnimationOutput(name, phase, outputName);
|
||||
}
|
||||
|
||||
function _parseAnimationDeclarationStates(
|
||||
stateMetadata: CompileAnimationStateDeclarationMetadata,
|
||||
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
||||
var styleValues: {[key: string]: string | number}[] = [];
|
||||
var styleValues: Styles[] = [];
|
||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
||||
// TODO (matsko): change this when we get CSS class integration support
|
||||
if (isStringMap(stylesEntry)) {
|
||||
styleValues.push(<{[key: string]: string | number}>stylesEntry);
|
||||
styleValues.push(stylesEntry as Styles);
|
||||
} else {
|
||||
errors.push(new AnimationParseError(
|
||||
`State based animations cannot contain references to other states`));
|
||||
@ -145,16 +157,6 @@ function _parseAnimationTransitionExpr(
|
||||
return expressions;
|
||||
}
|
||||
|
||||
function _fetchSylesFromState(stateName: string, stateStyles: {[key: string]: AnimationStylesAst}):
|
||||
CompileAnimationStyleMetadata {
|
||||
var entry = stateStyles[stateName];
|
||||
if (isPresent(entry)) {
|
||||
var styles = <{[key: string]: string | number}[]>entry.styles;
|
||||
return new CompileAnimationStyleMetadata(0, styles);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
|
||||
CompileAnimationMetadata {
|
||||
return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) :
|
||||
@ -210,7 +212,7 @@ function _normalizeStyleStepEntry(
|
||||
}
|
||||
|
||||
var newSteps: CompileAnimationMetadata[] = [];
|
||||
var combinedStyles: {[key: string]: string | number}[];
|
||||
var combinedStyles: Styles[];
|
||||
steps.forEach(step => {
|
||||
if (step instanceof CompileAnimationStyleMetadata) {
|
||||
// this occurs when a style step is followed by a previous style step
|
||||
@ -266,7 +268,7 @@ function _normalizeStyleStepEntry(
|
||||
function _resolveStylesFromState(
|
||||
stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]) {
|
||||
var styles: {[key: string]: string | number}[] = [];
|
||||
var styles: Styles[] = [];
|
||||
if (stateName[0] != ':') {
|
||||
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
|
||||
} else {
|
||||
@ -278,7 +280,7 @@ function _resolveStylesFromState(
|
||||
} else {
|
||||
value.styles.forEach(stylesEntry => {
|
||||
if (isStringMap(stylesEntry)) {
|
||||
styles.push(<{[key: string]: string | number}>stylesEntry);
|
||||
styles.push(stylesEntry as Styles);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -312,15 +314,13 @@ function _parseAnimationKeyframes(
|
||||
var lastOffset = 0;
|
||||
keyframeSequence.steps.forEach(styleMetadata => {
|
||||
var offset = styleMetadata.offset;
|
||||
var keyframeStyles: {[key: string]: string | number} = {};
|
||||
var keyframeStyles: Styles = {};
|
||||
styleMetadata.styles.forEach(entry => {
|
||||
StringMapWrapper.forEach(
|
||||
<{[key: string]: string | number}>entry,
|
||||
(value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
||||
if (prop != 'offset') {
|
||||
keyframeStyles[prop] = value;
|
||||
}
|
||||
});
|
||||
Object.keys(entry).forEach(prop => {
|
||||
if (prop != 'offset') {
|
||||
keyframeStyles[prop] = (entry as Styles)[prop];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (isPresent(offset)) {
|
||||
@ -357,24 +357,22 @@ function _parseAnimationKeyframes(
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
|
||||
StringMapWrapper.forEach(
|
||||
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
||||
if (!isPresent(firstKeyframeStyles[prop])) {
|
||||
firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
|
||||
}
|
||||
});
|
||||
Object.keys(styles).forEach(prop => {
|
||||
if (!isPresent(firstKeyframeStyles[prop])) {
|
||||
firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (i = limit - 1; i >= 0; i--) {
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
|
||||
StringMapWrapper.forEach(
|
||||
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
||||
if (!isPresent(lastKeyframeStyles[prop])) {
|
||||
lastKeyframeStyles[prop] = value;
|
||||
}
|
||||
});
|
||||
Object.keys(styles).forEach(prop => {
|
||||
if (!isPresent(lastKeyframeStyles[prop])) {
|
||||
lastKeyframeStyles[prop] = styles[prop];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return rawKeyframes.map(
|
||||
@ -398,11 +396,9 @@ function _parseTransitionAnimation(
|
||||
if (entry instanceof CompileAnimationStyleMetadata) {
|
||||
entry.styles.forEach(stylesEntry => {
|
||||
// by this point we know that we only have stringmap values
|
||||
var map = <{[key: string]: string | number}>stylesEntry;
|
||||
StringMapWrapper.forEach(
|
||||
map, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
||||
collectedStyles.insertAtTime(prop, time, value);
|
||||
});
|
||||
var map = stylesEntry as Styles;
|
||||
Object.keys(map).forEach(
|
||||
prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
|
||||
});
|
||||
previousStyles = entry.styles;
|
||||
return;
|
||||
@ -448,7 +444,7 @@ function _parseTransitionAnimation(
|
||||
} else {
|
||||
let styleData = <CompileAnimationStyleMetadata>styles;
|
||||
let offset = _TERMINAL_KEYFRAME;
|
||||
let styleAst = new AnimationStylesAst(<{[key: string]: string | number}[]>styleData.styles);
|
||||
let styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
|
||||
var keyframe = new AnimationKeyframeAst(offset, styleAst);
|
||||
keyframes = [keyframe];
|
||||
}
|
||||
@ -460,9 +456,8 @@ function _parseTransitionAnimation(
|
||||
|
||||
keyframes.forEach(
|
||||
(keyframe: any /** TODO #9100 */) => keyframe.styles.styles.forEach(
|
||||
(entry: any /** TODO #9100 */) => StringMapWrapper.forEach(
|
||||
entry, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) =>
|
||||
collectedStyles.insertAtTime(prop, currentTime, value))));
|
||||
(entry: any /** TODO #9100 */) => Object.keys(entry).forEach(
|
||||
prop => { collectedStyles.insertAtTime(prop, currentTime, entry[prop]); })));
|
||||
} else {
|
||||
// if the code reaches this stage then an error
|
||||
// has already been populated within the _normalizeStyleSteps()
|
||||
@ -535,10 +530,11 @@ function _parseTimeExpression(
|
||||
function _createStartKeyframeFromEndKeyframe(
|
||||
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
|
||||
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
|
||||
var values: {[key: string]: string | number} = {};
|
||||
var values: Styles = {};
|
||||
var endTime = startTime + duration;
|
||||
endKeyframe.styles.styles.forEach((styleData: {[key: string]: string | number}) => {
|
||||
StringMapWrapper.forEach(styleData, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
||||
endKeyframe.styles.styles.forEach((styleData: Styles) => {
|
||||
Object.keys(styleData).forEach(prop => {
|
||||
const val = styleData[prop];
|
||||
if (prop == 'offset') return;
|
||||
|
||||
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
||||
|
@ -8,11 +8,10 @@
|
||||
|
||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from './facade/collection';
|
||||
import {isBlank, isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang';
|
||||
import {LifecycleHooks, reflector} from './private_import_core';
|
||||
import {ListWrapper, MapWrapper} from './facade/collection';
|
||||
import {isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang';
|
||||
import {LifecycleHooks} from './private_import_core';
|
||||
import {CssSelector} from './selector';
|
||||
import {getUrlScheme} from './url_resolver';
|
||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
||||
|
||||
function unimplemented(): any {
|
||||
@ -343,7 +342,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
var hostProperties: {[key: string]: string} = {};
|
||||
var hostAttributes: {[key: string]: string} = {};
|
||||
if (isPresent(host)) {
|
||||
StringMapWrapper.forEach(host, (value: string, key: string) => {
|
||||
Object.keys(host).forEach(key => {
|
||||
const value = host[key];
|
||||
const matches = key.match(HOST_REG_EXP);
|
||||
if (matches === null) {
|
||||
hostAttributes[key] = value;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {COMPILER_OPTIONS, ClassProvider, Compiler, CompilerFactory, CompilerOptions, Component, ExistingProvider, FactoryProvider, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, TypeProvider, ValueProvider, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||
|
||||
export * from './template_parser/template_ast';
|
||||
export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser';
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import * as chars from '../chars';
|
||||
import {BaseError} from '../facade/errors';
|
||||
import {StringWrapper, isPresent, resolveEnumToken} from '../facade/lang';
|
||||
import {StringWrapper, isPresent} from '../facade/lang';
|
||||
|
||||
export enum CssTokenType {
|
||||
EOF,
|
||||
@ -223,8 +223,8 @@ export class CssScanner {
|
||||
|
||||
var error: CssScannerError = null;
|
||||
if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
|
||||
var errorMessage = resolveEnumToken(CssTokenType, next.type) + ' does not match expected ' +
|
||||
resolveEnumToken(CssTokenType, type) + ' value';
|
||||
var errorMessage =
|
||||
CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value';
|
||||
|
||||
if (isPresent(value)) {
|
||||
errorMessage += ' ("' + next.strValue + '" should match "' + value + '")';
|
||||
|
@ -115,27 +115,27 @@ export class DirectiveNormalizer {
|
||||
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||
|
||||
const allStyles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
||||
const allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
||||
|
||||
let encapsulation = templateMeta.encapsulation;
|
||||
if (isBlank(encapsulation)) {
|
||||
encapsulation = this._config.defaultEncapsulation;
|
||||
}
|
||||
if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 &&
|
||||
allStyleUrls.length === 0) {
|
||||
|
||||
const styles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
||||
const styleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
||||
|
||||
if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 &&
|
||||
styleUrls.length === 0) {
|
||||
encapsulation = ViewEncapsulation.None;
|
||||
}
|
||||
|
||||
return new CompileTemplateMetadata({
|
||||
encapsulation,
|
||||
template: template,
|
||||
templateUrl: templateAbsUrl,
|
||||
styles: allStyles,
|
||||
styleUrls: allStyleUrls,
|
||||
template,
|
||||
templateUrl: templateAbsUrl, styles, styleUrls,
|
||||
externalStylesheets: templateMeta.externalStylesheets,
|
||||
ngContentSelectors: visitor.ngContentSelectors,
|
||||
animations: templateMeta.animations,
|
||||
interpolation: templateMeta.interpolation
|
||||
interpolation: templateMeta.interpolation,
|
||||
});
|
||||
}
|
||||
|
||||
@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate(
|
||||
viewProviders: directive.viewProviders,
|
||||
queries: directive.queries,
|
||||
viewQueries: directive.viewQueries,
|
||||
entryComponents: directive.entryComponents,
|
||||
template: template
|
||||
entryComponents: directive.entryComponents, template,
|
||||
});
|
||||
}
|
||||
|
@ -9,14 +9,10 @@
|
||||
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper} from './facade/collection';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {stringify} from './facade/lang';
|
||||
import {ReflectorReader, reflector} from './private_import_core';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
function _isDirectiveMetadata(type: any): type is Directive {
|
||||
return type instanceof Directive;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve a `Type` for {@link Directive}.
|
||||
*
|
||||
@ -32,54 +28,57 @@ export class DirectiveResolver {
|
||||
* Return {@link Directive} for a given `Type`.
|
||||
*/
|
||||
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
||||
var typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (isPresent(typeMetadata)) {
|
||||
var metadata = typeMetadata.find(_isDirectiveMetadata);
|
||||
if (isPresent(metadata)) {
|
||||
var propertyMetadata = this._reflector.propMetadata(type);
|
||||
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (typeMetadata) {
|
||||
const metadata = typeMetadata.find(isDirectiveMetadata);
|
||||
if (metadata) {
|
||||
const propertyMetadata = this._reflector.propMetadata(type);
|
||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
||||
}
|
||||
}
|
||||
|
||||
if (throwIfNotFound) {
|
||||
throw new Error(`No Directive annotation found on ${stringify(type)}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _mergeWithPropertyMetadata(
|
||||
dm: Directive, propertyMetadata: {[key: string]: any[]},
|
||||
directiveType: Type<any>): Directive {
|
||||
var inputs: string[] = [];
|
||||
var outputs: string[] = [];
|
||||
var host: {[key: string]: string} = {};
|
||||
var queries: {[key: string]: any} = {};
|
||||
const inputs: string[] = [];
|
||||
const outputs: string[] = [];
|
||||
const host: {[key: string]: string} = {};
|
||||
const queries: {[key: string]: any} = {};
|
||||
|
||||
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => {
|
||||
metadata.forEach(a => {
|
||||
Object.keys(propertyMetadata).forEach((propName: string) => {
|
||||
|
||||
propertyMetadata[propName].forEach(a => {
|
||||
if (a instanceof Input) {
|
||||
if (isPresent(a.bindingPropertyName)) {
|
||||
if (a.bindingPropertyName) {
|
||||
inputs.push(`${propName}: ${a.bindingPropertyName}`);
|
||||
} else {
|
||||
inputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof Output) {
|
||||
const output: Output = a;
|
||||
if (isPresent(output.bindingPropertyName)) {
|
||||
if (output.bindingPropertyName) {
|
||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||
} else {
|
||||
outputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof HostBinding) {
|
||||
const hostBinding: HostBinding = a;
|
||||
if (isPresent(hostBinding.hostPropertyName)) {
|
||||
if (hostBinding.hostPropertyName) {
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
} else if (a instanceof HostListener) {
|
||||
const hostListener: HostListener = a;
|
||||
var args = isPresent(hostListener.args) ? (<any[]>hostListener.args).join(', ') : '';
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args})`;
|
||||
const args = hostListener.args || [];
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||
} else if (a instanceof Query) {
|
||||
queries[propName] = a;
|
||||
}
|
||||
@ -91,13 +90,14 @@ export class DirectiveResolver {
|
||||
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
|
||||
|
||||
private _merge(
|
||||
dm: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
||||
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
||||
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
||||
let mergedInputs: string[];
|
||||
const mergedInputs: string[] = inputs;
|
||||
|
||||
if (isPresent(dm.inputs)) {
|
||||
if (directive.inputs) {
|
||||
const inputNames: string[] =
|
||||
dm.inputs.map((def: string): string => this._extractPublicName(def));
|
||||
directive.inputs.map((def: string): string => this._extractPublicName(def));
|
||||
|
||||
inputs.forEach((inputDef: string) => {
|
||||
const publicName = this._extractPublicName(inputDef);
|
||||
if (inputNames.indexOf(publicName) > -1) {
|
||||
@ -105,16 +105,15 @@ export class DirectiveResolver {
|
||||
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
||||
}
|
||||
});
|
||||
mergedInputs = dm.inputs.concat(inputs);
|
||||
} else {
|
||||
mergedInputs = inputs;
|
||||
|
||||
mergedInputs.unshift(...directive.inputs);
|
||||
}
|
||||
|
||||
let mergedOutputs: string[];
|
||||
let mergedOutputs: string[] = outputs;
|
||||
|
||||
if (isPresent(dm.outputs)) {
|
||||
if (directive.outputs) {
|
||||
const outputNames: string[] =
|
||||
dm.outputs.map((def: string): string => this._extractPublicName(def));
|
||||
directive.outputs.map((def: string): string => this._extractPublicName(def));
|
||||
|
||||
outputs.forEach((outputDef: string) => {
|
||||
const publicName = this._extractPublicName(outputDef);
|
||||
@ -123,47 +122,48 @@ export class DirectiveResolver {
|
||||
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
||||
}
|
||||
});
|
||||
mergedOutputs = dm.outputs.concat(outputs);
|
||||
} else {
|
||||
mergedOutputs = outputs;
|
||||
mergedOutputs.unshift(...directive.outputs);
|
||||
}
|
||||
|
||||
var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host;
|
||||
var mergedQueries =
|
||||
isPresent(dm.queries) ? StringMapWrapper.merge(dm.queries, queries) : queries;
|
||||
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
||||
const mergedQueries =
|
||||
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
||||
|
||||
if (dm instanceof Component) {
|
||||
if (directive instanceof Component) {
|
||||
return new Component({
|
||||
selector: dm.selector,
|
||||
selector: directive.selector,
|
||||
inputs: mergedInputs,
|
||||
outputs: mergedOutputs,
|
||||
host: mergedHost,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
exportAs: directive.exportAs,
|
||||
moduleId: directive.moduleId,
|
||||
queries: mergedQueries,
|
||||
changeDetection: dm.changeDetection,
|
||||
providers: dm.providers,
|
||||
viewProviders: dm.viewProviders,
|
||||
entryComponents: dm.entryComponents,
|
||||
template: dm.template,
|
||||
templateUrl: dm.templateUrl,
|
||||
styles: dm.styles,
|
||||
styleUrls: dm.styleUrls,
|
||||
encapsulation: dm.encapsulation,
|
||||
animations: dm.animations,
|
||||
interpolation: dm.interpolation
|
||||
changeDetection: directive.changeDetection,
|
||||
providers: directive.providers,
|
||||
viewProviders: directive.viewProviders,
|
||||
entryComponents: directive.entryComponents,
|
||||
template: directive.template,
|
||||
templateUrl: directive.templateUrl,
|
||||
styles: directive.styles,
|
||||
styleUrls: directive.styleUrls,
|
||||
encapsulation: directive.encapsulation,
|
||||
animations: directive.animations,
|
||||
interpolation: directive.interpolation
|
||||
});
|
||||
|
||||
} else {
|
||||
return new Directive({
|
||||
selector: dm.selector,
|
||||
selector: directive.selector,
|
||||
inputs: mergedInputs,
|
||||
outputs: mergedOutputs,
|
||||
host: mergedHost,
|
||||
exportAs: dm.exportAs,
|
||||
exportAs: directive.exportAs,
|
||||
queries: mergedQueries,
|
||||
providers: dm.providers
|
||||
providers: directive.providers
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isDirectiveMetadata(type: any): type is Directive {
|
||||
return type instanceof Directive;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
|
||||
import {isBlank} from '../facade/lang';
|
||||
|
||||
export class ParserError {
|
||||
@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor {
|
||||
}
|
||||
|
||||
visitAll(asts: any[]): any[] {
|
||||
var res = ListWrapper.createFixedSize(asts.length);
|
||||
var res = new Array(asts.length);
|
||||
for (var i = 0; i < asts.length; ++i) {
|
||||
res[i] = asts[i].visit(this);
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ const _PLACEHOLDER_TAG = 'x';
|
||||
const _SOURCE_TAG = 'source';
|
||||
const _TARGET_TAG = 'target';
|
||||
const _UNIT_TAG = 'trans-unit';
|
||||
const _CR = (ws: number = 0) => new xml.Text(`\n${new Array(ws).join(' ')}`);
|
||||
|
||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
||||
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
||||
@ -44,34 +43,37 @@ export class Xliff implements Serializer {
|
||||
|
||||
let transUnit = new xml.Tag(_UNIT_TAG, {id: id, datatype: 'html'});
|
||||
transUnit.children.push(
|
||||
_CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), _CR(8),
|
||||
new xml.Tag(_TARGET_TAG));
|
||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||
new xml.CR(8), new xml.Tag(_TARGET_TAG));
|
||||
|
||||
if (message.description) {
|
||||
transUnit.children.push(
|
||||
_CR(8),
|
||||
new xml.CR(8),
|
||||
new xml.Tag(
|
||||
'note', {priority: '1', from: 'description'}, [new xml.Text(message.description)]));
|
||||
}
|
||||
|
||||
if (message.meaning) {
|
||||
transUnit.children.push(
|
||||
_CR(8),
|
||||
new xml.CR(8),
|
||||
new xml.Tag('note', {priority: '1', from: 'meaning'}, [new xml.Text(message.meaning)]));
|
||||
}
|
||||
|
||||
transUnit.children.push(_CR(6));
|
||||
transUnit.children.push(new xml.CR(6));
|
||||
|
||||
transUnits.push(_CR(6), transUnit);
|
||||
transUnits.push(new xml.CR(6), transUnit);
|
||||
});
|
||||
|
||||
const body = new xml.Tag('body', {}, [...transUnits, _CR(4)]);
|
||||
const body = new xml.Tag('body', {}, [...transUnits, new xml.CR(4)]);
|
||||
const file = new xml.Tag(
|
||||
'file', {'source-language': _SOURCE_LANG, datatype: 'plaintext', original: 'ng2.template'},
|
||||
[_CR(4), body, _CR(2)]);
|
||||
const xliff = new xml.Tag('xliff', {version: _VERSION, xmlns: _XMLNS}, [_CR(2), file, _CR()]);
|
||||
[new xml.CR(4), body, new xml.CR(2)]);
|
||||
const xliff = new xml.Tag(
|
||||
'xliff', {version: _VERSION, xmlns: _XMLNS}, [new xml.CR(2), file, new xml.CR()]);
|
||||
|
||||
return xml.serialize([new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), _CR(), xliff]);
|
||||
return xml.serialize([
|
||||
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), new xml.CR(), xliff, new xml.CR()
|
||||
]);
|
||||
}
|
||||
|
||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} {
|
||||
@ -137,13 +139,15 @@ class _WriteVisitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
|
||||
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype: ph.tag});
|
||||
const ctype = getCtypeForTag(ph.tag);
|
||||
|
||||
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype});
|
||||
if (ph.isVoid) {
|
||||
// void tags have no children nor closing tags
|
||||
return [startTagPh];
|
||||
}
|
||||
|
||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype: ph.tag});
|
||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype});
|
||||
|
||||
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
||||
}
|
||||
@ -287,3 +291,14 @@ class _LoadVisitor implements ml.Visitor {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
}
|
||||
}
|
||||
|
||||
function getCtypeForTag(tag: string): string {
|
||||
switch (tag.toLowerCase()) {
|
||||
case 'br':
|
||||
return 'lb';
|
||||
case 'img':
|
||||
return 'image';
|
||||
default:
|
||||
return `x-${tag}`;
|
||||
}
|
||||
}
|
@ -43,7 +43,6 @@ export class Xmb implements Serializer {
|
||||
write(messageMap: {[k: string]: i18n.Message}): string {
|
||||
const visitor = new _Visitor();
|
||||
let rootNode = new xml.Tag(_MESSAGES_TAG);
|
||||
rootNode.children.push(new xml.Text('\n'));
|
||||
|
||||
Object.keys(messageMap).forEach((id) => {
|
||||
const message = messageMap[id];
|
||||
@ -58,16 +57,18 @@ export class Xmb implements Serializer {
|
||||
}
|
||||
|
||||
rootNode.children.push(
|
||||
new xml.Text(' '), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)),
|
||||
new xml.Text('\n'));
|
||||
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
|
||||
});
|
||||
|
||||
rootNode.children.push(new xml.CR());
|
||||
|
||||
return xml.serialize([
|
||||
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}),
|
||||
new xml.Text('\n'),
|
||||
new xml.CR(),
|
||||
new xml.Doctype(_MESSAGES_TAG, _DOCTYPE),
|
||||
new xml.Text('\n'),
|
||||
new xml.CR(),
|
||||
rootNode,
|
||||
new xml.CR(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,10 @@ export class Text implements Node {
|
||||
visit(visitor: IVisitor): any { return visitor.visitText(this); }
|
||||
}
|
||||
|
||||
export class CR extends Text {
|
||||
constructor(ws: number = 0) { super(`\n${new Array(ws + 1).join(' ')}`); }
|
||||
}
|
||||
|
||||
const _ESCAPED_CHARS: [RegExp, string][] = [
|
||||
[/&/g, '&'],
|
||||
[/"/g, '"'],
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationOutput, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
|
||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
|
||||
import {assetUrl} from './util';
|
||||
|
||||
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||
@ -266,11 +266,6 @@ export class Identifiers {
|
||||
moduleUrl: assetUrl('core', 'i18n/tokens'),
|
||||
runtime: TRANSLATIONS_FORMAT_
|
||||
};
|
||||
static AnimationOutput: IdentifierSpec = {
|
||||
name: 'AnimationOutput',
|
||||
moduleUrl: assetUrl('core', 'animation/animation_output'),
|
||||
runtime: AnimationOutput
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveIdentifier(identifier: IdentifierSpec) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user