Compare commits
70 Commits
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 |
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` =
|
||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@ -1,3 +1,45 @@
|
||||
<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)
|
||||
|
||||
|
@ -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,7 +4,6 @@
|
||||
[](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][browserstack].*
|
||||
|
@ -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());
|
||||
}
|
||||
|
146
gulpfile.js
146
gulpfile.js
@ -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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
@ -13,12 +21,14 @@ const os = require('os');
|
||||
|
||||
// clang-format entry points
|
||||
const srcsToFmt = [
|
||||
'modules/@angular/**/*.ts',
|
||||
'modules/benchmarks/**/*.ts',
|
||||
'modules/e2e_util/**/*.ts',
|
||||
'modules/playground/**/*.ts',
|
||||
'tools/**/*.ts',
|
||||
'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)
|
||||
@ -26,15 +36,16 @@ 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 = [
|
||||
@ -62,12 +73,18 @@ const entrypoints = [
|
||||
];
|
||||
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
|
||||
@ -83,17 +100,17 @@ gulp.task('public-api:enforce', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.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();
|
||||
}
|
||||
});
|
||||
.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
|
||||
@ -101,20 +118,24 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
});
|
||||
|
||||
// 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.
|
||||
// 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}));
|
||||
return gulp
|
||||
.src([
|
||||
'modules/**/*.spec.ts',
|
||||
'modules/**/*_spec.ts',
|
||||
])
|
||||
.pipe(ddescribeIit({allowDisabledTests: true}));
|
||||
});
|
||||
|
||||
// Check the coding standards and programming errors
|
||||
@ -123,14 +144,21 @@ gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||
// 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); });
|
||||
@ -142,7 +170,7 @@ gulp.task('check-cycle', (done) => {
|
||||
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, '//'); }
|
||||
});
|
||||
const circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
@ -173,11 +201,11 @@ gulp.task('serve-examples', () => {
|
||||
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()],
|
||||
});
|
||||
});
|
||||
|
||||
@ -187,16 +215,13 @@ 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) {
|
||||
@ -205,8 +230,7 @@ function tsc(projectPath, done) {
|
||||
childProcess
|
||||
.spawn(
|
||||
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
||||
['-p', path.join(__dirname, projectPath)],
|
||||
{stdio: 'inherit'})
|
||||
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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';
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {Injector, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
|
||||
export type PerfLogEvent = {
|
||||
[key: string]: any
|
||||
@ -50,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;
|
||||
|
@ -169,7 +169,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||
expectedName: string = null): boolean {
|
||||
var hasCategories = expectedCategories.reduce(
|
||||
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
|
||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||
}
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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';
|
||||
@ -35,7 +35,7 @@ export function main() {
|
||||
function createExtension(
|
||||
perfRecords: any[] = null, userAgent: string = null,
|
||||
messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
|
||||
if (isBlank(perfRecords)) {
|
||||
if (!perfRecords) {
|
||||
perfRecords = [];
|
||||
}
|
||||
if (isBlank(userAgent)) {
|
||||
@ -396,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,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',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -61,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);
|
||||
@ -70,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();
|
||||
|
@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
||||
*/
|
||||
@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) {}
|
||||
@ -69,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);
|
||||
}
|
||||
|
@ -111,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();
|
||||
@ -120,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();
|
||||
@ -141,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);
|
||||
@ -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(
|
||||
|
@ -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 {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']},
|
||||
};
|
||||
|
@ -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,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) {
|
||||
|
@ -123,7 +123,7 @@ class _TreeBuilder {
|
||||
// read =
|
||||
while (this._peek.type === lex.TokenType.EXPANSION_CASE_VALUE) {
|
||||
let expCase = this._parseExpansionCase();
|
||||
if (isBlank(expCase)) return; // error
|
||||
if (!expCase) return; // error
|
||||
cases.push(expCase);
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ class _TreeBuilder {
|
||||
const start = this._advance();
|
||||
|
||||
const exp = this._collectExpansionExpTokens(start);
|
||||
if (isBlank(exp)) return null;
|
||||
if (!exp) return null;
|
||||
|
||||
const end = this._advance();
|
||||
exp.push(new lex.Token(lex.TokenType.EOF, [], end.sourceSpan));
|
||||
|
@ -9,8 +9,8 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||
import * as o from './output/output_ast';
|
||||
import {convertValueToOutputAst} from './output/value_util';
|
||||
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
||||
@ -190,7 +190,7 @@ class _InjectorBuilder {
|
||||
resolvedProviderValueExpr = providerValueExpressions[0];
|
||||
type = providerValueExpressions[0].type;
|
||||
}
|
||||
if (isBlank(type)) {
|
||||
if (!type) {
|
||||
type = o.DYNAMIC_TYPE;
|
||||
}
|
||||
if (isEager) {
|
||||
@ -223,11 +223,11 @@ class _InjectorBuilder {
|
||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
|
||||
result = o.THIS_EXPR;
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
result = this._instances.get(dep.token.reference);
|
||||
}
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
var args = [createDiTokenExpression(dep.token)];
|
||||
if (dep.isOptional) {
|
||||
args.push(o.NULL_EXPR);
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
import {SchemaMetadata} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
||||
import {AnimationCompiler} from './animation/animation_compiler';
|
||||
import {AnimationParser} from './animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
||||
import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||
import {CompileMetadataResolver} from './metadata_resolver';
|
||||
@ -28,6 +30,9 @@ export class NgModulesSummary {
|
||||
}
|
||||
|
||||
export class OfflineCompiler {
|
||||
private _animationParser = new AnimationParser();
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
private _metadataResolver: CompileMetadataResolver,
|
||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||
@ -162,14 +167,19 @@ export class OfflineCompiler {
|
||||
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
|
||||
fileSuffix: string, targetStatements: o.Statement[]): string {
|
||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const viewResult =
|
||||
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(
|
||||
entry => { entry.statements.forEach(statement => { targetStatements.push(statement); }); });
|
||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||
return viewResult.viewFactoryVar;
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
||||
}
|
||||
|
||||
visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void {
|
||||
statements.forEach((stmt) => { return stmt.visitStatement(this, ctx); });
|
||||
statements.forEach((stmt) => stmt.visitStatement(this, ctx));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import {StringWrapper, evalExpression, isBlank, isPresent, isString} from '../facade/lang';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||
|
@ -8,9 +8,7 @@
|
||||
|
||||
|
||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, isString} from '../facade/lang';
|
||||
import {ValueTransformer, visitValue} from '../util';
|
||||
import {isPresent, isString} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
@ -21,7 +19,7 @@ export enum TypeModifier {
|
||||
|
||||
export abstract class Type {
|
||||
constructor(public modifiers: TypeModifier[] = null) {
|
||||
if (isBlank(modifiers)) {
|
||||
if (!modifiers) {
|
||||
this.modifiers = [];
|
||||
}
|
||||
}
|
||||
@ -464,7 +462,7 @@ export enum StmtModifier {
|
||||
|
||||
export abstract class Statement {
|
||||
constructor(public modifiers: StmtModifier[] = null) {
|
||||
if (isBlank(modifiers)) {
|
||||
if (!modifiers) {
|
||||
this.modifiers = [];
|
||||
}
|
||||
}
|
||||
@ -519,7 +517,7 @@ export class ReturnStatement extends Statement {
|
||||
|
||||
export class AbstractClassPart {
|
||||
constructor(public type: Type = null, public modifiers: StmtModifier[]) {
|
||||
if (isBlank(modifiers)) {
|
||||
if (!modifiers) {
|
||||
this.modifiers = [];
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {Math, isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
// asset:<package-name>/<realm>/<path-to-module>
|
||||
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/;
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
|
||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {ValueTransformer, visitValue} from '../util';
|
||||
|
||||
import * as o from './output_ast';
|
||||
@ -24,9 +23,7 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||
|
||||
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
||||
var entries: Array<string|o.Expression>[] = [];
|
||||
StringMapWrapper.forEach(map, (value: any, key: string) => {
|
||||
entries.push([key, visitValue(value, this, null)]);
|
||||
});
|
||||
Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); });
|
||||
return o.literalMap(entries, type);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ export const isDefaultChangeDetectionStrategy: typeof r.isDefaultChangeDetection
|
||||
r.isDefaultChangeDetectionStrategy;
|
||||
export type ChangeDetectorStatus = typeof r._ChangeDetectorStatus;
|
||||
export const ChangeDetectorStatus: typeof r.ChangeDetectorStatus = r.ChangeDetectorStatus;
|
||||
r.CHANGE_DETECTION_STRATEGY_VALUES;
|
||||
export type LifecycleHooks = typeof r._LifecycleHooks;
|
||||
export const LifecycleHooks: typeof r.LifecycleHooks = r.LifecycleHooks;
|
||||
export const LIFECYCLE_HOOKS_VALUES: typeof r.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES;
|
||||
@ -75,8 +74,6 @@ export type AnimationKeyframe = typeof r._AnimationKeyframe;
|
||||
export const AnimationKeyframe: typeof r.AnimationKeyframe = r.AnimationKeyframe;
|
||||
export type AnimationStyles = typeof r._AnimationStyles;
|
||||
export const AnimationStyles: typeof r.AnimationStyles = r.AnimationStyles;
|
||||
export type AnimationOutput = typeof r._AnimationOutput;
|
||||
export const AnimationOutput: typeof r.AnimationOutput = r.AnimationOutput;
|
||||
export const ANY_STATE = r.ANY_STATE;
|
||||
export const DEFAULT_STATE = r.DEFAULT_STATE;
|
||||
export const EMPTY_STATE = r.EMPTY_STATE;
|
||||
|
@ -12,7 +12,7 @@ import {ListWrapper, MapWrapper} from './facade/collection';
|
||||
import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang';
|
||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst, VariableAst} from './template_parser/template_ast';
|
||||
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
||||
|
||||
export class ProviderError extends ParseError {
|
||||
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
|
||||
@ -50,14 +50,14 @@ export class ProviderElementContext {
|
||||
private _hasViewContainer: boolean = false;
|
||||
|
||||
constructor(
|
||||
private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
|
||||
public viewContext: ProviderViewContext, private _parent: ProviderElementContext,
|
||||
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], attrs: AttrAst[],
|
||||
refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
|
||||
this._attrs = {};
|
||||
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
|
||||
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
|
||||
this._allProviders =
|
||||
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors);
|
||||
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
|
||||
this._contentQueries = _getContentQueries(directivesMeta);
|
||||
var queriedTokens = new Map<any, boolean>();
|
||||
MapWrapper.values(this._allProviders).forEach((provider) => {
|
||||
@ -124,7 +124,7 @@ export class ProviderElementContext {
|
||||
}
|
||||
currentEl = currentEl._parent;
|
||||
}
|
||||
queries = this._viewContext.viewQueries.get(token.reference);
|
||||
queries = this.viewContext.viewQueries.get(token.reference);
|
||||
if (isPresent(queries)) {
|
||||
ListWrapper.addAll(result, queries);
|
||||
}
|
||||
@ -136,10 +136,9 @@ export class ProviderElementContext {
|
||||
requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
|
||||
eager: boolean): ProviderAst {
|
||||
var resolvedProvider = this._allProviders.get(token.reference);
|
||||
if (isBlank(resolvedProvider) ||
|
||||
((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
||||
if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
||||
((requestingProviderType === ProviderAstType.PrivateService ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
||||
@ -150,7 +149,7 @@ export class ProviderElementContext {
|
||||
return transformedProviderAst;
|
||||
}
|
||||
if (isPresent(this._seenProviders.get(token.reference))) {
|
||||
this._viewContext.errors.push(new ProviderError(
|
||||
this.viewContext.errors.push(new ProviderError(
|
||||
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
|
||||
return null;
|
||||
}
|
||||
@ -239,12 +238,12 @@ export class ProviderElementContext {
|
||||
result = this._getLocalDependency(requestingProviderType, dep, eager);
|
||||
}
|
||||
if (dep.isSelf) {
|
||||
if (isBlank(result) && dep.isOptional) {
|
||||
if (!result && dep.isOptional) {
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||
}
|
||||
} else {
|
||||
// check parent elements
|
||||
while (isBlank(result) && isPresent(currElement._parent)) {
|
||||
while (!result && isPresent(currElement._parent)) {
|
||||
var prevElement = currElement;
|
||||
currElement = currElement._parent;
|
||||
if (prevElement._isViewRoot) {
|
||||
@ -253,10 +252,10 @@ export class ProviderElementContext {
|
||||
result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
|
||||
}
|
||||
// check @Host restriction
|
||||
if (isBlank(result)) {
|
||||
if (!dep.isHost || this._viewContext.component.type.isHost ||
|
||||
this._viewContext.component.type.reference === dep.token.reference ||
|
||||
isPresent(this._viewContext.viewProviders.get(dep.token.reference))) {
|
||||
if (!result) {
|
||||
if (!dep.isHost || this.viewContext.component.type.isHost ||
|
||||
this.viewContext.component.type.reference === dep.token.reference ||
|
||||
isPresent(this.viewContext.viewProviders.get(dep.token.reference))) {
|
||||
result = dep;
|
||||
} else {
|
||||
result = dep.isOptional ?
|
||||
@ -265,8 +264,8 @@ export class ProviderElementContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
this._viewContext.errors.push(
|
||||
if (!result) {
|
||||
this.viewContext.errors.push(
|
||||
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan));
|
||||
}
|
||||
return result;
|
||||
@ -311,7 +310,7 @@ export class NgModuleProviderAnalyzer {
|
||||
|
||||
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
|
||||
var resolvedProvider = this._allProviders.get(token.reference);
|
||||
if (isBlank(resolvedProvider)) {
|
||||
if (!resolvedProvider) {
|
||||
return null;
|
||||
}
|
||||
var transformedProviderAst = this._transformedProviders.get(token.reference);
|
||||
@ -414,7 +413,7 @@ function _normalizeProviders(
|
||||
providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>,
|
||||
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
|
||||
if (isBlank(targetProviders)) {
|
||||
if (!targetProviders) {
|
||||
targetProviders = [];
|
||||
}
|
||||
if (isPresent(providers)) {
|
||||
@ -479,7 +478,7 @@ function _resolveProviders(
|
||||
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`,
|
||||
sourceSpan));
|
||||
}
|
||||
if (isBlank(resolvedProvider)) {
|
||||
if (!resolvedProvider) {
|
||||
const lifecycleHooks =
|
||||
provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ?
|
||||
provider.token.identifier.lifecycleHooks :
|
||||
@ -530,7 +529,7 @@ function _getContentQueries(directives: CompileDirectiveMetadata[]):
|
||||
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
|
||||
query.selectors.forEach((token: CompileTokenMetadata) => {
|
||||
var entry = map.get(token.reference);
|
||||
if (isBlank(entry)) {
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(token.reference, entry);
|
||||
}
|
||||
|
@ -6,12 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, Optional, Provider, SchemaMetadata, SkipSelf, Type} from '@angular/core';
|
||||
|
||||
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
||||
import {AnimationCompiler} from './animation/animation_compiler';
|
||||
import {AnimationParser} from './animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
|
||||
import {CompilerConfig} from './config';
|
||||
import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {isBlank, stringify} from './facade/lang';
|
||||
import {stringify} from './facade/lang';
|
||||
import {CompileMetadataResolver} from './metadata_resolver';
|
||||
import {NgModuleCompiler} from './ng_module_compiler';
|
||||
import * as ir from './output/output_ast';
|
||||
@ -23,8 +24,6 @@ import {TemplateParser} from './template_parser/template_parser';
|
||||
import {SyncAsyncResult} from './util';
|
||||
import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An internal module of the Angular compiler that begins with component types,
|
||||
* extracts templates, and eventually produces a compiled version of the component
|
||||
@ -39,6 +38,8 @@ export class RuntimeCompiler implements Compiler {
|
||||
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
||||
private _animationParser = new AnimationParser();
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
||||
@ -190,7 +191,7 @@ export class RuntimeCompiler implements Compiler {
|
||||
|
||||
private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate {
|
||||
var compiledTemplate = this._compiledHostTemplateCache.get(compType);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
if (!compiledTemplate) {
|
||||
var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||
assertComponent(compMeta);
|
||||
var hostMeta = createHostComponentMeta(compMeta);
|
||||
@ -205,7 +206,7 @@ export class RuntimeCompiler implements Compiler {
|
||||
private _createCompiledTemplate(
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata): CompiledTemplate {
|
||||
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.reference);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
if (!compiledTemplate) {
|
||||
assertComponent(compMeta);
|
||||
compiledTemplate = new CompiledTemplate(
|
||||
false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives,
|
||||
@ -253,12 +254,15 @@ export class RuntimeCompiler implements Compiler {
|
||||
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
|
||||
const viewCompMetas = template.viewComponentTypes.map(
|
||||
(compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta);
|
||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas),
|
||||
template.viewPipes, template.schemas, compMeta.type.name);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
const compileResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||
template.viewPipes);
|
||||
template.viewPipes, compiledAnimations);
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
let depTemplate: CompiledTemplate;
|
||||
if (dep instanceof ViewFactoryDependency) {
|
||||
@ -275,6 +279,8 @@ export class RuntimeCompiler implements Compiler {
|
||||
});
|
||||
const statements =
|
||||
stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements);
|
||||
compiledAnimations.forEach(
|
||||
entry => { entry.statements.forEach(statement => { statements.push(statement); }); });
|
||||
let factory: any;
|
||||
if (!this._compilerConfig.useJit) {
|
||||
factory = interpretStatements(statements, compileResult.viewFactoryVar);
|
||||
|
@ -344,4 +344,26 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
||||
getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; }
|
||||
|
||||
getDefaultComponentElementName(): string { return 'ng-component'; }
|
||||
|
||||
validateProperty(name: string): {error: boolean, msg?: string} {
|
||||
if (name.toLowerCase().startsWith('on')) {
|
||||
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
|
||||
`please use (${name.slice(2)})=...` +
|
||||
`\nIf '${name}' is a directive input, make sure the directive is imported by the` +
|
||||
` current module.`;
|
||||
return {error: true, msg: msg};
|
||||
} else {
|
||||
return {error: false};
|
||||
}
|
||||
}
|
||||
|
||||
validateAttribute(name: string): {error: boolean, msg?: string} {
|
||||
if (name.toLowerCase().startsWith('on')) {
|
||||
const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
|
||||
`please use (${name.slice(2)})=...`;
|
||||
return {error: true, msg: msg};
|
||||
} else {
|
||||
return {error: false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,4 +14,6 @@ export abstract class ElementSchemaRegistry {
|
||||
abstract securityContext(tagName: string, propName: string): any;
|
||||
abstract getMappedPropName(propName: string): string;
|
||||
abstract getDefaultComponentElementName(): string;
|
||||
abstract validateProperty(name: string): {error: boolean, msg?: string};
|
||||
abstract validateAttribute(name: string): {error: boolean, msg?: string};
|
||||
}
|
||||
|
@ -6,18 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent} from './facade/lang';
|
||||
import {getHtmlTagDefinition} from './ml_parser/html_tags';
|
||||
|
||||
const _EMPTY_ATTR_VALUE = '';
|
||||
|
||||
const _SELECTOR_REGEXP = new RegExp(
|
||||
'(\\:not\\()|' + //":not("
|
||||
'([-\\w]+)|' + // "tag"
|
||||
'(?:\\.([-\\w]+))|' + // ".class"
|
||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" or "[name*=value]"
|
||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
|
||||
'(\\))|' + // ")"
|
||||
'(\\s*,\\s*)', // ","
|
||||
'g');
|
||||
@ -34,21 +29,21 @@ export class CssSelector {
|
||||
notSelectors: CssSelector[] = [];
|
||||
|
||||
static parse(selector: string): CssSelector[] {
|
||||
var results: CssSelector[] = [];
|
||||
var _addResult = (res: CssSelector[], cssSel: CssSelector) => {
|
||||
if (cssSel.notSelectors.length > 0 && isBlank(cssSel.element) &&
|
||||
ListWrapper.isEmpty(cssSel.classNames) && ListWrapper.isEmpty(cssSel.attrs)) {
|
||||
const results: CssSelector[] = [];
|
||||
const _addResult = (res: CssSelector[], cssSel: CssSelector) => {
|
||||
if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
|
||||
cssSel.attrs.length == 0) {
|
||||
cssSel.element = '*';
|
||||
}
|
||||
res.push(cssSel);
|
||||
};
|
||||
var cssSelector = new CssSelector();
|
||||
var match: string[];
|
||||
var current = cssSelector;
|
||||
var inNot = false;
|
||||
let cssSelector = new CssSelector();
|
||||
let match: string[];
|
||||
let current = cssSelector;
|
||||
let inNot = false;
|
||||
_SELECTOR_REGEXP.lastIndex = 0;
|
||||
while (isPresent(match = _SELECTOR_REGEXP.exec(selector))) {
|
||||
if (isPresent(match[1])) {
|
||||
while (match = _SELECTOR_REGEXP.exec(selector)) {
|
||||
if (match[1]) {
|
||||
if (inNot) {
|
||||
throw new Error('Nesting :not is not allowed in a selector');
|
||||
}
|
||||
@ -56,20 +51,20 @@ export class CssSelector {
|
||||
current = new CssSelector();
|
||||
cssSelector.notSelectors.push(current);
|
||||
}
|
||||
if (isPresent(match[2])) {
|
||||
if (match[2]) {
|
||||
current.setElement(match[2]);
|
||||
}
|
||||
if (isPresent(match[3])) {
|
||||
if (match[3]) {
|
||||
current.addClassName(match[3]);
|
||||
}
|
||||
if (isPresent(match[4])) {
|
||||
if (match[4]) {
|
||||
current.addAttribute(match[4], match[5]);
|
||||
}
|
||||
if (isPresent(match[6])) {
|
||||
if (match[6]) {
|
||||
inNot = false;
|
||||
current = cssSelector;
|
||||
}
|
||||
if (isPresent(match[7])) {
|
||||
if (match[7]) {
|
||||
if (inNot) {
|
||||
throw new Error('Multiple selectors in :not are not supported');
|
||||
}
|
||||
@ -106,37 +101,22 @@ export class CssSelector {
|
||||
`<${tagName}${classAttr}${attrs}></${tagName}>`;
|
||||
}
|
||||
|
||||
addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) {
|
||||
this.attrs.push(name);
|
||||
if (isPresent(value)) {
|
||||
value = value.toLowerCase();
|
||||
} else {
|
||||
value = _EMPTY_ATTR_VALUE;
|
||||
}
|
||||
this.attrs.push(value);
|
||||
addAttribute(name: string, value: string = '') {
|
||||
this.attrs.push(name, value && value.toLowerCase() || '');
|
||||
}
|
||||
|
||||
addClassName(name: string) { this.classNames.push(name.toLowerCase()); }
|
||||
|
||||
toString(): string {
|
||||
var res = '';
|
||||
if (isPresent(this.element)) {
|
||||
res += this.element;
|
||||
let res: string = this.element || '';
|
||||
if (this.classNames) {
|
||||
this.classNames.forEach(klass => res += `.${klass}`);
|
||||
}
|
||||
if (isPresent(this.classNames)) {
|
||||
for (var i = 0; i < this.classNames.length; i++) {
|
||||
res += '.' + this.classNames[i];
|
||||
}
|
||||
}
|
||||
if (isPresent(this.attrs)) {
|
||||
for (var i = 0; i < this.attrs.length;) {
|
||||
var attrName = this.attrs[i++];
|
||||
var attrValue = this.attrs[i++];
|
||||
res += '[' + attrName;
|
||||
if (attrValue.length > 0) {
|
||||
res += '=' + attrValue;
|
||||
}
|
||||
res += ']';
|
||||
if (this.attrs) {
|
||||
for (let i = 0; i < this.attrs.length; i += 2) {
|
||||
const name = this.attrs[i];
|
||||
const value = this.attrs[i + 1];
|
||||
res += `[${name}${value ? '=' + value : ''}]`;
|
||||
}
|
||||
}
|
||||
this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
|
||||
@ -150,26 +130,26 @@ export class CssSelector {
|
||||
*/
|
||||
export class SelectorMatcher {
|
||||
static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher {
|
||||
var notMatcher = new SelectorMatcher();
|
||||
const notMatcher = new SelectorMatcher();
|
||||
notMatcher.addSelectables(notSelectors, null);
|
||||
return notMatcher;
|
||||
}
|
||||
|
||||
private _elementMap = new Map<string, SelectorContext[]>();
|
||||
private _elementPartialMap = new Map<string, SelectorMatcher>();
|
||||
private _classMap = new Map<string, SelectorContext[]>();
|
||||
private _classPartialMap = new Map<string, SelectorMatcher>();
|
||||
private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>();
|
||||
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>();
|
||||
private _elementMap: {[k: string]: SelectorContext[]} = {};
|
||||
private _elementPartialMap: {[k: string]: SelectorMatcher} = {};
|
||||
private _classMap: {[k: string]: SelectorContext[]} = {};
|
||||
private _classPartialMap: {[k: string]: SelectorMatcher} = {};
|
||||
private _attrValueMap: {[k: string]: {[k: string]: SelectorContext[]}} = {};
|
||||
private _attrValuePartialMap: {[k: string]: {[k: string]: SelectorMatcher}} = {};
|
||||
private _listContexts: SelectorListContext[] = [];
|
||||
|
||||
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
|
||||
var listContext: SelectorListContext = null;
|
||||
let listContext: SelectorListContext = null;
|
||||
if (cssSelectors.length > 1) {
|
||||
listContext = new SelectorListContext(cssSelectors);
|
||||
this._listContexts.push(listContext);
|
||||
}
|
||||
for (var i = 0; i < cssSelectors.length; i++) {
|
||||
for (let i = 0; i < cssSelectors.length; i++) {
|
||||
this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
||||
}
|
||||
}
|
||||
@ -181,14 +161,14 @@ export class SelectorMatcher {
|
||||
*/
|
||||
private _addSelectable(
|
||||
cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) {
|
||||
var matcher: SelectorMatcher = this;
|
||||
var element = cssSelector.element;
|
||||
var classNames = cssSelector.classNames;
|
||||
var attrs = cssSelector.attrs;
|
||||
var selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
||||
let matcher: SelectorMatcher = this;
|
||||
const element = cssSelector.element;
|
||||
const classNames = cssSelector.classNames;
|
||||
const attrs = cssSelector.attrs;
|
||||
const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
||||
|
||||
if (isPresent(element)) {
|
||||
var isTerminal = attrs.length === 0 && classNames.length === 0;
|
||||
if (element) {
|
||||
const isTerminal = attrs.length === 0 && classNames.length === 0;
|
||||
if (isTerminal) {
|
||||
this._addTerminal(matcher._elementMap, element, selectable);
|
||||
} else {
|
||||
@ -196,10 +176,10 @@ export class SelectorMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(classNames)) {
|
||||
for (var index = 0; index < classNames.length; index++) {
|
||||
var isTerminal = attrs.length === 0 && index === classNames.length - 1;
|
||||
var className = classNames[index];
|
||||
if (classNames) {
|
||||
for (let i = 0; i < classNames.length; i++) {
|
||||
const isTerminal = attrs.length === 0 && i === classNames.length - 1;
|
||||
const className = classNames[i];
|
||||
if (isTerminal) {
|
||||
this._addTerminal(matcher._classMap, className, selectable);
|
||||
} else {
|
||||
@ -208,47 +188,47 @@ export class SelectorMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(attrs)) {
|
||||
for (var index = 0; index < attrs.length;) {
|
||||
var isTerminal = index === attrs.length - 2;
|
||||
var attrName = attrs[index++];
|
||||
var attrValue = attrs[index++];
|
||||
if (attrs) {
|
||||
for (let i = 0; i < attrs.length; i += 2) {
|
||||
const isTerminal = i === attrs.length - 2;
|
||||
const name = attrs[i];
|
||||
const value = attrs[i + 1];
|
||||
if (isTerminal) {
|
||||
var terminalMap = matcher._attrValueMap;
|
||||
var terminalValuesMap = terminalMap.get(attrName);
|
||||
if (isBlank(terminalValuesMap)) {
|
||||
terminalValuesMap = new Map<string, SelectorContext[]>();
|
||||
terminalMap.set(attrName, terminalValuesMap);
|
||||
const terminalMap = matcher._attrValueMap;
|
||||
let terminalValuesMap = terminalMap[name];
|
||||
if (!terminalValuesMap) {
|
||||
terminalValuesMap = {};
|
||||
terminalMap[name] = terminalValuesMap;
|
||||
}
|
||||
this._addTerminal(terminalValuesMap, attrValue, selectable);
|
||||
this._addTerminal(terminalValuesMap, value, selectable);
|
||||
} else {
|
||||
var parttialMap = matcher._attrValuePartialMap;
|
||||
var partialValuesMap = parttialMap.get(attrName);
|
||||
if (isBlank(partialValuesMap)) {
|
||||
partialValuesMap = new Map<string, SelectorMatcher>();
|
||||
parttialMap.set(attrName, partialValuesMap);
|
||||
let partialMap = matcher._attrValuePartialMap;
|
||||
let partialValuesMap = partialMap[name];
|
||||
if (!partialValuesMap) {
|
||||
partialValuesMap = {};
|
||||
partialMap[name] = partialValuesMap;
|
||||
}
|
||||
matcher = this._addPartial(partialValuesMap, attrValue);
|
||||
matcher = this._addPartial(partialValuesMap, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _addTerminal(
|
||||
map: Map<string, SelectorContext[]>, name: string, selectable: SelectorContext) {
|
||||
var terminalList = map.get(name);
|
||||
if (isBlank(terminalList)) {
|
||||
map: {[k: string]: SelectorContext[]}, name: string, selectable: SelectorContext) {
|
||||
let terminalList = map[name];
|
||||
if (!terminalList) {
|
||||
terminalList = [];
|
||||
map.set(name, terminalList);
|
||||
map[name] = terminalList;
|
||||
}
|
||||
terminalList.push(selectable);
|
||||
}
|
||||
|
||||
private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher {
|
||||
var matcher = map.get(name);
|
||||
if (isBlank(matcher)) {
|
||||
private _addPartial(map: {[k: string]: SelectorMatcher}, name: string): SelectorMatcher {
|
||||
let matcher = map[name];
|
||||
if (!matcher) {
|
||||
matcher = new SelectorMatcher();
|
||||
map.set(name, matcher);
|
||||
map[name] = matcher;
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
@ -261,12 +241,12 @@ export class SelectorMatcher {
|
||||
* @return boolean true if a match was found
|
||||
*/
|
||||
match(cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
||||
var result = false;
|
||||
var element = cssSelector.element;
|
||||
var classNames = cssSelector.classNames;
|
||||
var attrs = cssSelector.attrs;
|
||||
let result = false;
|
||||
const element = cssSelector.element;
|
||||
const classNames = cssSelector.classNames;
|
||||
const attrs = cssSelector.attrs;
|
||||
|
||||
for (var i = 0; i < this._listContexts.length; i++) {
|
||||
for (let i = 0; i < this._listContexts.length; i++) {
|
||||
this._listContexts[i].alreadyMatched = false;
|
||||
}
|
||||
|
||||
@ -274,9 +254,9 @@ export class SelectorMatcher {
|
||||
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
|
||||
if (isPresent(classNames)) {
|
||||
for (var index = 0; index < classNames.length; index++) {
|
||||
var className = classNames[index];
|
||||
if (classNames) {
|
||||
for (let i = 0; i < classNames.length; i++) {
|
||||
const className = classNames[i];
|
||||
result =
|
||||
this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
||||
result =
|
||||
@ -285,28 +265,25 @@ export class SelectorMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(attrs)) {
|
||||
for (var index = 0; index < attrs.length;) {
|
||||
var attrName = attrs[index++];
|
||||
var attrValue = attrs[index++];
|
||||
if (attrs) {
|
||||
for (let i = 0; i < attrs.length; i += 2) {
|
||||
const name = attrs[i];
|
||||
const value = attrs[i + 1];
|
||||
|
||||
var terminalValuesMap = this._attrValueMap.get(attrName);
|
||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||
result = this._matchTerminal(
|
||||
terminalValuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
}
|
||||
result = this._matchTerminal(terminalValuesMap, attrValue, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
|
||||
var partialValuesMap = this._attrValuePartialMap.get(attrName);
|
||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||
result = this._matchPartial(
|
||||
partialValuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
const terminalValuesMap = this._attrValueMap[name];
|
||||
if (value) {
|
||||
result =
|
||||
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
||||
}
|
||||
result =
|
||||
this._matchPartial(partialValuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
||||
|
||||
const partialValuesMap = this._attrValuePartialMap[name];
|
||||
if (value) {
|
||||
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
||||
}
|
||||
result =
|
||||
this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -314,24 +291,24 @@ export class SelectorMatcher {
|
||||
|
||||
/** @internal */
|
||||
_matchTerminal(
|
||||
map: Map<string, SelectorContext[]>, name: string, cssSelector: CssSelector,
|
||||
map: {[k: string]: SelectorContext[]}, name: string, cssSelector: CssSelector,
|
||||
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
||||
if (isBlank(map) || isBlank(name)) {
|
||||
if (!map || typeof name !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var selectables = map.get(name);
|
||||
var starSelectables = map.get('*');
|
||||
if (isPresent(starSelectables)) {
|
||||
let selectables = map[name];
|
||||
const starSelectables = map['*'];
|
||||
if (starSelectables) {
|
||||
selectables = selectables.concat(starSelectables);
|
||||
}
|
||||
if (isBlank(selectables)) {
|
||||
if (!selectables) {
|
||||
return false;
|
||||
}
|
||||
var selectable: SelectorContext;
|
||||
var result = false;
|
||||
for (var index = 0; index < selectables.length; index++) {
|
||||
selectable = selectables[index];
|
||||
let selectable: SelectorContext;
|
||||
let result = false;
|
||||
for (let i = 0; i < selectables.length; i++) {
|
||||
selectable = selectables[i];
|
||||
result = selectable.finalize(cssSelector, matchedCallback) || result;
|
||||
}
|
||||
return result;
|
||||
@ -339,13 +316,14 @@ export class SelectorMatcher {
|
||||
|
||||
/** @internal */
|
||||
_matchPartial(
|
||||
map: Map<string, SelectorMatcher>, name: string, cssSelector: CssSelector,
|
||||
map: {[k: string]: SelectorMatcher}, name: string, cssSelector: CssSelector,
|
||||
matchedCallback: (c: CssSelector, a: any) => void): boolean {
|
||||
if (isBlank(map) || isBlank(name)) {
|
||||
if (!map || typeof name !== 'string') {
|
||||
return false;
|
||||
}
|
||||
var nestedSelector = map.get(name);
|
||||
if (isBlank(nestedSelector)) {
|
||||
|
||||
const nestedSelector = map[name];
|
||||
if (!nestedSelector) {
|
||||
return false;
|
||||
}
|
||||
// TODO(perf): get rid of recursion and measure again
|
||||
@ -373,15 +351,13 @@ export class SelectorContext {
|
||||
}
|
||||
|
||||
finalize(cssSelector: CssSelector, callback: (c: CssSelector, a: any) => void): boolean {
|
||||
var result = true;
|
||||
if (this.notSelectors.length > 0 &&
|
||||
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
var notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
||||
let result = true;
|
||||
if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
||||
const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
||||
result = !notMatcher.match(cssSelector, null);
|
||||
}
|
||||
if (result && isPresent(callback) &&
|
||||
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
if (isPresent(this.listContext)) {
|
||||
if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
|
||||
if (this.listContext) {
|
||||
this.listContext.alreadyMatched = true;
|
||||
}
|
||||
callback(this.selector, this.cbContext);
|
||||
|
@ -6,8 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StringWrapper, isBlank, isPresent} from './facade/lang';
|
||||
|
||||
/**
|
||||
* This file is a port of shadowCSS from webcomponents.js to TypeScript.
|
||||
*
|
||||
@ -175,9 +173,8 @@ export class ShadowCss {
|
||||
**/
|
||||
private _insertPolyfillDirectivesInCssText(cssText: string): string {
|
||||
// Difference with webcomponents.js: does not handle comments
|
||||
return StringWrapper.replaceAllMapped(
|
||||
cssText, _cssContentNextSelectorRe,
|
||||
function(m: any /** TODO #9100 */) { return m[1] + '{'; });
|
||||
return cssText.replace(
|
||||
_cssContentNextSelectorRe, function(...m: string[]) { return m[2] + '{'; });
|
||||
}
|
||||
|
||||
/*
|
||||
@ -197,13 +194,10 @@ export class ShadowCss {
|
||||
**/
|
||||
private _insertPolyfillRulesInCssText(cssText: string): string {
|
||||
// Difference with webcomponents.js: does not handle comments
|
||||
return StringWrapper.replaceAllMapped(
|
||||
cssText, _cssContentRuleRe, function(m: any /** TODO #9100 */) {
|
||||
let rule = m[0];
|
||||
rule = StringWrapper.replace(rule, m[1], '');
|
||||
rule = StringWrapper.replace(rule, m[2], '');
|
||||
return m[3] + rule;
|
||||
});
|
||||
return cssText.replace(_cssContentRuleRe, (...m: string[]) => {
|
||||
const rule = m[0].replace(m[1], '').replace(m[2], '');
|
||||
return m[4] + rule;
|
||||
});
|
||||
}
|
||||
|
||||
/* Ensure styles are scoped. Pseudo-scoping takes a rule like:
|
||||
@ -215,15 +209,16 @@ export class ShadowCss {
|
||||
* scopeName .foo { ... }
|
||||
*/
|
||||
private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string {
|
||||
const unscoped = this._extractUnscopedRulesFromCssText(cssText);
|
||||
const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
|
||||
// replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
|
||||
cssText = this._insertPolyfillHostInCssText(cssText);
|
||||
cssText = this._convertColonHost(cssText);
|
||||
cssText = this._convertColonHostContext(cssText);
|
||||
cssText = this._convertShadowDOMSelectors(cssText);
|
||||
if (isPresent(scopeSelector)) {
|
||||
if (scopeSelector) {
|
||||
cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
|
||||
}
|
||||
cssText = cssText + '\n' + unscoped;
|
||||
cssText = cssText + '\n' + unscopedRules;
|
||||
return cssText.trim();
|
||||
}
|
||||
|
||||
@ -248,9 +243,7 @@ export class ShadowCss {
|
||||
let m: RegExpExecArray;
|
||||
_cssContentUnscopedRuleRe.lastIndex = 0;
|
||||
while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
|
||||
let rule = m[0];
|
||||
rule = StringWrapper.replace(rule, m[2], '');
|
||||
rule = StringWrapper.replace(rule, m[1], m[3]);
|
||||
const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
|
||||
r += rule + '\n\n';
|
||||
}
|
||||
return r;
|
||||
@ -261,7 +254,7 @@ export class ShadowCss {
|
||||
*
|
||||
* to
|
||||
*
|
||||
* scopeName.foo > .bar
|
||||
* .foo<scopeName> > .bar
|
||||
*/
|
||||
private _convertColonHost(cssText: string): string {
|
||||
return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer);
|
||||
@ -272,7 +265,7 @@ export class ShadowCss {
|
||||
*
|
||||
* to
|
||||
*
|
||||
* scopeName.foo > .bar, .foo scopeName > .bar { }
|
||||
* .foo<scopeName> > .bar, .foo scopeName > .bar { }
|
||||
*
|
||||
* and
|
||||
*
|
||||
@ -280,7 +273,7 @@ export class ShadowCss {
|
||||
*
|
||||
* to
|
||||
*
|
||||
* scopeName.foo .bar { ... }
|
||||
* .foo<scopeName> .bar { ... }
|
||||
*/
|
||||
private _convertColonHostContext(cssText: string): string {
|
||||
return this._convertColonRule(
|
||||
@ -288,14 +281,14 @@ export class ShadowCss {
|
||||
}
|
||||
|
||||
private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string {
|
||||
// p1 = :host, p2 = contents of (), p3 rest of rule
|
||||
return StringWrapper.replaceAllMapped(cssText, regExp, function(m: any /** TODO #9100 */) {
|
||||
if (isPresent(m[2])) {
|
||||
const parts = m[2].split(','), r: any[] /** TODO #9100 */ = [];
|
||||
// m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule
|
||||
return cssText.replace(regExp, function(...m: string[]) {
|
||||
if (m[2]) {
|
||||
const parts = m[2].split(',');
|
||||
const r: string[] = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
let p = parts[i];
|
||||
if (isBlank(p)) break;
|
||||
p = p.trim();
|
||||
let p = parts[i].trim();
|
||||
if (!p) break;
|
||||
r.push(partReplacer(_polyfillHostNoCombinator, p, m[3]));
|
||||
}
|
||||
return r.join(',');
|
||||
@ -306,7 +299,7 @@ export class ShadowCss {
|
||||
}
|
||||
|
||||
private _colonHostContextPartReplacer(host: string, part: string, suffix: string): string {
|
||||
if (StringWrapper.contains(part, _polyfillHost)) {
|
||||
if (part.indexOf(_polyfillHost) > -1) {
|
||||
return this._colonHostPartReplacer(host, part, suffix);
|
||||
} else {
|
||||
return host + part + suffix + ', ' + part + ' ' + host + suffix;
|
||||
@ -314,7 +307,7 @@ export class ShadowCss {
|
||||
}
|
||||
|
||||
private _colonHostPartReplacer(host: string, part: string, suffix: string): string {
|
||||
return host + StringWrapper.replace(part, _polyfillHost, '') + suffix;
|
||||
return host + part.replace(_polyfillHost, '') + suffix;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -322,8 +315,7 @@ export class ShadowCss {
|
||||
* by replacing with space.
|
||||
*/
|
||||
private _convertShadowDOMSelectors(cssText: string): string {
|
||||
return _shadowDOMSelectorsRe.reduce(
|
||||
(result, pattern) => { return StringWrapper.replaceAll(result, pattern, ' '); }, cssText);
|
||||
return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
|
||||
}
|
||||
|
||||
// change a selector like 'div' to 'name div'
|
||||
@ -331,10 +323,12 @@ export class ShadowCss {
|
||||
return processRules(cssText, (rule: CssRule) => {
|
||||
let selector = rule.selector;
|
||||
let content = rule.content;
|
||||
if (rule.selector[0] != '@' || rule.selector.startsWith('@page')) {
|
||||
if (rule.selector[0] != '@') {
|
||||
selector =
|
||||
this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
|
||||
} else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports')) {
|
||||
} else if (
|
||||
rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
|
||||
rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) {
|
||||
content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
|
||||
}
|
||||
return new CssRule(selector, content);
|
||||
@ -369,8 +363,7 @@ export class ShadowCss {
|
||||
private _makeScopeMatcher(scopeSelector: string): RegExp {
|
||||
const lre = /\[/g;
|
||||
const rre = /\]/g;
|
||||
scopeSelector = StringWrapper.replaceAll(scopeSelector, lre, '\\[');
|
||||
scopeSelector = StringWrapper.replaceAll(scopeSelector, rre, '\\]');
|
||||
scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
|
||||
return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
|
||||
}
|
||||
|
||||
@ -385,13 +378,14 @@ export class ShadowCss {
|
||||
string {
|
||||
// In Android browser, the lastIndex is not reset when the regex is used in String.replace()
|
||||
_polyfillHostRe.lastIndex = 0;
|
||||
|
||||
if (_polyfillHostRe.test(selector)) {
|
||||
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
||||
selector = StringWrapper.replace(selector, _polyfillHostNoCombinator, replaceBy);
|
||||
return StringWrapper.replaceAll(selector, _polyfillHostRe, replaceBy + ' ');
|
||||
} else {
|
||||
return scopeSelector + ' ' + selector;
|
||||
return selector.replace(_polyfillHostNoCombinatorRe, (hnc, selector) => selector + replaceBy)
|
||||
.replace(_polyfillHostRe, replaceBy + ' ');
|
||||
}
|
||||
|
||||
return scopeSelector + ' ' + selector;
|
||||
}
|
||||
|
||||
// return a selector with [name] suffix on each simple selector
|
||||
@ -404,9 +398,9 @@ export class ShadowCss {
|
||||
const attrName = '[' + scopeSelector + ']';
|
||||
|
||||
const _scopeSelectorPart = (p: string) => {
|
||||
var scopedP = p.trim();
|
||||
let scopedP = p.trim();
|
||||
|
||||
if (scopedP.length == 0) {
|
||||
if (!scopedP) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -426,12 +420,23 @@ export class ShadowCss {
|
||||
return scopedP;
|
||||
};
|
||||
|
||||
const sep = /( |>|\+|~(?!=))\s*/g;
|
||||
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
|
||||
let attrSelectorIndex = 0;
|
||||
const attrSelectors: string[] = [];
|
||||
|
||||
let scoped = '';
|
||||
// replace attribute selectors with placeholders to avoid issue with white space being treated
|
||||
// as separator
|
||||
selector = selector.replace(/\[[^\]]*\]/g, (attrSelector) => {
|
||||
const replaceBy = `__attr_sel_${attrSelectorIndex}__`;
|
||||
attrSelectors.push(attrSelector);
|
||||
attrSelectorIndex++;
|
||||
return replaceBy;
|
||||
});
|
||||
|
||||
let scopedSelector = '';
|
||||
let startIndex = 0;
|
||||
let res: RegExpExecArray;
|
||||
const sep = /( |>|\+|~(?!=))\s*/g;
|
||||
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
|
||||
|
||||
while ((res = sep.exec(selector)) !== null) {
|
||||
const separator = res[1];
|
||||
@ -439,10 +444,14 @@ export class ShadowCss {
|
||||
// if a selector appears before :host-context it should not be shimmed as it
|
||||
// matches on ancestor elements and not on elements in the host's shadow
|
||||
const scopedPart = startIndex >= scopeAfter ? _scopeSelectorPart(part) : part;
|
||||
scoped += `${scopedPart} ${separator} `;
|
||||
scopedSelector += `${scopedPart} ${separator} `;
|
||||
startIndex = sep.lastIndex;
|
||||
}
|
||||
return scoped + _scopeSelectorPart(selector.substring(startIndex));
|
||||
|
||||
scopedSelector += _scopeSelectorPart(selector.substring(startIndex));
|
||||
|
||||
// replace the placeholders with their original values
|
||||
return scopedSelector.replace(/__attr_sel_(\d+)__/g, (ph, index) => attrSelectors[+index]);
|
||||
}
|
||||
|
||||
private _insertPolyfillHostInCssText(selector: string): string {
|
||||
@ -451,10 +460,10 @@ export class ShadowCss {
|
||||
}
|
||||
}
|
||||
const _cssContentNextSelectorRe =
|
||||
/polyfill-next-selector[^}]*content:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim;
|
||||
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim;
|
||||
/polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
||||
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
||||
const _cssContentUnscopedRuleRe =
|
||||
/(polyfill-unscoped-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim;
|
||||
/(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
||||
const _polyfillHost = '-shadowcsshost';
|
||||
// note: :host-context pre-processed to -shadowcsshostcontext.
|
||||
const _polyfillHostContext = '-shadowcsscontext';
|
||||
@ -464,6 +473,7 @@ const _parenSuffix = ')(?:\\((' +
|
||||
const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim');
|
||||
const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim');
|
||||
const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
|
||||
const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
|
||||
const _shadowDOMSelectorsRe = [
|
||||
/::shadow/g,
|
||||
/::content/g,
|
||||
@ -480,7 +490,7 @@ const _colonHostContextRe = /:host-context/gim;
|
||||
const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
|
||||
|
||||
function stripComments(input: string): string {
|
||||
return StringWrapper.replaceAllMapped(input, _commentRe, (_: any /** TODO #9100 */) => '');
|
||||
return input.replace(_commentRe, '');
|
||||
}
|
||||
|
||||
// all comments except inline source mapping
|
||||
@ -501,23 +511,22 @@ export class CssRule {
|
||||
constructor(public selector: string, public content: string) {}
|
||||
}
|
||||
|
||||
export function processRules(input: string, ruleCallback: Function): string {
|
||||
export function processRules(input: string, ruleCallback: (rule: CssRule) => CssRule): string {
|
||||
const inputWithEscapedBlocks = escapeBlocks(input);
|
||||
let nextBlockIndex = 0;
|
||||
return StringWrapper.replaceAllMapped(
|
||||
inputWithEscapedBlocks.escapedString, _ruleRe, function(m: any /** TODO #9100 */) {
|
||||
const selector = m[2];
|
||||
let content = '';
|
||||
let suffix = m[4];
|
||||
let contentPrefix = '';
|
||||
if (isPresent(m[4]) && m[4].startsWith('{' + BLOCK_PLACEHOLDER)) {
|
||||
content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
|
||||
suffix = m[4].substring(BLOCK_PLACEHOLDER.length + 1);
|
||||
contentPrefix = '{';
|
||||
}
|
||||
const rule = ruleCallback(new CssRule(selector, content));
|
||||
return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
|
||||
});
|
||||
return inputWithEscapedBlocks.escapedString.replace(_ruleRe, function(...m: string[]) {
|
||||
const selector = m[2];
|
||||
let content = '';
|
||||
let suffix = m[4];
|
||||
let contentPrefix = '';
|
||||
if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
|
||||
content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
|
||||
suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
|
||||
contentPrefix = '{';
|
||||
}
|
||||
const rule = ruleCallback(new CssRule(selector, content));
|
||||
return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
|
||||
});
|
||||
}
|
||||
|
||||
class StringWithEscapedBlocks {
|
||||
@ -525,11 +534,11 @@ class StringWithEscapedBlocks {
|
||||
}
|
||||
|
||||
function escapeBlocks(input: string): StringWithEscapedBlocks {
|
||||
const inputParts = StringWrapper.split(input, _curlyRe);
|
||||
const resultParts: any[] /** TODO #9100 */ = [];
|
||||
const escapedBlocks: any[] /** TODO #9100 */ = [];
|
||||
const inputParts = input.split(_curlyRe);
|
||||
const resultParts: string[] = [];
|
||||
const escapedBlocks: string[] = [];
|
||||
let bracketCount = 0;
|
||||
let currentBlockParts: any[] /** TODO #9100 */ = [];
|
||||
let currentBlockParts: string[] = [];
|
||||
for (let partIndex = 0; partIndex < inputParts.length; partIndex++) {
|
||||
const part = inputParts[partIndex];
|
||||
if (part == CLOSE_CURLY) {
|
||||
|
@ -12,11 +12,8 @@ import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata}
|
||||
import {AST} from '../expression_parser/ast';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {ParseSourceSpan} from '../parse_util';
|
||||
|
||||
import {LifecycleHooks} from '../private_import_core';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An Abstract Syntax Tree node representing part of a parsed Angular template.
|
||||
*/
|
||||
@ -61,7 +58,8 @@ export class AttrAst implements TemplateAst {
|
||||
}
|
||||
|
||||
/**
|
||||
* A binding for an element property (e.g. `[property]="expression"`).
|
||||
* A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
|
||||
* `[@trigger]="stateExp"`)
|
||||
*/
|
||||
export class BoundElementPropertyAst implements TemplateAst {
|
||||
constructor(
|
||||
@ -71,14 +69,16 @@ export class BoundElementPropertyAst implements TemplateAst {
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitElementProperty(this, context);
|
||||
}
|
||||
get isAnimation(): boolean { return this.type === PropertyBindingType.Animation; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A binding for an element event (e.g. `(event)="handler()"`).
|
||||
* A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g.
|
||||
* `(@trigger.phase)="callback($event)"`).
|
||||
*/
|
||||
export class BoundEventAst implements TemplateAst {
|
||||
constructor(
|
||||
public name: string, public target: string, public handler: AST,
|
||||
public name: string, public target: string, public phase: string, public handler: AST,
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitEvent(this, context);
|
||||
@ -90,6 +90,7 @@ export class BoundEventAst implements TemplateAst {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
get isAnimation(): boolean { return !!this.phase; }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,11 +8,10 @@
|
||||
|
||||
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
|
||||
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, isString} from '../facade/lang';
|
||||
import {isPresent, isString} from '../facade/lang';
|
||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
|
||||
import * as html from '../ml_parser/ast';
|
||||
@ -26,12 +25,13 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {CssSelector, SelectorMatcher} from '../selector';
|
||||
import {isStyleUrlResolvable} from '../style_url_resolver';
|
||||
import {splitAtColon} from '../util';
|
||||
import {splitAtColon, splitAtPeriod} from '../util';
|
||||
|
||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
|
||||
import {PreparsedElementType, preparseElement} from './template_preparser';
|
||||
|
||||
|
||||
|
||||
// Group 1 = "bind-"
|
||||
// Group 2 = "let-"
|
||||
// Group 3 = "ref-/#"
|
||||
@ -142,7 +142,6 @@ export class TemplateParser {
|
||||
const parseVisitor = new TemplateParseVisitor(
|
||||
providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser,
|
||||
this._schemaRegistry);
|
||||
|
||||
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
||||
errors.push(...parseVisitor.errors, ...providerViewContext.errors);
|
||||
} else {
|
||||
@ -444,6 +443,15 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
|
||||
providerContext.transformedHasViewContainer, children,
|
||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||
|
||||
this._findComponentDirectives(directiveAsts)
|
||||
.forEach(
|
||||
componentDirectiveAst => this._validateElementAnimationInputOutputs(
|
||||
componentDirectiveAst.hostProperties, componentDirectiveAst.hostEvents,
|
||||
componentDirectiveAst.directive.template));
|
||||
|
||||
const componentTemplate = providerContext.viewContext.component.template;
|
||||
this._validateElementAnimationInputOutputs(elementProps, events, componentTemplate);
|
||||
}
|
||||
|
||||
if (hasInlineTemplates) {
|
||||
@ -469,9 +477,36 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
|
||||
element.sourceSpan);
|
||||
}
|
||||
|
||||
return parsedElement;
|
||||
}
|
||||
|
||||
private _validateElementAnimationInputOutputs(
|
||||
inputs: BoundElementPropertyAst[], outputs: BoundEventAst[],
|
||||
template: CompileTemplateMetadata) {
|
||||
const triggerLookup = new Set<string>();
|
||||
template.animations.forEach(entry => { triggerLookup.add(entry.name); });
|
||||
|
||||
const animationInputs = inputs.filter(input => input.isAnimation);
|
||||
animationInputs.forEach(input => {
|
||||
const name = input.name;
|
||||
if (!triggerLookup.has(name)) {
|
||||
this._reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan);
|
||||
}
|
||||
});
|
||||
|
||||
outputs.forEach(output => {
|
||||
if (output.isAnimation) {
|
||||
const found = animationInputs.find(input => input.name == output.name);
|
||||
if (!found) {
|
||||
this._reportError(
|
||||
`Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`,
|
||||
output.sourceSpan);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _parseInlineTemplateBinding(
|
||||
attr: html.Attribute, targetMatchableAttrs: string[][],
|
||||
targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean {
|
||||
@ -533,7 +568,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
this._parseReference(identifier, value, srcSpan, targetRefs);
|
||||
|
||||
} else if (bindParts[KW_ON_IDX]) {
|
||||
this._parseEvent(
|
||||
this._parseEventOrAnimationEvent(
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
|
||||
} else if (bindParts[KW_BINDON_IDX]) {
|
||||
@ -544,7 +579,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
|
||||
} else if (bindParts[KW_AT_IDX]) {
|
||||
if (name[0] == '@' && isPresent(value) && value.length > 0) {
|
||||
if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) {
|
||||
this._reportError(
|
||||
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
||||
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
|
||||
@ -565,7 +600,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
targetAnimationProps);
|
||||
|
||||
} else if (bindParts[IDENT_EVENT_IDX]) {
|
||||
this._parseEvent(
|
||||
this._parseEventOrAnimationEvent(
|
||||
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
}
|
||||
} else {
|
||||
@ -608,7 +643,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[],
|
||||
targetAnimationProps: BoundElementPropertyAst[]) {
|
||||
const animatePropLength = ANIMATE_PROP_PREFIX.length;
|
||||
var isAnimationProp = name[0] == '@';
|
||||
var isAnimationProp = _isAnimationLabel(name);
|
||||
var animationPrefixLength = 1;
|
||||
if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) {
|
||||
isAnimationProp = true;
|
||||
@ -635,6 +670,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
if (!isPresent(expression) || expression.length == 0) {
|
||||
expression = 'null';
|
||||
}
|
||||
|
||||
const ast = this._parseBinding(expression, sourceSpan);
|
||||
targetMatchableAttrs.push([name, ast.source]);
|
||||
targetAnimationProps.push(new BoundElementPropertyAst(
|
||||
@ -662,20 +698,56 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
private _parseAssignmentEvent(
|
||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||
this._parseEvent(
|
||||
this._parseEventOrAnimationEvent(
|
||||
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
}
|
||||
|
||||
private _parseEventOrAnimationEvent(
|
||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||
if (_isAnimationLabel(name)) {
|
||||
name = name.substr(1);
|
||||
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
|
||||
} else {
|
||||
this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
}
|
||||
}
|
||||
|
||||
private _parseAnimationEvent(
|
||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetEvents: BoundEventAst[]) {
|
||||
const matches = splitAtPeriod(name, [name, '']);
|
||||
const eventName = matches[0];
|
||||
const phase = matches[1].toLowerCase();
|
||||
if (phase) {
|
||||
switch (phase) {
|
||||
case 'start':
|
||||
case 'done':
|
||||
const ast = this._parseAction(expression, sourceSpan);
|
||||
targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan));
|
||||
break;
|
||||
|
||||
default:
|
||||
this._reportError(
|
||||
`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`,
|
||||
sourceSpan);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._reportError(
|
||||
`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`,
|
||||
sourceSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private _parseEvent(
|
||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
||||
// long format: 'target: eventName'
|
||||
const parts = splitAtColon(name, [null, name]);
|
||||
const target = parts[0];
|
||||
const eventName = parts[1];
|
||||
const [target, eventName] = splitAtColon(name, [null, name]);
|
||||
const ast = this._parseAction(expression, sourceSpan);
|
||||
targetMatchableAttrs.push([name, ast.source]);
|
||||
targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
|
||||
targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan));
|
||||
// Don't detect directives for event names for now,
|
||||
// so don't add the event name to the matchableAttrs
|
||||
}
|
||||
@ -759,7 +831,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan,
|
||||
targetPropertyAsts: BoundElementPropertyAst[]) {
|
||||
if (hostProps) {
|
||||
StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => {
|
||||
Object.keys(hostProps).forEach(propName => {
|
||||
const expression = hostProps[propName];
|
||||
if (isString(expression)) {
|
||||
const exprAst = this._parseBinding(expression, sourceSpan);
|
||||
targetPropertyAsts.push(
|
||||
@ -777,9 +850,10 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan,
|
||||
targetEventAsts: BoundEventAst[]) {
|
||||
if (hostListeners) {
|
||||
StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => {
|
||||
Object.keys(hostListeners).forEach(propName => {
|
||||
const expression = hostListeners[propName];
|
||||
if (isString(expression)) {
|
||||
this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
|
||||
this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts);
|
||||
} else {
|
||||
this._reportError(
|
||||
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
||||
@ -796,13 +870,14 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
|
||||
boundProps.forEach(boundProp => {
|
||||
const prevValue = boundPropsByName.get(boundProp.name);
|
||||
if (isBlank(prevValue) || prevValue.isLiteral) {
|
||||
if (!prevValue || prevValue.isLiteral) {
|
||||
// give [a]="b" a higher precedence than a="b" on the same element
|
||||
boundPropsByName.set(boundProp.name, boundProp);
|
||||
}
|
||||
});
|
||||
|
||||
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
|
||||
Object.keys(directiveProperties).forEach(dirProp => {
|
||||
const elProp = directiveProperties[dirProp];
|
||||
const boundProp = boundPropsByName.get(elProp);
|
||||
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
@ -827,7 +902,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
});
|
||||
|
||||
props.forEach((prop: BoundElementOrDirectiveProperty) => {
|
||||
if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) {
|
||||
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
|
||||
boundElementProps.push(this._createElementPropertyAst(
|
||||
elementName, prop.name, prop.expression, prop.sourceSpan));
|
||||
}
|
||||
@ -846,7 +921,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
if (parts.length === 1) {
|
||||
var partValue = parts[0];
|
||||
if (partValue[0] == '@') {
|
||||
if (_isAnimationLabel(partValue)) {
|
||||
boundPropertyName = partValue.substr(1);
|
||||
bindingType = PropertyBindingType.Animation;
|
||||
securityContext = SecurityContext.NONE;
|
||||
@ -854,7 +929,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
||||
bindingType = PropertyBindingType.Property;
|
||||
this._assertNoEventBinding(boundPropertyName, sourceSpan);
|
||||
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false);
|
||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
|
||||
let errorMsg =
|
||||
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
|
||||
@ -869,7 +944,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
} else {
|
||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||
boundPropertyName = parts[1];
|
||||
this._assertNoEventBinding(boundPropertyName, sourceSpan);
|
||||
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, true);
|
||||
// NB: For security purposes, use the mapped property name, not the attribute name.
|
||||
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
|
||||
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
|
||||
@ -902,24 +977,29 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
|
||||
}
|
||||
|
||||
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void {
|
||||
if (propName.toLowerCase().startsWith('on')) {
|
||||
this._reportError(
|
||||
`Binding to event attribute '${propName}' is disallowed ` +
|
||||
`for security reasons, please use (${propName.slice(2)})=...`,
|
||||
sourceSpan, ParseErrorLevel.FATAL);
|
||||
|
||||
/**
|
||||
* @param propName the name of the property / attribute
|
||||
* @param sourceSpan
|
||||
* @param isAttr true when binding to an attribute
|
||||
* @private
|
||||
*/
|
||||
private _validatePropertyOrAttributeName(
|
||||
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
|
||||
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
|
||||
this._schemaRegistry.validateProperty(propName);
|
||||
if (report.error) {
|
||||
this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
|
||||
return directives.filter(directive => directive.directive.isComponent);
|
||||
}
|
||||
|
||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
||||
const componentTypeNames: string[] = [];
|
||||
directives.forEach(directive => {
|
||||
const typeName = directive.directive.type.name;
|
||||
if (directive.directive.isComponent) {
|
||||
componentTypeNames.push(typeName);
|
||||
}
|
||||
});
|
||||
return componentTypeNames;
|
||||
return this._findComponentDirectives(directives)
|
||||
.map(directive => directive.directive.type.name);
|
||||
}
|
||||
|
||||
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
||||
@ -969,7 +1049,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
const allDirectiveEvents = new Set<string>();
|
||||
|
||||
directives.forEach(directive => {
|
||||
StringMapWrapper.forEach(directive.directive.outputs, (eventName: string) => {
|
||||
Object.keys(directive.directive.outputs).forEach(k => {
|
||||
const eventName = directive.directive.outputs[k];
|
||||
allDirectiveEvents.add(eventName);
|
||||
});
|
||||
});
|
||||
@ -1103,3 +1184,7 @@ export class PipeCollector extends RecursiveAstVisitor {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _isAnimationLabel(name: string): boolean {
|
||||
return name[0] == '@';
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {CompileTokenMetadata} from './compile_metadata';
|
||||
import {StringMapWrapper} from './facade/collection';
|
||||
import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||
import * as o from './output/output_ast';
|
||||
|
||||
@ -17,13 +16,21 @@ var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
|
||||
export function camelCaseToDashCase(input: string): string {
|
||||
return StringWrapper.replaceAllMapped(
|
||||
input, CAMEL_CASE_REGEXP, (m: string[]) => { return '-' + m[1].toLowerCase(); });
|
||||
input, CAMEL_CASE_REGEXP, (m: string[]) => '-' + m[1].toLowerCase());
|
||||
}
|
||||
|
||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||
const colonIndex = input.indexOf(':');
|
||||
if (colonIndex == -1) return defaultValues;
|
||||
return [input.slice(0, colonIndex).trim(), input.slice(colonIndex + 1).trim()];
|
||||
return _splitAt(input, ':', defaultValues);
|
||||
}
|
||||
|
||||
export function splitAtPeriod(input: string, defaultValues: string[]): string[] {
|
||||
return _splitAt(input, '.', defaultValues);
|
||||
}
|
||||
|
||||
function _splitAt(input: string, character: string, defaultValues: string[]): string[] {
|
||||
const characterIndex = input.indexOf(character);
|
||||
if (characterIndex == -1) return defaultValues;
|
||||
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
|
||||
}
|
||||
|
||||
export function sanitizeIdentifier(name: string): string {
|
||||
@ -55,9 +62,8 @@ export class ValueTransformer implements ValueVisitor {
|
||||
}
|
||||
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||
var result = {};
|
||||
StringMapWrapper.forEach(map, (value: any /** TODO #9100 */, key: any /** TODO #9100 */) => {
|
||||
(result as any /** TODO #9100 */)[key] = visitValue(value, this, context);
|
||||
});
|
||||
Object.keys(map).forEach(
|
||||
key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); });
|
||||
return result;
|
||||
}
|
||||
visitPrimitive(value: any, context: any): any { return value; }
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {ListWrapper, MapWrapper} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {convertValueToOutputAst} from '../output/value_util';
|
||||
@ -27,7 +27,7 @@ export class CompileNode {
|
||||
public parent: CompileElement, public view: CompileView, public nodeIndex: number,
|
||||
public renderNode: o.Expression, public sourceAst: TemplateAst) {}
|
||||
|
||||
isNull(): boolean { return isBlank(this.renderNode); }
|
||||
isNull(): boolean { return !this.renderNode; }
|
||||
|
||||
isRootElement(): boolean { return this.view != this.parent.view; }
|
||||
}
|
||||
@ -117,7 +117,7 @@ export class CompileElement extends CompileNode {
|
||||
setComponentView(compViewExpr: o.Expression) {
|
||||
this._compViewExpr = compViewExpr;
|
||||
this.contentNodesByNgContentIndex =
|
||||
ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length);
|
||||
new Array(this.component.template.ngContentSelectors.length);
|
||||
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||
this.contentNodesByNgContentIndex[i] = [];
|
||||
}
|
||||
@ -192,7 +192,7 @@ export class CompileElement extends CompileNode {
|
||||
queriesWithReads,
|
||||
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
|
||||
});
|
||||
StringMapWrapper.forEach(this.referenceTokens, (_: CompileTokenMetadata, varName: string) => {
|
||||
Object.keys(this.referenceTokens).forEach(varName => {
|
||||
var token = this.referenceTokens[varName];
|
||||
var varValue: o.Expression;
|
||||
if (isPresent(token)) {
|
||||
@ -313,12 +313,12 @@ export class CompileElement extends CompileNode {
|
||||
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
|
||||
var result: o.Expression = null;
|
||||
// constructor content query
|
||||
if (isBlank(result) && isPresent(dep.query)) {
|
||||
if (!result && isPresent(dep.query)) {
|
||||
result = this._addQuery(dep.query, null).queryList;
|
||||
}
|
||||
|
||||
// constructor view query
|
||||
if (isBlank(result) && isPresent(dep.viewQuery)) {
|
||||
if (!result && isPresent(dep.viewQuery)) {
|
||||
result = createQueryList(
|
||||
dep.viewQuery, null,
|
||||
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
|
||||
@ -328,7 +328,7 @@ export class CompileElement extends CompileNode {
|
||||
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtins with special visibility
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
if (dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
|
||||
if (requestingProviderType === ProviderAstType.Component) {
|
||||
@ -339,7 +339,7 @@ export class CompileElement extends CompileNode {
|
||||
}
|
||||
}
|
||||
// access regular providers on the element
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
let resolvedProvider = this._resolvedProviders.get(dep.token.reference);
|
||||
// don't allow directives / public services to access private services.
|
||||
// only components and private services can access private services.
|
||||
@ -361,20 +361,20 @@ export class CompileElement extends CompileNode {
|
||||
if (dep.isValue) {
|
||||
result = o.literal(dep.value);
|
||||
}
|
||||
if (isBlank(result) && !dep.isSkipSelf) {
|
||||
if (!result && !dep.isSkipSelf) {
|
||||
result = this._getLocalDependency(requestingProviderType, dep);
|
||||
}
|
||||
// check parent elements
|
||||
while (isBlank(result) && !currElement.parent.isNull()) {
|
||||
while (!result && !currElement.parent.isNull()) {
|
||||
currElement = currElement.parent;
|
||||
result = currElement._getLocalDependency(
|
||||
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
|
||||
}
|
||||
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
result = injectFromViewParentInjector(dep.token, dep.isOptional);
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
if (!result) {
|
||||
result = o.NULL_EXPR;
|
||||
}
|
||||
return getPropertyInView(result, this.view, currElement.view);
|
||||
@ -411,7 +411,7 @@ function createProviderProperty(
|
||||
resolvedProviderValueExpr = providerValueExpressions[0];
|
||||
type = providerValueExpressions[0].type;
|
||||
}
|
||||
if (isBlank(type)) {
|
||||
if (!type) {
|
||||
type = o.DYNAMIC_TYPE;
|
||||
}
|
||||
if (isEager) {
|
||||
|
@ -8,8 +8,7 @@
|
||||
|
||||
|
||||
import {CompilePipeMetadata} from '../compile_metadata';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
@ -23,7 +22,7 @@ export class CompilePipe {
|
||||
if (meta.pure) {
|
||||
// pure pipes live on the component view
|
||||
pipe = compView.purePipes.get(name);
|
||||
if (isBlank(pipe)) {
|
||||
if (!pipe) {
|
||||
pipe = new CompilePipe(compView, meta);
|
||||
compView.purePipes.set(name, pipe);
|
||||
compView.pipes.push(pipe);
|
||||
@ -85,7 +84,7 @@ function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isBlank(pipeMeta)) {
|
||||
if (!pipeMeta) {
|
||||
throw new Error(
|
||||
`Illegal state: Could not find pipe ${name} although the parser should have detected this error!`);
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {CompileQueryMetadata} from '../compile_metadata';
|
||||
import {ListWrapper} 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';
|
||||
|
||||
@ -101,9 +101,8 @@ function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
|
||||
function mapNestedViews(
|
||||
declarationAppElement: o.Expression, view: CompileView,
|
||||
expressions: o.Expression[]): o.Expression {
|
||||
var adjustedExpressions: o.Expression[] = expressions.map((expr) => {
|
||||
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr);
|
||||
});
|
||||
var adjustedExpressions: o.Expression[] = expressions.map(
|
||||
(expr) => o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr));
|
||||
return declarationAppElement.callMethod('mapNestedViews', [
|
||||
o.variable(view.className),
|
||||
o.fn(
|
||||
@ -129,7 +128,7 @@ export function createQueryList(
|
||||
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
|
||||
query.meta.selectors.forEach((selector) => {
|
||||
var entry = map.get(selector.reference);
|
||||
if (isBlank(entry)) {
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(selector.reference, entry);
|
||||
}
|
||||
|
@ -6,15 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompiledAnimationTriggerResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {ListWrapper, MapWrapper} 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 {ViewType} from '../private_import_core';
|
||||
import {createDiTokenExpression} from '../util';
|
||||
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
@ -23,7 +22,7 @@ import {CompilePipe} from './compile_pipe';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {EventHandlerVars} from './constants';
|
||||
import {NameResolver} from './expression_converter';
|
||||
import {createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewParentInjector} from './util';
|
||||
import {createPureProxy, getPropertyInView, getViewFactoryName} from './util';
|
||||
|
||||
export class CompileView implements NameResolver {
|
||||
public viewType: ViewType;
|
||||
@ -72,7 +71,7 @@ export class CompileView implements NameResolver {
|
||||
constructor(
|
||||
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
|
||||
public animations: CompiledAnimationTriggerResult[], public viewIndex: number,
|
||||
public animations: AnimationEntryCompileResult[], public viewIndex: number,
|
||||
public declarationElement: CompileElement, public templateVariableBindings: string[][]) {
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.animationBindingsMethod = new CompileMethod(this);
|
||||
@ -139,7 +138,7 @@ export class CompileView implements NameResolver {
|
||||
}
|
||||
var currView: CompileView = this;
|
||||
var result = currView.locals.get(name);
|
||||
while (isBlank(result) && isPresent(currView.declarationElement.view)) {
|
||||
while (!result && isPresent(currView.declarationElement.view)) {
|
||||
currView = currView.declarationElement.view;
|
||||
result = currView.locals.get(name);
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
|
@ -7,11 +7,9 @@
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import {StringWrapper, isPresent} from '../facade/lang';
|
||||
import {identifierToken} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {AnimationOutput} from '../private_import_core';
|
||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileBinding} from './compile_binding';
|
||||
@ -20,10 +18,6 @@ import {CompileMethod} from './compile_method';
|
||||
import {EventHandlerVars, ViewProperties} from './constants';
|
||||
import {convertCdStatementToIr} from './expression_converter';
|
||||
|
||||
export class CompileElementAnimationOutput {
|
||||
constructor(public listener: CompileEventListener, public output: AnimationOutput) {}
|
||||
}
|
||||
|
||||
export class CompileEventListener {
|
||||
private _method: CompileMethod;
|
||||
private _hasComponentHostListener: boolean = false;
|
||||
@ -32,13 +26,14 @@ export class CompileEventListener {
|
||||
private _actionResultExprs: o.Expression[] = [];
|
||||
|
||||
static getOrCreate(
|
||||
compileElement: CompileElement, eventTarget: string, eventName: string,
|
||||
compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string,
|
||||
targetEventListeners: CompileEventListener[]): CompileEventListener {
|
||||
var listener = targetEventListeners.find(
|
||||
listener => listener.eventTarget == eventTarget && listener.eventName == eventName);
|
||||
if (isBlank(listener)) {
|
||||
listener => listener.eventTarget == eventTarget && listener.eventName == eventName &&
|
||||
listener.eventPhase == eventPhase);
|
||||
if (!listener) {
|
||||
listener = new CompileEventListener(
|
||||
compileElement, eventTarget, eventName, targetEventListeners.length);
|
||||
compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length);
|
||||
targetEventListeners.push(listener);
|
||||
}
|
||||
return listener;
|
||||
@ -48,7 +43,7 @@ export class CompileEventListener {
|
||||
|
||||
constructor(
|
||||
public compileElement: CompileElement, public eventTarget: string, public eventName: string,
|
||||
listenerIndex: number) {
|
||||
public eventPhase: string, listenerIndex: number) {
|
||||
this._method = new CompileMethod(compileElement.view);
|
||||
this._methodName =
|
||||
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
||||
@ -119,7 +114,7 @@ export class CompileEventListener {
|
||||
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||
}
|
||||
|
||||
listenToAnimation(output: AnimationOutput) {
|
||||
listenToAnimation() {
|
||||
var outputListener = o.THIS_EXPR.callMethod(
|
||||
'eventHandler',
|
||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
||||
@ -129,11 +124,8 @@ export class CompileEventListener {
|
||||
.callMethod(
|
||||
'registerAnimationOutput',
|
||||
[
|
||||
this.compileElement.renderNode,
|
||||
o.importExpr(resolveIdentifier(Identifiers.AnimationOutput)).instantiate([
|
||||
o.literal(output.name), o.literal(output.phase)
|
||||
]),
|
||||
outputListener
|
||||
this.compileElement.renderNode, o.literal(this.eventName),
|
||||
o.literal(this.eventPhase), outputListener
|
||||
])
|
||||
.toStmt();
|
||||
this.compileElement.view.createMethod.addStmt(stmt);
|
||||
@ -160,7 +152,7 @@ export function collectEventListeners(
|
||||
hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(
|
||||
compileElement, hostEvent.target, hostEvent.name, eventListeners);
|
||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
||||
listener.addAction(hostEvent, null, null);
|
||||
});
|
||||
dirs.forEach((directiveAst) => {
|
||||
@ -169,7 +161,7 @@ export function collectEventListeners(
|
||||
directiveAst.hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(
|
||||
compileElement, hostEvent.target, hostEvent.name, eventListeners);
|
||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
||||
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
||||
});
|
||||
});
|
||||
@ -180,21 +172,22 @@ export function collectEventListeners(
|
||||
export function bindDirectiveOutputs(
|
||||
directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||
eventListeners: CompileEventListener[]) {
|
||||
StringMapWrapper.forEach(
|
||||
directiveAst.directive.outputs,
|
||||
(eventName: any /** TODO #9100 */, observablePropName: any /** TODO #9100 */) => {
|
||||
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
|
||||
listener.listenToDirective(directiveInstance, observablePropName);
|
||||
});
|
||||
});
|
||||
Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
|
||||
const eventName = directiveAst.directive.outputs[observablePropName];
|
||||
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
|
||||
listener.listenToDirective(directiveInstance, observablePropName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
|
||||
eventListeners.forEach(listener => listener.listenToRenderer());
|
||||
}
|
||||
|
||||
export function bindAnimationOutputs(eventListeners: CompileElementAnimationOutput[]) {
|
||||
eventListeners.forEach(entry => { entry.listener.listenToAnimation(entry.output); });
|
||||
eventListeners.forEach(listener => {
|
||||
if (listener.eventPhase) {
|
||||
listener.listenToAnimation();
|
||||
} else {
|
||||
listener.listenToRenderer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user