Compare commits

...

70 Commits

Author SHA1 Message Date
91f7aa3b15 docs(CHANGELOG): add changelog for 2.0.2 2016-10-05 16:25:04 -07:00
58b8091097 docs(readme): remove incorrect download count badge
npm doesn't currently support download counts for scoped packages, so there is no replacement right now.
2016-10-05 15:33:16 -07:00
0fde7ecd0f refactor: add license header to JS files & format files (#12081) 2016-10-05 15:33:16 -07:00
d25cd244af refactor(benchmarks): refactor to support AOT bootstrap in G3 (#12075) 2016-10-05 15:33:16 -07:00
e00de0c606 fix(forms): properly validate empty strings with patterns (#11450) 2016-10-05 15:33:16 -07:00
205103bdaa docs(traige): update triaging doc 2016-10-05 15:33:16 -07:00
0528dcb9dc refactor: simplify arrow functions (#12057) 2016-10-05 15:33:16 -07:00
6f7ed32154 fix(ShadowCss): fix attribute selectors in :host and :host-context (#12056)
Fix a regression introduced in #11917 while fixing #6249
2016-10-05 15:33:16 -07:00
1bd8ba80a7 refactor(facade): Remove most of StringMapWrapper facade. (#12022)
This change mostly automated by
12012b07a2
with some manual fixes.
2016-10-05 15:33:16 -07:00
adb17fed98 fix(Header): preserve case of the first init, set() or append() (#12023)
fixes #11624
2016-10-05 15:33:16 -07:00
3067ce6eb4 docs(NgModule): Fixed docs for NgModule.entryComponents (#12006)
* docs(NgModule): Corrected the wording of the documentation of `entryComponents`, fixed some minor grammar issues

* docs(NgModule): Remove redundant ComponentFactory mentions

* docs(NgModule): Restore ComponentFactory/ComponentResolver links
2016-10-05 15:33:16 -07:00
e102cd4757 docs(DEVELOPER.md): fix typos on "Tests" section (#12029) 2016-10-05 15:33:16 -07:00
db00ba7ae7 refactor(facade): remove DateWrapper (#12027) 2016-10-05 15:33:16 -07:00
5a8f116687 text(offline compiler): fix expected output 2016-10-05 15:33:16 -07:00
02a862f8af test(AstSerializer): fix serializing void tags 2016-10-05 15:33:16 -07:00
7578d8573b fix(xlif): fix <x> ctype names
fixes #12000
see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#ctype
2016-10-05 15:33:16 -07:00
de56e31cf0 style(I18N): Carriage returns in serialized files 2016-10-05 15:33:16 -07:00
eb85a7709a docs(gh): try to improve the issue template (#11891) 2016-10-05 15:33:16 -07:00
c99ef4938f fix(ShadowCss): support @page and @document CSS rules (#11878)
fixes #11860
2016-10-05 15:33:16 -07:00
7395400066 fix(ShadowCss): support quoted attribute values
fixes #6085
2016-10-05 15:33:16 -07:00
d985cc0253 refactor(ShadowCss): add missing types 2016-10-05 15:33:16 -07:00
aca117ac56 fix(ShadowCss): fix :host(tag) and :host-context(tag)
fixes #11972
2016-10-05 15:33:16 -07:00
2c3825f2c8 fix(BrowserAdapter): correctly removes styles on IE
fixes #7916
2016-10-05 15:33:16 -07:00
ea6defcf11 refactor(BrowserAdapter): cleanup 2016-10-05 15:33:16 -07:00
975aca95bb refactor(CssSelector): misc cleanup 2016-10-05 15:33:16 -07:00
5cb78566c2 docs(CssSelector): [name*=value] is not supported
fixes #6042
2016-10-05 15:33:16 -07:00
9bacb32ec5 feat(Parse5): update to the latest version 2.2.1
fixes #6237
2016-10-05 15:33:16 -07:00
6970991546 docs: update docs for ng2_ftl benchmark 2016-10-05 15:33:16 -07:00
bd012efcc4 fix(ShadowCss): support [attr="value with space"]
fixes #6249
2016-10-05 15:33:16 -07:00
2dd399658d refactor(ShadowCss): cleanup 2016-10-05 15:33:16 -07:00
d0dea574cb test(DirectiveResolver): test that a prop can have both @Input and @HostBinding 2016-10-05 15:33:16 -07:00
f7864edd4b refactor(DirectiveResolver): cleanup 2016-10-05 15:33:16 -07:00
9cc0a4ed10 fix(UrlSearchParams): change a behavior when a param value is null or undefined (#11990) 2016-10-05 15:33:16 -07:00
3ee8c75eff refactor(routerLinkActive): optimised routerLinkActive active check code (#11968)
Modify routerLinkActive to optimise performance by removing unnecessary iteration. By replacing Array.reduce with Array.some, the loop will break when it finds an active link. Useful if used on the parent of a large group of routerLinks. Furthermore, if a RouterLink is active it will not check the RouterLinkWithHrefs.
2016-10-05 15:33:16 -07:00
b39d3a173e refactor(facade): Inline isBlank called with object-type argument (#11992) 2016-10-05 15:33:16 -07:00
a4af1561b7 benchmarks: add ng2_ftl and ng2_switch_ftl benchmarks (#11963)
These benchmarks take the output of AoT
and manually tweaks it to explore possible
future changes to the compiler to produce
this output directly.
2016-10-05 15:33:16 -07:00
0851238e78 fix(upgrade): bind optional properties when upgrading from ng1 (#11411)
Previously, optional properties of a directive/component would be wrongly mapped and thus ignored.

Closes #10181
2016-10-05 15:33:16 -07:00
d2d98dad61 docs(forms): Added FormControl initialization information (#11948) 2016-10-05 15:33:16 -07:00
826c98e50c fix(ngc): allow ReflectorHost passed as argument to CodeGenerator#create (#11951) 2016-10-05 15:33:16 -07:00
830e6352dd chore(lint): remove unused imports (#11923)
This was done automatically by tslint, which can now fix issues it finds.
The fixer is still pending in PR https://github.com/palantir/tslint/pull/1568
Also I have a local bugfix for https://github.com/palantir/tslint/issues/1569
which causes too many imports to be deleted.
2016-10-05 15:33:16 -07:00
5911c3bd43 fix(compiler): move detection of unsafe properties for binding to ElementSchemaRegistry (#11378) 2016-10-05 15:33:16 -07:00
51e1994af0 fix(compiler): Do not embed templateUrl in view factories in non-debug mode. (#11818)
Fixes #11117.
2016-10-05 15:33:16 -07:00
faf5d90bf6 refactor: remove dead code 2016-10-05 15:33:16 -07:00
7e5413da89 refactor(facade): remove useless facades 2016-10-05 15:33:16 -07:00
e82a78e641 docs(upgrade): rename undeclared Ng2 to Ng2Component (#11950) 2016-10-05 15:33:16 -07:00
c13e55c8c3 (docs): removing addProvider from UpgradeAdapter (#11934)
The `addProvider` function in the `UpgradeAdapter` was deprecated in this [commit](d21331e902 (diff-77163e956a7842149f583846c1c01651)) and has been removed in final. Given this, the documentation for downgrading ng2 providers for use in ng1 is invalid.
2016-10-05 15:33:16 -07:00
a153504212 docs(router_config): add missing quote (#11925) 2016-10-05 15:33:16 -07:00
b8a75818ee docs: remove outdated docs (#11875) 2016-10-05 15:33:16 -07:00
f633826e99 chore(CHANGELOG): fix wrong issue link (#11871) 2016-10-05 15:33:16 -07:00
0a8887240a docs(ExceptionHandler): fix API docs (#11772)
fixes #11769
2016-10-05 15:33:16 -07:00
6d606dd02b ci(travis): increase node's heap size to prevent OOM on travis (#11869) 2016-10-05 15:33:16 -07:00
5c4215ccb4 Fix(http): invalidStateError if response body without content (#11786)
Fix(http): invalidStateError if response body without content
If the responseType has been specified and other than 'text', responseText throw an InvalidStateError exception

See XHR doc => https://xhr.spec.whatwg.org/#the-responsetext-attribute

Unit Test to prevent invalidStateError
2016-10-05 15:33:16 -07:00
85489a166e refactor(animations): ensure animation input/outputs are managed within the template parser (#11782)
Closes #11782
Closes #11601
Related #11707
2016-10-05 15:33:16 -07:00
cf750e17ed fix(router): do not reset the router state when updating the component (#11867) 2016-09-24 07:14:06 +09:00
712d1a7c37 chore(release): v2.0.1 2016-09-23 11:28:36 -07:00
16601f9359 docs(changelog): add changelog for 2.0.1 2016-09-23 10:50:42 -07:00
b81e2e7a31 fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808)
fixes #11280
2016-09-23 10:48:47 -07:00
98fac36706 docs(Component): API docs for .encapsulation and .interpolation 2016-09-23 10:22:24 -07:00
3e780c032e refactor: misc cleanup 2016-09-23 10:22:05 -07:00
e09882180e refactor(common): cleanup (#11668) 2016-09-23 10:21:58 -07:00
0e18c57a17 docs(core): mark TestBed as stable api and add preliminary docs (#11767)
TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable.
2016-09-23 10:21:50 -07:00
51e2b9c073 docs(contributing): remove preview references (#11795) 2016-09-23 10:21:44 -07:00
f218e240d3 ci(BrowserStack): add Safari 10 (#11796) 2016-09-23 10:21:31 -07:00
af6b219f8e refactor(TemplateParser): clearer error message for on* binding (#11802)
fixes #11756
2016-09-23 10:21:15 -07:00
20addf5f9f chore(ISSUE_TEMPLATE): update Angular version field (#11821) 2016-09-23 10:21:02 -07:00
2860418a3c fix(forms): disable all radios with disable() 2016-09-23 10:19:58 -07:00
39e251eea7 fix(forms): support unbound disabled in ngModel (#11736) 2016-09-23 10:19:46 -07:00
d7d716d5db chore(zone.js): update to 0.6.25 (#11725) 2016-09-23 10:19:30 -07:00
a95d65241c fix(compiler): Safe property access expressions work in event bindings (#11724) 2016-09-23 10:19:24 -07:00
fdb22bd185 refactor: misc cleanup (#11654) 2016-09-23 10:18:38 -07:00
488 changed files with 7407 additions and 6884 deletions

View File

@ -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") **I'm submitting a ...** (check one with "x")
``` ```
[ ] bug report => search github for a similar issue or PR before submitting [ ] bug report => search github for a similar issue or PR before submitting
@ -11,8 +15,12 @@
**Expected behavior** **Expected behavior**
<!-- Describe what the behavior would be without the bug. --> <!-- Describe what the behavior would be without the bug. -->
**Reproduction of the problem** **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). --> <!--
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?** **What is the motivation / use case for changing the behavior?**
<!-- Describe the motivation or the concrete use case --> <!-- Describe the motivation or the concrete use case -->
@ -20,7 +28,7 @@
**Please tell us about your environment:** **Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... --> <!-- 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 --> <!-- 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 ]

View File

@ -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> <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) # [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)

View File

@ -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? ## <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] 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? ## <a name="issue"></a> Found an Issue?
If you find a bug in the source code, you can help us by 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? ## <a name="feature"></a> Want a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub 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 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 a proposal for your work first, to be sure that we can use it.
and we are not ready to accept major contributions ahead of the full release.
Please consider what kind of change it is: 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 * For a **Major Feature**, first open an issue and outline your proposal so that it can be

View File

@ -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. 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, - 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 ## Update the public API tests

View File

@ -4,7 +4,6 @@
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular) [![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular) [![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore) [![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci) [![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].* *Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*

View File

@ -17,9 +17,9 @@ with it.
* `comp: animations`: `@matsko` * `comp: animations`: `@matsko`
* `comp: benchpress`: `@tbosch` * `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: 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. intertwined, we will be treating them as one.
* `comp: forms`: `@kara` * `comp: forms`: `@kara`
* `comp: http`: `@jeffbcross` * `comp: http`: `@jeffbcross`
@ -29,14 +29,14 @@ with it.
* `comp: testing`: `@juliemr` * `comp: testing`: `@juliemr`
* `comp: upgrade`: `@mhevery` * `comp: upgrade`: `@mhevery`
* `comp: web-worker`: `@vicb` * `comp: web-worker`: `@vicb`
* `comp: zone`: `@mhevery` * `comp: zones`: `@mhevery`
There are few components which are cross-cutting. They don't have 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 a clear location in the source tree. We will treat them as a component
even thought no specific source tree is associated with them. even thought no specific source tree is associated with them.
* `comp: documentation`: `@naomiblack` * `comp: documentation`: `@naomiblack`
* `comp: packaging`: `@mhevery` * `comp: packaging`: `@IgorMinar`
* `comp: performance`: `@tbosch` * `comp: performance`: `@tbosch`
* `comp: security`: `@IgorMinar` * `comp: security`: `@IgorMinar`

View File

@ -1,6 +1,16 @@
// 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 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 = { var CIconfiguration = {
'Chrome': {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}}, 'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
@ -22,195 +32,67 @@ var CIconfiguration = {
'Safari7': {unitTest: {target: 'BS', 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}}, 'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': {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}}, 'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': {unitTest: {target: 'BS', required: false}, 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}}, '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}} 'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
}; };
var customLaunchers = { var customLaunchers = {
'DartiumWithWebPlatform': { 'DartiumWithWebPlatform':
base: 'Dartium', {base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
flags: ['--enable-experimental-web-platform-features'] }, 'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
'ChromeNoSandbox': { 'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
base: 'Chrome', 'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
flags: ['--no-sandbox'] }, 'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
'SL_CHROME': { 'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
base: 'SauceLabs', 'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
browserName: 'chrome', 'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
version: '52' '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_CHROMEBETA': { 'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'},
base: 'SauceLabs', 'SL_SAFARI10':
browserName: 'chrome', {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'},
version: 'beta' '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_CHROMEDEV': { 'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
base: 'SauceLabs', 'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
browserName: 'chrome', 'SL_IE9':
version: 'dev' {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
},
'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'
},
'SL_IE10': { 'SL_IE10': {
base: 'SauceLabs', base: 'SauceLabs',
browserName: 'internet explorer', browserName: 'internet explorer',
platform: 'Windows 2012', platform: 'Windows 2012',
version: '10' version: '10'
}, },
'SL_IE11': { 'SL_IE11':
base: 'SauceLabs', {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
browserName: 'internet explorer',
platform: 'Windows 8.1',
version: '11'
},
'SL_EDGE': { 'SL_EDGE': {
base: 'SauceLabs', base: 'SauceLabs',
browserName: 'MicrosoftEdge', browserName: 'MicrosoftEdge',
platform: 'Windows 10', platform: 'Windows 10',
version: '13.10586' version: '13.10586'
}, },
'SL_ANDROID4.1': { 'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'},
base: 'SauceLabs', 'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'},
browserName: 'android', 'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'},
platform: 'Linux', 'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
version: '4.1' 'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.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': { 'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
base: 'BrowserStack', 'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
browser: 'chrome', 'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'},
os: 'OS X', 'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'},
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_FIREFOX': { 'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'},
base: 'BrowserStack', 'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'},
browser: 'firefox', 'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'},
os: 'Windows', 'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
os_version: '10' 'BS_IE9':
}, {base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
'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_IE10': { 'BS_IE10': {
base: 'BrowserStack', base: 'BrowserStack',
browser: 'ie', browser: 'ie',
@ -225,58 +107,35 @@ var customLaunchers = {
os: 'Windows', os: 'Windows',
os_version: '10' os_version: '10'
}, },
'BS_EDGE': { 'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
base: 'BrowserStack', 'BS_WINDOWSPHONE':
browser: 'edge', {base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'},
os: 'Windows', 'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'},
os_version: '10' 'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'},
}, 'BS_ANDROID4.3':
'BS_WINDOWSPHONE' : { {base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'},
base: 'BrowserStack', 'BS_ANDROID4.2':
device: 'Nokia Lumia 930', {base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'},
os: 'winphone', 'BS_ANDROID4.1':
os_version: '8.1' {base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.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 = { var sauceAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}), 'ALL': Object.keys(customLaunchers).filter(function(item) {
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], return customLaunchers[item].base == 'SauceLabs';
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'], }),
'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'], 'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'], 'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'], 'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], 'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'], 'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'], 'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true), 'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
@ -284,13 +143,20 @@ var sauceAliases = {
}; };
var browserstackAliases = { var browserstackAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}), 'ALL': Object.keys(customLaunchers).filter(function(item) {
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], return customLaunchers[item].base == 'BrowserStack';
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'], }),
'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'], 'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'], 'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'], 'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], 'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true), 'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false) 'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
}; };
@ -307,7 +173,5 @@ function buildConfiguration(type, target, required) {
var conf = CIconfiguration[item][type]; var conf = CIconfiguration[item][type];
return conf.required === required && conf.target === target; return conf.required === required && conf.target === target;
}) })
.map((item) => { .map((item) => target + '_' + item.toUpperCase());
return target + '_' + item.toUpperCase();
});
} }

View File

@ -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'; 'use strict';
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE // THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
@ -13,12 +21,14 @@ const os = require('os');
// clang-format entry points // clang-format entry points
const srcsToFmt = [ const srcsToFmt = [
'modules/@angular/**/*.ts', 'modules/@angular/**/*.{js,ts}',
'modules/benchmarks/**/*.ts', 'modules/benchmarks/**/*.{js,ts}',
'modules/e2e_util/**/*.ts', 'modules/e2e_util/**/*.{js,ts}',
'modules/playground/**/*.ts', 'modules/playground/**/*.{js,ts}',
'tools/**/*.ts', 'tools/**/*.{js,ts}',
'!tools/public_api_guard/**/*.d.ts', '!tools/public_api_guard/**/*.d.ts',
'./*.{js,ts}',
'!shims_for_IE.js',
]; ];
// Check source code for formatting errors (clang-format) // Check source code for formatting errors (clang-format)
@ -33,8 +43,9 @@ gulp.task('format:enforce', () => {
gulp.task('format', () => { gulp.task('format', () => {
const format = require('gulp-clang-format'); const format = require('gulp-clang-format');
const clangFormat = require('clang-format'); const clangFormat = require('clang-format');
return gulp.src(srcsToFmt, { base: '.' }).pipe( return gulp.src(srcsToFmt, {base: '.'})
format.format('file', clangFormat)).pipe(gulp.dest('.')); .pipe(format.format('file', clangFormat))
.pipe(gulp.dest('.'));
}); });
const entrypoints = [ const entrypoints = [
@ -62,12 +73,18 @@ const entrypoints = [
]; ];
const publicApiDir = path.normalize('tools/public_api_guard'); const publicApiDir = path.normalize('tools/public_api_guard');
const publicApiArgs = [ const publicApiArgs = [
'--rootDir', 'dist/packages-dist', '--rootDir',
'--stripExportPattern', '^__', 'dist/packages-dist',
'--allowModuleIdentifiers', 'jasmine', '--stripExportPattern',
'--allowModuleIdentifiers', 'protractor', '^__',
'--allowModuleIdentifiers', 'angular', '--allowModuleIdentifiers',
'--onStabilityMissing', 'error', 'jasmine',
'--allowModuleIdentifiers',
'protractor',
'--allowModuleIdentifiers',
'angular',
'--onStabilityMissing',
'error',
].concat(entrypoints); ].concat(entrypoints);
// Build angular // Build angular
@ -107,14 +124,18 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
.on('close', done); .on('close', done);
}); });
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the focused tests is found. // Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded tests in our code base. // 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() { gulp.task('check-tests', function() {
const ddescribeIit = require('gulp-ddescribe-iit'); const ddescribeIit = require('gulp-ddescribe-iit');
return gulp.src([ return gulp
.src([
'modules/**/*.spec.ts', 'modules/**/*.spec.ts',
'modules/**/*_spec.ts', 'modules/**/*_spec.ts',
]).pipe(ddescribeIit({allowDisabledTests: true})); ])
.pipe(ddescribeIit({allowDisabledTests: true}));
}); });
// Check the coding standards and programming errors // Check the coding standards and programming errors
@ -123,7 +144,14 @@ gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
// Built-in rules are at // Built-in rules are at
// https://github.com/palantir/tslint#supported-rules // https://github.com/palantir/tslint#supported-rules
const tslintConfig = require('./tslint.json'); const tslintConfig = require('./tslint.json');
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts']) 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({ .pipe(tslint({
tslint: require('tslint').default, tslint: require('tslint').default,
configuration: tslintConfig, configuration: tslintConfig,
@ -142,7 +170,7 @@ gulp.task('check-cycle', (done) => {
const dependencyObject = madge(['dist/all/'], { const dependencyObject = madge(['dist/all/'], {
format: 'cjs', format: 'cjs',
extensions: ['.js'], 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(); const circularDependencies = dependencyObject.circular().getArray();
if (circularDependencies.length > 0) { if (circularDependencies.length > 0) {
@ -187,10 +215,7 @@ gulp.task('changelog', () => {
const conventionalChangelog = require('gulp-conventional-changelog'); const conventionalChangelog = require('gulp-conventional-changelog');
return gulp.src('CHANGELOG.md') return gulp.src('CHANGELOG.md')
.pipe(conventionalChangelog({ .pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
preset: 'angular',
releaseCount: 1
}, {
// Conventional Changelog Context // Conventional Changelog Context
// We have to manually set version number so it doesn't get prefixed with `v` // 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 // See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
@ -205,8 +230,7 @@ function tsc(projectPath, done) {
childProcess childProcess
.spawn( .spawn(
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)), path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
['-p', path.join(__dirname, projectPath)], ['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
{stdio: 'inherit'})
.on('close', done); .on('close', done);
} }

View File

@ -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 browserProvidersConf = require('./browser-providers.conf.js');
var internalAngularReporter = require('./tools/karma/reporter.js'); var internalAngularReporter = require('./tools/karma/reporter.js');
@ -17,24 +25,25 @@ module.exports = function(config) {
// include Angular v1 for upgrade module testing // include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js', 'node_modules/angular/angular.min.js',
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-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/proxy.js', 'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.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', 'node_modules/zone.js/dist/fake-async-test.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces. // Including systemjs because it defines `__eval`, which produces correct stack traces.
'shims_for_IE.js', 'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true}, {pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js', 'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js',
'tools/build/file2modulename.js', {pattern: 'dist/all/empty.*', included: false, watched: false}, {
'test-main.js', pattern: 'modules/@angular/platform-browser/test/static_assets/**',
{pattern: 'dist/all/empty.*', included: false, watched: false}, included: false,
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false}, watched: false
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false} },
{
pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**',
included: false,
watched: false,
}
], ],
exclude: [ exclude: [
@ -44,7 +53,7 @@ module.exports = function(config) {
'dist/all/@angular/benchpress/**', 'dist/all/@angular/benchpress/**',
'dist/all/angular1_router.js', 'dist/all/angular1_router.js',
'dist/all/@angular/platform-browser/testing/e2e_util.js', 'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/examples/**/e2e_test/**' 'dist/examples/**/e2e_test/**',
], ],
customLaunchers: browserProvidersConf.customLaunchers, customLaunchers: browserProvidersConf.customLaunchers,
@ -55,11 +64,11 @@ module.exports = function(config) {
'karma-sauce-launcher', 'karma-sauce-launcher',
'karma-chrome-launcher', 'karma-chrome-launcher',
'karma-sourcemap-loader', 'karma-sourcemap-loader',
internalAngularReporter internalAngularReporter,
], ],
preprocessors: { preprocessors: {
'**/*.js': ['sourcemap'] '**/*.js': ['sourcemap'],
}, },
reporters: ['internal-angular'], reporters: ['internal-angular'],
@ -73,7 +82,7 @@ module.exports = function(config) {
'selenium-version': '2.53.0', 'selenium-version': '2.53.0',
'command-timeout': 600, 'command-timeout': 600,
'idle-timeout': 600, 'idle-timeout': 600,
'max-duration': 5400 'max-duration': 5400,
} }
}, },
@ -82,7 +91,7 @@ module.exports = function(config) {
startTunnel: false, startTunnel: false,
retryLimit: 3, retryLimit: 3,
timeout: 600, timeout: 600,
pollingTimeout: 10000 pollingTimeout: 10000,
}, },
browsers: ['Chrome'], browsers: ['Chrome'],
@ -95,7 +104,8 @@ module.exports = function(config) {
}); });
if (process.env.TRAVIS) { 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')) { if (process.env.CI_MODE.startsWith('saucelabs')) {
config.sauceLabs.build = buildId; config.sauceLabs.build = buildId;
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER; config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;

View File

@ -9,8 +9,6 @@
import {OpaqueToken} from '@angular/core'; import {OpaqueToken} from '@angular/core';
import * as fs from 'fs'; import * as fs from 'fs';
import {DateWrapper} from './facade/lang';
export class Options { export class Options {
static SAMPLE_ID = new OpaqueToken('Options.sampleId'); static SAMPLE_ID = new OpaqueToken('Options.sampleId');
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription'); static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
@ -34,7 +32,7 @@ export class Options {
{provide: Options.FORCE_GC, useValue: false}, {provide: Options.FORCE_GC, useValue: false},
{provide: Options.PREPARE, useValue: Options.NO_PREPARE}, {provide: Options.PREPARE, useValue: Options.NO_PREPARE},
{provide: Options.MICRO_METRICS, useValue: {}}, {provide: Options.USER_METRICS, useValue: {}}, {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.RECEIVED_DATA, useValue: false},
{provide: Options.REQUEST_COUNT, useValue: false}, {provide: Options.REQUEST_COUNT, useValue: false},
{provide: Options.CAPTURE_FRAMES, useValue: false}, {provide: Options.CAPTURE_FRAMES, useValue: false},

View File

@ -6,18 +6,15 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Map} from './facade/collection';
import {Date, DateWrapper} from './facade/lang';
export class MeasureValues { export class MeasureValues {
constructor( constructor(
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {} public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
toJson() { toJson() {
return { return {
'timeStamp': DateWrapper.toJson(this.timeStamp), 'timeStamp': this.timeStamp.toJSON(),
'runIndex': this.runIndex, 'runIndex': this.runIndex,
'values': this.values 'values': this.values,
}; };
} }
} }

View File

@ -7,7 +7,6 @@
*/ */
import {Injector, OpaqueToken} from '@angular/core'; import {Injector, OpaqueToken} from '@angular/core';
import {StringMapWrapper} from '../facade/collection';
import {Metric} from '../metric'; import {Metric} from '../metric';
@ -57,8 +56,7 @@ export class MultiMetric extends Metric {
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} { function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
var result: {[key: string]: string} = {}; var result: {[key: string]: string} = {};
maps.forEach( maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
map => { StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); });
return result; return result;
} }

View File

@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license * 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 {Options} from '../common_options';
import {StringMapWrapper} from '../facade/collection';
import {isNumber} from '../facade/lang'; import {isNumber} from '../facade/lang';
import {Metric} from '../metric'; import {Metric} from '../metric';
import {WebDriverAdapter} from '../web_driver_adapter'; import {WebDriverAdapter} from '../web_driver_adapter';
@ -40,7 +39,7 @@ export class UserMetric extends Metric {
reject = rej; reject = rej;
}); });
let adapter = this._wdAdapter; let adapter = this._wdAdapter;
let names = StringMapWrapper.keys(this._userMetrics); let names = Object.keys(this._userMetrics);
function getAndClearValues() { function getAndClearValues() {
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`))) Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
@ -48,9 +47,9 @@ export class UserMetric extends Metric {
if (values.every(isNumber)) { if (values.every(isNumber)) {
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`))) Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
.then((_: any[]) => { .then((_: any[]) => {
let map = StringMapWrapper.create(); let map: {[k: string]: any} = {};
for (let i = 0, n = names.length; i < n; i++) { for (let i = 0, n = names.length; i < n; i++) {
StringMapWrapper.set(map, names[i], values[i]); map[names[i]] = values[i];
} }
resolve(map); resolve(map);
}, reject); }, reject);

View File

@ -7,10 +7,7 @@
*/ */
import {Inject, Injectable, OpaqueToken} from '@angular/core'; import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {print} from '../facade/lang';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {NumberWrapper, isBlank, isPresent, print} from '../facade/lang';
import {Math} from '../facade/math';
import {MeasureValues} from '../measure_values'; import {MeasureValues} from '../measure_values';
import {Reporter} from '../reporter'; import {Reporter} from '../reporter';
import {SampleDescription} from '../sample_description'; import {SampleDescription} from '../sample_description';

View File

@ -9,7 +9,7 @@
import {Inject, Injectable, OpaqueToken} from '@angular/core'; import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from '../common_options'; import {Options} from '../common_options';
import {DateWrapper, Json, isBlank, isPresent} from '../facade/lang'; import {Json} from '../facade/lang';
import {MeasureValues} from '../measure_values'; import {MeasureValues} from '../measure_values';
import {Reporter} from '../reporter'; import {Reporter} from '../reporter';
import {SampleDescription} from '../sample_description'; import {SampleDescription} from '../sample_description';
@ -45,8 +45,7 @@ export class JsonFileReporter extends Reporter {
'completeSample': completeSample, 'completeSample': completeSample,
'validSample': validSample, 'validSample': validSample,
}); });
var filePath = var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
`${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
return this._writeFile(filePath, content); return this._writeFile(filePath, content);
} }
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StringMapWrapper} from '../facade/collection';
import {NumberWrapper} from '../facade/lang'; import {NumberWrapper} from '../facade/lang';
import {MeasureValues} from '../measure_values'; import {MeasureValues} from '../measure_values';
import {Statistic} from '../statistic'; import {Statistic} from '../statistic';
@ -17,7 +17,7 @@ export function formatNum(n: number) {
export function sortedProps(obj: {[key: string]: any}) { export function sortedProps(obj: {[key: string]: any}) {
var props: string[] = []; var props: string[] = [];
StringMapWrapper.forEach(obj, (value, prop) => props.push(prop)); props.push(...Object.keys(obj));
props.sort(); props.sort();
return props; return props;
} }

View File

@ -9,7 +9,7 @@
import {Provider, ReflectiveInjector} from '@angular/core'; import {Provider, ReflectiveInjector} from '@angular/core';
import {Options} from './common_options'; import {Options} from './common_options';
import {isBlank, isPresent} from './facade/lang'; import {isPresent} from './facade/lang';
import {Metric} from './metric'; import {Metric} from './metric';
import {MultiMetric} from './metric/multi_metric'; import {MultiMetric} from './metric/multi_metric';
import {PerflogMetric} from './metric/perflog_metric'; import {PerflogMetric} from './metric/perflog_metric';

View File

@ -9,7 +9,6 @@
import {OpaqueToken} from '@angular/core'; import {OpaqueToken} from '@angular/core';
import {Options} from './common_options'; import {Options} from './common_options';
import {StringMapWrapper} from './facade/collection';
import {Metric} from './metric'; import {Metric} from './metric';
import {Validator} from './validator'; import {Validator} from './validator';
@ -42,7 +41,7 @@ export class SampleDescription {
public metrics: {[key: string]: any}) { public metrics: {[key: string]: any}) {
this.description = {}; this.description = {};
descriptions.forEach(description => { descriptions.forEach(description => {
StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value); Object.keys(description).forEach(prop => { this.description[prop] = description[prop]; });
}); });
} }

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {Options} from './common_options';
import {Date, DateWrapper, isBlank, isPresent} from './facade/lang'; import {isPresent} from './facade/lang';
import {MeasureValues} from './measure_values'; import {MeasureValues} from './measure_values';
import {Metric} from './metric'; import {Metric} from './metric';
import {Reporter} from './reporter'; import {Reporter} from './reporter';

View File

@ -9,7 +9,6 @@
import {Injector, OpaqueToken} from '@angular/core'; import {Injector, OpaqueToken} from '@angular/core';
import {Options} from './common_options'; import {Options} from './common_options';
import {isBlank, isPresent} from './facade/lang';
export type PerfLogEvent = { export type PerfLogEvent = {
[key: string]: any [key: string]: any
@ -50,7 +49,7 @@ export abstract class WebDriverExtension {
delegate = extension; delegate = extension;
} }
}); });
if (isBlank(delegate)) { if (!delegate) {
throw new Error('Could not find a delegate for given capabilities!'); throw new Error('Could not find a delegate for given capabilities!');
} }
return delegate; return delegate;

View File

@ -169,7 +169,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
eventCategories: string[], eventName: string, expectedCategories: string[], eventCategories: string[], eventName: string, expectedCategories: string[],
expectedName: string = null): boolean { expectedName: string = null): boolean {
var hasCategories = expectedCategories.reduce( 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; return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
} }

View File

@ -52,7 +52,7 @@ export class IOsDriverExtension extends WebDriverExtension {
/** @internal */ /** @internal */
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) { private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
if (isBlank(events)) { if (!events) {
events = []; events = [];
} }
records.forEach((record) => { records.forEach((record) => {

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license * 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'; import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
export function main() { export function main() {
function createMetric(ids: any[]) { function createMetric(ids: any[]) {
var m = ReflectiveInjector var m = ReflectiveInjector
.resolveAndCreate([ .resolveAndCreate([
ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }), ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
MultiMetric.provideWith(ids) MultiMetric.provideWith(ids)
]) ])
.get(MultiMetric); .get(MultiMetric);

View File

@ -7,11 +7,10 @@
*/ */
import {Provider} from '@angular/core'; 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 {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
import {StringMapWrapper} from '../../src/facade/collection'; import {isPresent} from '../../src/facade/lang';
import {isBlank, isPresent} from '../../src/facade/lang';
import {TraceEventFactory} from '../trace_event_factory'; import {TraceEventFactory} from '../trace_event_factory';
export function main() { export function main() {
@ -28,12 +27,12 @@ export function main() {
requestCount?: boolean requestCount?: boolean
} = {}): Metric { } = {}): Metric {
commandLog = []; commandLog = [];
if (isBlank(perfLogFeatures)) { if (!perfLogFeatures) {
perfLogFeatures = perfLogFeatures =
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
} }
if (isBlank(microMetrics)) { if (!microMetrics) {
microMetrics = StringMapWrapper.create(); microMetrics = {};
} }
var providers: Provider[] = [ var providers: Provider[] = [
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS, Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
@ -68,7 +67,7 @@ export function main() {
function sortedKeys(stringMap: {[key: string]: any}) { function sortedKeys(stringMap: {[key: string]: any}) {
var res: string[] = []; var res: string[] = [];
StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); }); res.push(...Object.keys(stringMap));
res.sort(); res.sort();
return res; return res;
} }

View File

@ -7,11 +7,9 @@
*/ */
import {Provider, ReflectiveInjector} from '@angular/core'; 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 {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
import {StringMapWrapper} from '../../src/facade/collection';
import {Json, isBlank, isPresent} from '../../src/facade/lang';
export function main() { export function main() {
var wdAdapter: MockDriverAdapter; var wdAdapter: MockDriverAdapter;
@ -19,12 +17,12 @@ export function main() {
function createMetric( function createMetric(
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric { {userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
if (isBlank(perfLogFeatures)) { if (!perfLogFeatures) {
perfLogFeatures = perfLogFeatures =
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
} }
if (isBlank(userMetrics)) { if (!userMetrics) {
userMetrics = StringMapWrapper.create(); userMetrics = {};
} }
wdAdapter = new MockDriverAdapter(); wdAdapter = new MockDriverAdapter();
var providers: Provider[] = [ var providers: Provider[] = [

View File

@ -7,10 +7,10 @@
*/ */
import {Provider} from '@angular/core'; 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 {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
import {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang'; import {isBlank, isPresent} from '../../src/facade/lang';
export function main() { export function main() {
describe('console reporter', () => { describe('console reporter', () => {
@ -25,7 +25,7 @@ export function main() {
metrics?: {[key: string]: any} metrics?: {[key: string]: any}
}) { }) {
log = []; log = [];
if (isBlank(descriptions)) { if (!descriptions) {
descriptions = []; descriptions = [];
} }
if (isBlank(sampleId)) { if (isBlank(sampleId)) {
@ -90,5 +90,5 @@ export function main() {
} }
function mv(runIndex: number, time: number, values: {[key: string]: number}) { 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);
} }

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {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() { export function main() {
describe('file reporter', () => { describe('file reporter', () => {
@ -27,7 +27,7 @@ export function main() {
useValue: new SampleDescription(sampleId, descriptions, metrics) useValue: new SampleDescription(sampleId, descriptions, metrics)
}, },
{provide: JsonFileReporter.PATH, useValue: path}, {provide: JsonFileReporter.PATH, useValue: path},
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, { {provide: Options.NOW, useValue: () => new Date(1234)}, {
provide: Options.WRITE_FILE, provide: Options.WRITE_FILE,
useValue: (filename: string, content: string) => { useValue: (filename: string, content: string) => {
loggedFile = {'filename': filename, 'content': content}; loggedFile = {'filename': filename, 'content': content};
@ -77,5 +77,5 @@ export function main() {
} }
function mv(runIndex: number, time: number, values: {[key: string]: number}) { 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);
} }

View File

@ -6,16 +6,15 @@
* found in the LICENSE file at https://angular.io/license * 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 {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
import {DateWrapper} from '../../src/facade/lang';
export function main() { export function main() {
function createReporters(ids: any[]) { function createReporters(ids: any[]) {
var r = ReflectiveInjector var r = ReflectiveInjector
.resolveAndCreate([ .resolveAndCreate([
ids.map(id => { return {provide: id, useValue: new MockReporter(id)}; }), ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
MultiReporter.provideWith(ids) MultiReporter.provideWith(ids)
]) ])
.get(MultiReporter); .get(MultiReporter);
@ -26,7 +25,7 @@ export function main() {
it('should reportMeasureValues to all', it('should reportMeasureValues to all',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { 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) => { createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); 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) => { it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var completeSample = [ var completeSample =
new MeasureValues(0, DateWrapper.now(), {}), new MeasureValues(1, DateWrapper.now(), {}) [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
];
var validSample = [completeSample[1]]; var validSample = [completeSample[1]];
createReporters(['m1', 'm2']) createReporters(['m1', 'm2'])

View File

@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license * 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 {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
import {isBlank} from '../src/facade/lang';
export function main() { export function main() {
describe('runner', () => { describe('runner', () => {
@ -17,7 +16,7 @@ export function main() {
var runner: Runner; var runner: Runner;
function createRunner(defaultProviders: any[] = null): Runner { function createRunner(defaultProviders: any[] = null): Runner {
if (isBlank(defaultProviders)) { if (!defaultProviders) {
defaultProviders = []; defaultProviders = [];
} }
runner = new Runner([ runner = new Runner([

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {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() { export function main() {
var EMPTY_EXECUTE = () => {}; var EMPTY_EXECUTE = () => {};
@ -26,10 +26,10 @@ export function main() {
execute?: any execute?: any
} = {}) { } = {}) {
var time = 1000; var time = 1000;
if (isBlank(metric)) { if (!metric) {
metric = new MockMetric([]); metric = new MockMetric([]);
} }
if (isBlank(reporter)) { if (!reporter) {
reporter = new MockReporter([]); reporter = new MockReporter([]);
} }
if (isBlank(driver)) { if (isBlank(driver)) {
@ -39,7 +39,7 @@ export function main() {
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric}, Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver}, {provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator}, {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)) { if (isPresent(prepare)) {
providers.push({provide: Options.PREPARE, useValue: prepare}); providers.push({provide: Options.PREPARE, useValue: prepare});
@ -60,8 +60,8 @@ export function main() {
createSampler({ createSampler({
driver: driver, driver: driver,
validator: createCountingValidator(2), validator: createCountingValidator(2),
prepare: () => { return count++; }, prepare: () => count++,
execute: () => { return count++; } execute: () => count++,
}); });
sampler.sample().then((_) => { sampler.sample().then((_) => {
expect(count).toBe(4); expect(count).toBe(4);
@ -204,7 +204,7 @@ export function main() {
} }
function mv(runIndex: number, time: number, values: {[key: string]: number}) { 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( function createCountingValidator(
@ -221,7 +221,7 @@ function createCountingValidator(
function createCountingMetric(log: any[] = []) { function createCountingMetric(log: any[] = []) {
var scriptTime = 0; var scriptTime = 0;
return new MockMetric(log, () => { return {'script': scriptTime++}; }); return new MockMetric(log, () => ({'script': scriptTime++}));
} }
class MockDriverAdapter extends WebDriverAdapter { class MockDriverAdapter extends WebDriverAdapter {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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'; import {Statistic} from '../src/statistic';
export function main() { export function main() {

View File

@ -6,11 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
import {ListWrapper} from '../../src/facade/collection'; import {ListWrapper} from '../../src/facade/collection';
import {Date, DateWrapper} from '../../src/facade/lang';
export function main() { export function main() {
describe('regression slope validator', () => { describe('regression slope validator', () => {
@ -62,5 +61,5 @@ export function main() {
} }
function mv(runIndex: number, time: number, values: {[key: string]: number}) { 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);
} }

View File

@ -6,11 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {ListWrapper} from '../../src/facade/collection';
import {Date, DateWrapper} from '../../src/facade/lang';
export function main() { export function main() {
describe('size validator', () => { describe('size validator', () => {
@ -47,5 +46,5 @@ export function main() {
} }
function mv(runIndex: number, time: number, values: {[key: string]: number}) { 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);
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 {Options, ReflectiveInjector, WebDriverExtension} from '../index';
import {StringWrapper, isPresent} from '../src/facade/lang'; import {StringWrapper, isPresent} from '../src/facade/lang';
@ -17,7 +17,7 @@ export function main() {
try { try {
res(ReflectiveInjector res(ReflectiveInjector
.resolveAndCreate([ .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}, {provide: Options.CAPABILITIES, useValue: caps},
WebDriverExtension.provideFirstSupported(ids) WebDriverExtension.provideFirstSupported(ids)
]) ])

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {Json, isBlank} from '../../src/facade/lang'; import {Json, isBlank} from '../../src/facade/lang';
@ -35,7 +35,7 @@ export function main() {
function createExtension( function createExtension(
perfRecords: any[] = null, userAgent: string = null, perfRecords: any[] = null, userAgent: string = null,
messageMethod = 'Tracing.dataCollected'): WebDriverExtension { messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
if (isBlank(perfRecords)) { if (!perfRecords) {
perfRecords = []; perfRecords = [];
} }
if (isBlank(userAgent)) { if (isBlank(userAgent)) {
@ -396,11 +396,11 @@ class MockDriverAdapter extends WebDriverAdapter {
logs(type: string) { logs(type: string) {
this._log.push(['logs', type]); this._log.push(['logs', type]);
if (type === 'performance') { if (type === 'performance') {
return Promise.resolve(this._events.map((event) => { return Promise.resolve(this._events.map(
return { (event) => ({
'message': Json.stringify({'message': {'method': this._messageMethod, 'params': event}}) 'message':
}; Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
})); })));
} else { } else {
return null; return null;
} }

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {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'; import {TraceEventFactory} from '../trace_event_factory';
export function main() { export function main() {
@ -20,7 +20,7 @@ export function main() {
var normEvents = new TraceEventFactory('timeline', 'pid0'); var normEvents = new TraceEventFactory('timeline', 'pid0');
function createExtension(perfRecords: any[] = null): WebDriverExtension { function createExtension(perfRecords: any[] = null): WebDriverExtension {
if (isBlank(perfRecords)) { if (!perfRecords) {
perfRecords = []; perfRecords = [];
} }
log = []; log = [];
@ -156,7 +156,7 @@ function timeEndRecord(name: string, time: number) {
} }
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) { function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
if (isBlank(children)) { if (!children) {
children = []; children = [];
} }
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children}; return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};

View File

@ -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 { export default {
entry: '../../../dist/packages-dist/common/testing/index.js', entry: '../../../dist/packages-dist/common/testing/index.js',
@ -10,4 +17,4 @@ export default {
'rxjs/Observable': 'Rx', 'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx' 'rxjs/Subject': 'Rx'
} }
} };

View File

@ -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 { export default {
entry: '../../../dist/packages-dist/common/index.js', entry: '../../../dist/packages-dist/common/index.js',
@ -7,6 +14,6 @@ export default {
globals: { globals: {
'@angular/core': 'ng.core', '@angular/core': 'ng.core',
'rxjs/Observable': 'Rx', 'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx' 'rxjs/Subject': 'Rx',
}
} }
};

View File

@ -61,8 +61,7 @@ export class NgPlural {
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; } addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
/** @internal */ private _updateView(): void {
_updateView(): void {
this._clearViews(); this._clearViews();
const cases = Object.keys(this._caseViews); const cases = Object.keys(this._caseViews);
@ -70,13 +69,11 @@ export class NgPlural {
this._activateView(this._caseViews[key]); this._activateView(this._caseViews[key]);
} }
/** @internal */ private _clearViews() {
_clearViews() {
if (this._activeView) this._activeView.destroy(); if (this._activeView) this._activeView.destroy();
} }
/** @internal */ private _activateView(view: SwitchView) {
_activateView(view: SwitchView) {
if (view) { if (view) {
this._activeView = view; this._activeView = view;
this._activeView.create(); this._activeView.create();

View File

@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
*/ */
@Directive({selector: '[ngStyle]'}) @Directive({selector: '[ngStyle]'})
export class NgStyle implements DoCheck { export class NgStyle implements DoCheck {
/** @internal */ private _ngStyle: {[key: string]: string};
_ngStyle: {[key: string]: string}; private _differ: KeyValueDiffer;
/** @internal */
_differ: KeyValueDiffer;
constructor( constructor(
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {} 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 { private _setStyle(nameAndUnit: string, value: string): void {
const [name, unit] = nameAndUnit.split('.'); 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); this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
} }

View File

@ -111,8 +111,7 @@ export class NgSwitch {
} }
} }
/** @internal */ private _emptyAllActiveViews(): void {
_emptyAllActiveViews(): void {
const activeContainers = this._activeViews; const activeContainers = this._activeViews;
for (var i = 0; i < activeContainers.length; i++) { for (var i = 0; i < activeContainers.length; i++) {
activeContainers[i].destroy(); activeContainers[i].destroy();
@ -120,9 +119,7 @@ export class NgSwitch {
this._activeViews = []; this._activeViews = [];
} }
/** @internal */ private _activateViews(views: SwitchView[]): void {
_activateViews(views: SwitchView[]): void {
// TODO(vicb): assert(this._activeViews.length === 0);
if (views) { if (views) {
for (var i = 0; i < views.length; i++) { for (var i = 0; i < views.length; i++) {
views[i].create(); views[i].create();
@ -141,8 +138,7 @@ export class NgSwitch {
views.push(view); views.push(view);
} }
/** @internal */ private _deregisterView(value: any, view: SwitchView): void {
_deregisterView(value: any, view: SwitchView): void {
// `_CASE_DEFAULT` is used a marker for non-registered cases // `_CASE_DEFAULT` is used a marker for non-registered cases
if (value === _CASE_DEFAULT) return; if (value === _CASE_DEFAULT) return;
const views = this._valueViews.get(value); const views = this._valueViews.get(value);
@ -181,10 +177,8 @@ export class NgSwitch {
@Directive({selector: '[ngSwitchCase]'}) @Directive({selector: '[ngSwitchCase]'})
export class NgSwitchCase { export class NgSwitchCase {
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value // `_CASE_DEFAULT` is used as a marker for a not yet initialized value
/** @internal */ private _value: any = _CASE_DEFAULT;
_value: any = _CASE_DEFAULT; private _view: SwitchView;
/** @internal */
_view: SwitchView;
private _switch: NgSwitch; private _switch: NgSwitch;
constructor( constructor(

View File

@ -67,7 +67,7 @@ export enum Plural {
Two, Two,
Few, Few,
Many, Many,
Other Other,
} }
/** /**

View File

@ -49,16 +49,20 @@ export class Location {
_subject: EventEmitter<any> = new EventEmitter(); _subject: EventEmitter<any> = new EventEmitter();
/** @internal */ /** @internal */
_baseHref: string; _baseHref: string;
/** @internal */ /** @internal */
_platformStrategy: LocationStrategy; _platformStrategy: LocationStrategy;
constructor(platformStrategy: LocationStrategy) { constructor(platformStrategy: LocationStrategy) {
this._platformStrategy = platformStrategy; this._platformStrategy = platformStrategy;
var browserBaseHref = this._platformStrategy.getBaseHref(); const browserBaseHref = this._platformStrategy.getBaseHref();
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref)); this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
this._platformStrategy.onPopState( this._platformStrategy.onPopState((ev) => {
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); }); this._subject.emit({
'url': this.path(true),
'pop': true,
'type': ev.type,
});
});
} }
/** /**

View File

@ -8,7 +8,7 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing'; 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() { export function main() {
describe('binding to CSS class list', () => { describe('binding to CSS class list', () => {

View File

@ -8,7 +8,7 @@
import {LOCALE_ID} from '@angular/core'; import {LOCALE_ID} from '@angular/core';
import {TestBed} from '@angular/core/testing'; 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 {expect} from '@angular/platform-browser/testing/matchers';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization'; import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';

View File

@ -8,12 +8,11 @@
import {AsyncPipe} from '@angular/common'; import {AsyncPipe} from '@angular/common';
import {WrappedValue} from '@angular/core'; 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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {EventEmitter} from '../../src/facade/async'; import {EventEmitter} from '../../src/facade/async';
import {isBlank} from '../../src/facade/lang';
import {SpyChangeDetectorRef} from '../spies'; import {SpyChangeDetectorRef} from '../spies';
export function main() { export function main() {
@ -112,7 +111,7 @@ export function main() {
var promise: Promise<any>; var promise: Promise<any>;
var ref: SpyChangeDetectorRef; var ref: SpyChangeDetectorRef;
// adds longer timers for passing tests in IE // adds longer timers for passing tests in IE
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10; var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
beforeEach(() => { beforeEach(() => {
promise = new Promise((res, rej) => { promise = new Promise((res, rej) => {

View File

@ -8,11 +8,9 @@
import {DatePipe} from '@angular/common'; import {DatePipe} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; 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 {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {DateWrapper} from '../../src/facade/lang';
export function main() { export function main() {
describe('DatePipe', () => { describe('DatePipe', () => {
var date: Date; var date: Date;
@ -27,7 +25,7 @@ export function main() {
// Tracking issue: https://github.com/angular/angular/issues/11187 // Tracking issue: https://github.com/angular/angular/issues/11187
beforeEach(() => { beforeEach(() => {
date = DateWrapper.create(2015, 6, 15, 9, 3, 1); date = new Date(2015, 5, 15, 9, 3, 1);
pipe = new DatePipe('en-US'); pipe = new DatePipe('en-US');
}); });

View File

@ -8,7 +8,7 @@
import {I18nPluralPipe, NgLocalization} from '@angular/common'; import {I18nPluralPipe, NgLocalization} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; 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() { export function main() {
describe('I18nPluralPipe', () => { describe('I18nPluralPipe', () => {

View File

@ -8,7 +8,7 @@
import {I18nSelectPipe} from '@angular/common'; import {I18nSelectPipe} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; 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() { export function main() {
describe('I18nSelectPipe', () => { describe('I18nSelectPipe', () => {

View File

@ -7,7 +7,7 @@
*/ */
import {LowerCasePipe} from '@angular/common'; 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() { export function main() {
describe('LowerCasePipe', () => { describe('LowerCasePipe', () => {

View File

@ -7,7 +7,7 @@
*/ */
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common'; 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'; import {browserDetection} from '@angular/platform-browser/testing/browser_util';
export function main() { export function main() {

View File

@ -9,7 +9,6 @@
import {CommonModule, SlicePipe} from '@angular/common'; import {CommonModule, SlicePipe} from '@angular/common';
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {TestBed, async} from '@angular/core/testing'; import {TestBed, async} from '@angular/core/testing';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
export function main() { export function main() {

View File

@ -7,7 +7,7 @@
*/ */
import {UpperCasePipe} from '@angular/common'; 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() { export function main() {
describe('UpperCasePipe', () => { describe('UpperCasePipe', () => {

View File

@ -7,7 +7,7 @@
*/ */
import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; 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 { export class SpyChangeDetectorRef extends SpyObject {
constructor() { constructor() {

View File

@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core';
@Injectable() @Injectable()
export class SpyLocation implements Location { export class SpyLocation implements Location {
urlChanges: string[] = []; urlChanges: string[] = [];
/** @internal */
private _history: LocationState[] = [new LocationState('', '')]; private _history: LocationState[] = [new LocationState('', '')];
/** @internal */
private _historyIndex: number = 0; private _historyIndex: number = 0;
/** @internal */ /** @internal */
_subject: EventEmitter<any> = new EventEmitter(); _subject: EventEmitter<any> = new EventEmitter();

View File

@ -36,7 +36,8 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<messagebundle> <messagebundle>
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg> <msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg> <msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
</messagebundle>`; </messagebundle>
`;
const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
@ -54,7 +55,8 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
</xliff>`; </xliff>
`;
describe('template i18n extraction output', () => { describe('template i18n extraction output', () => {
const outDir = ''; const outDir = '';

View File

@ -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 = { module.exports = {
target: 'node', target: 'node',
entry: './test/all_spec.js', entry: './test/all_spec.js',
output: { output: {filename: './all_spec.js'},
filename: './all_spec.js' resolve: {extensions: ['.js']},
},
resolve: {
extensions: ['.js']
},
}; };

View File

@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"@angular/tsc-wrapped": "^0.3.0", "@angular/tsc-wrapped": "^0.3.0",
"reflect-metadata": "^0.1.2", "reflect-metadata": "^0.1.2",
"parse5": "1.3.2", "parse5": "^2.2.1",
"minimist": "^1.2.0" "minimist": "^1.2.0"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -126,7 +126,7 @@ export class CodeGenerator {
static create( static create(
options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program, options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program,
compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext, compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext,
resourceLoader?: compiler.ResourceLoader): CodeGenerator { resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator {
resourceLoader = resourceLoader || { resourceLoader = resourceLoader || {
get: (s: string) => { get: (s: string) => {
if (!compilerHost.fileExists(s)) { if (!compilerHost.fileExists(s)) {
@ -148,10 +148,12 @@ export class CodeGenerator {
} }
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
if (!reflectorHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0; const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
const reflectorHost = usePathMapping ? reflectorHost = usePathMapping ?
new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) : new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) :
new ReflectorHost(program, compilerHost, options, reflectorHostContext); new ReflectorHost(program, compilerHost, options, reflectorHostContext);
}
const staticReflector = new StaticReflector(reflectorHost); const staticReflector = new StaticReflector(reflectorHost);
StaticAndDynamicReflectionCapabilities.install(staticReflector); StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser = const htmlParser =

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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'; import {ReflectorReader} from './private_import_core';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 * as ts from 'typescript';
import {ReflectorHost} from '../src/reflector_host'; import {ReflectorHost} from '../src/reflector_host';

View File

@ -9,7 +9,6 @@
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector'; 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 {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ListWrapper} from '@angular/facade/src/collection'; import {ListWrapper} from '@angular/facade/src/collection';
import {isBlank} from '@angular/facade/src/lang';
import {MetadataCollector} from '@angular/tsc-wrapped'; import {MetadataCollector} from '@angular/tsc-wrapped';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -454,7 +453,7 @@ class MockReflectorHost implements StaticReflectorHost {
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`; var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
var result = this.staticTypeCache.get(cacheKey); var result = this.staticTypeCache.get(cacheKey);
if (isBlank(result)) { if (!result) {
result = new StaticSymbol(declarationFile, name, members); result = new StaticSymbol(declarationFile, name, members);
this.staticTypeCache.set(cacheKey, result); this.staticTypeCache.set(cacheKey, result);
} }

View File

@ -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 { export default {
entry: '../../../dist/packages-dist/compiler/testing/index.js', entry: '../../../dist/packages-dist/compiler/testing/index.js',
@ -11,4 +18,4 @@ export default {
'rxjs/Observable': 'Rx', 'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx' 'rxjs/Subject': 'Rx'
} }
} };

View File

@ -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 { export default {
entry: '../../../dist/packages-dist/compiler/index.js', entry: '../../../dist/packages-dist/compiler/index.js',
@ -7,9 +14,9 @@ export default {
globals: { globals: {
'@angular/core': 'ng.core', '@angular/core': 'ng.core',
'rxjs/Observable': 'Rx', 'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx' 'rxjs/Subject': 'Rx',
}, },
plugins: [ plugins: [
// nodeResolve({ jsnext: true, main: true }), // nodeResolve({ jsnext: true, main: true }),
] ]
} };

View File

@ -6,75 +6,26 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileDirectiveMetadata} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection'; import {isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ANY_STATE, AnimationOutput, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
import * as t from '../template_parser/template_ast';
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast'; import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser';
const animationCompilationCache = export class AnimationEntryCompileResult {
new Map<CompileDirectiveMetadata, CompiledAnimationTriggerResult[]>(); constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
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 AnimationCompiler { export class AnimationCompiler {
compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]): compile(factoryNamePrefix: string, parsedAnimations: AnimationEntryAst[]):
CompiledComponentAnimationResult { AnimationEntryCompileResult[] {
var compiledAnimations: CompiledAnimationTriggerResult[] = []; return parsedAnimations.map(entry => {
var groupedErrors: string[] = []; const factoryName = `${factoryNamePrefix}_${entry.name}`;
var triggerLookup: {[key: string]: CompiledAnimationTriggerResult} = {}; const visitor = new _AnimationBuilder(entry.name, factoryName);
var componentName = component.type.name; return visitor.build(entry);
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;
}
}); });
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 => { ast.styles.forEach(entry => {
stylesArr.push( stylesArr.push(o.literalMap(Object.keys(entry).map(key => [key, o.literal(entry[key])])));
o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])])));
}); });
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([ return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
@ -182,9 +132,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
visitAnimationStateDeclaration( visitAnimationStateDeclaration(
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void { ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
var flatStyles: {[key: string]: string | number} = {}; var flatStyles: {[key: string]: string | number} = {};
_getStylesArray(ast).forEach(entry => { _getStylesArray(ast).forEach(
StringMapWrapper.forEach(entry, (value: string, key: string) => { flatStyles[key] = value; }); entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
});
context.stateMap.registerState(ast.stateName, flatStyles); context.stateMap.registerState(ast.stateName, flatStyles);
} }
@ -334,28 +283,27 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements); statements);
} }
build(ast: AnimationAst): CompiledAnimationTriggerResult { build(ast: AnimationAst): AnimationEntryCompileResult {
var context = new _AnimationBuilderContext(); var context = new _AnimationBuilderContext();
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName); var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
var fnVariable = o.variable(this._fnVarName); var fnVariable = o.variable(this._fnVarName);
var lookupMap: any[] = []; var lookupMap: any[] = [];
StringMapWrapper.forEach( Object.keys(context.stateMap.states).forEach(stateName => {
context.stateMap.states, (value: {[key: string]: string}, stateName: string) => { const value = context.stateMap.states[stateName];
var variableValue = EMPTY_MAP; var variableValue = EMPTY_MAP;
if (isPresent(value)) { if (isPresent(value)) {
let styleMap: any[] = []; let styleMap: any[] = [];
StringMapWrapper.forEach(value, (value: string, key: string) => { Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
styleMap.push([key, o.literal(value)]);
});
variableValue = o.literalMap(styleMap); variableValue = o.literalMap(styleMap);
} }
lookupMap.push([stateName, variableValue]); lookupMap.push([stateName, variableValue]);
}); });
var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt(); const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
return new CompiledAnimationTriggerResult( const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable);
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);
} }
} }
@ -371,7 +319,7 @@ class _AnimationBuilderStateMap {
get states() { return this._states; } get states() { return this._states; }
registerState(name: string, value: {[prop: string]: string | number} = null): void { registerState(name: string, value: {[prop: string]: string | number} = null): void {
var existingEntry = this._states[name]; var existingEntry = this._states[name];
if (isBlank(existingEntry)) { if (!existingEntry) {
this._states[name] = value; this._states[name] = value;
} }
} }
@ -397,7 +345,7 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) { if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
var styles1 = _getStylesArray(step.keyframes[0])[0]; var styles1 = _getStylesArray(step.keyframes[0])[0];
var styles2 = _getStylesArray(step.keyframes[1])[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; return false;
} }
@ -405,99 +353,3 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
function _getStylesArray(obj: any): {[key: string]: any}[] { function _getStylesArray(obj: any): {[key: string]: any}[] {
return obj.styles.styles; 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 {}
}

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license * 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 {ListWrapper, StringMapWrapper} from '../facade/collection';
import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang'; import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang';
import {Math} from '../facade/math'; import {Math} from '../facade/math';
import {ParseError} from '../parse_util'; 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 {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
import {StylesCollection} from './styles_collection'; import {StylesCollection} from './styles_collection';
@ -20,21 +20,58 @@ const _INITIAL_KEYFRAME = 0;
const _TERMINAL_KEYFRAME = 1; const _TERMINAL_KEYFRAME = 1;
const _ONE_SECOND = 1000; const _ONE_SECOND = 1000;
declare type Styles = {
[key: string]: string | number
};
export class AnimationParseError extends ParseError { export class AnimationParseError extends ParseError {
constructor(message: any /** TODO #9100 */) { super(null, message); } constructor(message: string) { super(null, message); }
toString(): string { return `${this.msg}`; } toString(): string { return `${this.msg}`; }
} }
export class ParsedAnimationResult { export class AnimationEntryParseResult {
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {} constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
} }
export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult { 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;
});
if (errors.length > 0) {
const errorString = errors.join('\n');
throw new Error(`Animation parse errors:\n${errorString}`);
}
return asts;
}
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
var errors: AnimationParseError[] = []; var errors: AnimationParseError[] = [];
var stateStyles: {[key: string]: AnimationStylesAst} = {}; var stateStyles: {[key: string]: AnimationStylesAst} = {};
var transitions: CompileAnimationStateTransitionMetadata[] = []; var transitions: CompileAnimationStateTransitionMetadata[] = [];
var stateDeclarationAsts: any[] /** TODO #9100 */ = []; var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
entry.definitions.forEach(def => { entry.definitions.forEach(def => {
if (def instanceof CompileAnimationStateDeclarationMetadata) { if (def instanceof CompileAnimationStateDeclarationMetadata) {
_parseAnimationDeclarationStates(def, errors).forEach(ast => { _parseAnimationDeclarationStates(def, errors).forEach(ast => {
@ -50,43 +87,18 @@ export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): Parse
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors)); transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts); var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
return new ParsedAnimationResult(ast, errors); return new AnimationEntryParseResult(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 new AnimationOutput(name, phase, outputName);
} }
function _parseAnimationDeclarationStates( function _parseAnimationDeclarationStates(
stateMetadata: CompileAnimationStateDeclarationMetadata, stateMetadata: CompileAnimationStateDeclarationMetadata,
errors: AnimationParseError[]): AnimationStateDeclarationAst[] { errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
var styleValues: {[key: string]: string | number}[] = []; var styleValues: Styles[] = [];
stateMetadata.styles.styles.forEach(stylesEntry => { stateMetadata.styles.styles.forEach(stylesEntry => {
// TODO (matsko): change this when we get CSS class integration support // TODO (matsko): change this when we get CSS class integration support
if (isStringMap(stylesEntry)) { if (isStringMap(stylesEntry)) {
styleValues.push(<{[key: string]: string | number}>stylesEntry); styleValues.push(stylesEntry as Styles);
} else { } else {
errors.push(new AnimationParseError( errors.push(new AnimationParseError(
`State based animations cannot contain references to other states`)); `State based animations cannot contain references to other states`));
@ -145,16 +157,6 @@ function _parseAnimationTransitionExpr(
return expressions; 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[]): function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
CompileAnimationMetadata { CompileAnimationMetadata {
return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) : return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) :
@ -210,7 +212,7 @@ function _normalizeStyleStepEntry(
} }
var newSteps: CompileAnimationMetadata[] = []; var newSteps: CompileAnimationMetadata[] = [];
var combinedStyles: {[key: string]: string | number}[]; var combinedStyles: Styles[];
steps.forEach(step => { steps.forEach(step => {
if (step instanceof CompileAnimationStyleMetadata) { if (step instanceof CompileAnimationStyleMetadata) {
// this occurs when a style step is followed by a previous style step // this occurs when a style step is followed by a previous style step
@ -266,7 +268,7 @@ function _normalizeStyleStepEntry(
function _resolveStylesFromState( function _resolveStylesFromState(
stateName: string, stateStyles: {[key: string]: AnimationStylesAst}, stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
errors: AnimationParseError[]) { errors: AnimationParseError[]) {
var styles: {[key: string]: string | number}[] = []; var styles: Styles[] = [];
if (stateName[0] != ':') { if (stateName[0] != ':') {
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`)); errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
} else { } else {
@ -278,7 +280,7 @@ function _resolveStylesFromState(
} else { } else {
value.styles.forEach(stylesEntry => { value.styles.forEach(stylesEntry => {
if (isStringMap(stylesEntry)) { if (isStringMap(stylesEntry)) {
styles.push(<{[key: string]: string | number}>stylesEntry); styles.push(stylesEntry as Styles);
} }
}); });
} }
@ -312,13 +314,11 @@ function _parseAnimationKeyframes(
var lastOffset = 0; var lastOffset = 0;
keyframeSequence.steps.forEach(styleMetadata => { keyframeSequence.steps.forEach(styleMetadata => {
var offset = styleMetadata.offset; var offset = styleMetadata.offset;
var keyframeStyles: {[key: string]: string | number} = {}; var keyframeStyles: Styles = {};
styleMetadata.styles.forEach(entry => { styleMetadata.styles.forEach(entry => {
StringMapWrapper.forEach( Object.keys(entry).forEach(prop => {
<{[key: string]: string | number}>entry,
(value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (prop != 'offset') { if (prop != 'offset') {
keyframeStyles[prop] = value; keyframeStyles[prop] = (entry as Styles)[prop];
} }
}); });
}); });
@ -357,8 +357,7 @@ function _parseAnimationKeyframes(
let entry = rawKeyframes[i]; let entry = rawKeyframes[i];
let styles = entry[1]; let styles = entry[1];
StringMapWrapper.forEach( Object.keys(styles).forEach(prop => {
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (!isPresent(firstKeyframeStyles[prop])) { if (!isPresent(firstKeyframeStyles[prop])) {
firstKeyframeStyles[prop] = FILL_STYLE_FLAG; firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
} }
@ -369,10 +368,9 @@ function _parseAnimationKeyframes(
let entry = rawKeyframes[i]; let entry = rawKeyframes[i];
let styles = entry[1]; let styles = entry[1];
StringMapWrapper.forEach( Object.keys(styles).forEach(prop => {
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (!isPresent(lastKeyframeStyles[prop])) { if (!isPresent(lastKeyframeStyles[prop])) {
lastKeyframeStyles[prop] = value; lastKeyframeStyles[prop] = styles[prop];
} }
}); });
} }
@ -398,11 +396,9 @@ function _parseTransitionAnimation(
if (entry instanceof CompileAnimationStyleMetadata) { if (entry instanceof CompileAnimationStyleMetadata) {
entry.styles.forEach(stylesEntry => { entry.styles.forEach(stylesEntry => {
// by this point we know that we only have stringmap values // by this point we know that we only have stringmap values
var map = <{[key: string]: string | number}>stylesEntry; var map = stylesEntry as Styles;
StringMapWrapper.forEach( Object.keys(map).forEach(
map, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
collectedStyles.insertAtTime(prop, time, value);
});
}); });
previousStyles = entry.styles; previousStyles = entry.styles;
return; return;
@ -448,7 +444,7 @@ function _parseTransitionAnimation(
} else { } else {
let styleData = <CompileAnimationStyleMetadata>styles; let styleData = <CompileAnimationStyleMetadata>styles;
let offset = _TERMINAL_KEYFRAME; 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); var keyframe = new AnimationKeyframeAst(offset, styleAst);
keyframes = [keyframe]; keyframes = [keyframe];
} }
@ -460,9 +456,8 @@ function _parseTransitionAnimation(
keyframes.forEach( keyframes.forEach(
(keyframe: any /** TODO #9100 */) => keyframe.styles.styles.forEach( (keyframe: any /** TODO #9100 */) => keyframe.styles.styles.forEach(
(entry: any /** TODO #9100 */) => StringMapWrapper.forEach( (entry: any /** TODO #9100 */) => Object.keys(entry).forEach(
entry, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => prop => { collectedStyles.insertAtTime(prop, currentTime, entry[prop]); })));
collectedStyles.insertAtTime(prop, currentTime, value))));
} else { } else {
// if the code reaches this stage then an error // if the code reaches this stage then an error
// has already been populated within the _normalizeStyleSteps() // has already been populated within the _normalizeStyleSteps()
@ -535,10 +530,11 @@ function _parseTimeExpression(
function _createStartKeyframeFromEndKeyframe( function _createStartKeyframeFromEndKeyframe(
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number, endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst { collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
var values: {[key: string]: string | number} = {}; var values: Styles = {};
var endTime = startTime + duration; var endTime = startTime + duration;
endKeyframe.styles.styles.forEach((styleData: {[key: string]: string | number}) => { endKeyframe.styles.styles.forEach((styleData: Styles) => {
StringMapWrapper.forEach(styleData, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { Object.keys(styleData).forEach(prop => {
const val = styleData[prop];
if (prop == 'offset') return; if (prop == 'offset') return;
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime); var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);

View File

@ -8,11 +8,10 @@
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {ListWrapper, MapWrapper, StringMapWrapper} from './facade/collection'; import {ListWrapper, MapWrapper} from './facade/collection';
import {isBlank, isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang'; import {isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang';
import {LifecycleHooks, reflector} from './private_import_core'; import {LifecycleHooks} from './private_import_core';
import {CssSelector} from './selector'; import {CssSelector} from './selector';
import {getUrlScheme} from './url_resolver';
import {sanitizeIdentifier, splitAtColon} from './util'; import {sanitizeIdentifier, splitAtColon} from './util';
function unimplemented(): any { function unimplemented(): any {
@ -343,7 +342,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
var hostProperties: {[key: string]: string} = {}; var hostProperties: {[key: string]: string} = {};
var hostAttributes: {[key: string]: string} = {}; var hostAttributes: {[key: string]: string} = {};
if (isPresent(host)) { 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); const matches = key.match(HOST_REG_EXP);
if (matches === null) { if (matches === null) {
hostAttributes[key] = value; hostAttributes[key] = value;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 * from './template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser'; export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser';

View File

@ -9,7 +9,7 @@
import * as chars from '../chars'; import * as chars from '../chars';
import {BaseError} from '../facade/errors'; import {BaseError} from '../facade/errors';
import {StringWrapper, isPresent, resolveEnumToken} from '../facade/lang'; import {StringWrapper, isPresent} from '../facade/lang';
export enum CssTokenType { export enum CssTokenType {
EOF, EOF,
@ -223,8 +223,8 @@ export class CssScanner {
var error: CssScannerError = null; var error: CssScannerError = null;
if (!isMatchingType || (isPresent(value) && value != next.strValue)) { if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
var errorMessage = resolveEnumToken(CssTokenType, next.type) + ' does not match expected ' + var errorMessage =
resolveEnumToken(CssTokenType, type) + ' value'; CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value';
if (isPresent(value)) { if (isPresent(value)) {
errorMessage += ' ("' + next.strValue + '" should match "' + value + '")'; errorMessage += ' ("' + next.strValue + '" should match "' + value + '")';

View File

@ -115,27 +115,27 @@ export class DirectiveNormalizer {
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata( const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl})); {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; let encapsulation = templateMeta.encapsulation;
if (isBlank(encapsulation)) { if (isBlank(encapsulation)) {
encapsulation = this._config.defaultEncapsulation; 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; encapsulation = ViewEncapsulation.None;
} }
return new CompileTemplateMetadata({ return new CompileTemplateMetadata({
encapsulation, encapsulation,
template: template, template,
templateUrl: templateAbsUrl, templateUrl: templateAbsUrl, styles, styleUrls,
styles: allStyles,
styleUrls: allStyleUrls,
externalStylesheets: templateMeta.externalStylesheets, externalStylesheets: templateMeta.externalStylesheets,
ngContentSelectors: visitor.ngContentSelectors, ngContentSelectors: visitor.ngContentSelectors,
animations: templateMeta.animations, animations: templateMeta.animations,
interpolation: templateMeta.interpolation interpolation: templateMeta.interpolation,
}); });
} }
@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate(
viewProviders: directive.viewProviders, viewProviders: directive.viewProviders,
queries: directive.queries, queries: directive.queries,
viewQueries: directive.viewQueries, viewQueries: directive.viewQueries,
entryComponents: directive.entryComponents, entryComponents: directive.entryComponents, template,
template: template
}); });
} }

View File

@ -9,14 +9,10 @@
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core'; import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
import {StringMapWrapper} from './facade/collection'; import {StringMapWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
function _isDirectiveMetadata(type: any): type is Directive {
return type instanceof Directive;
}
/* /*
* Resolve a `Type` for {@link Directive}. * Resolve a `Type` for {@link Directive}.
* *
@ -32,54 +28,57 @@ export class DirectiveResolver {
* Return {@link Directive} for a given `Type`. * Return {@link Directive} for a given `Type`.
*/ */
resolve(type: Type<any>, throwIfNotFound = true): Directive { resolve(type: Type<any>, throwIfNotFound = true): Directive {
var typeMetadata = this._reflector.annotations(resolveForwardRef(type)); const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
if (isPresent(typeMetadata)) { if (typeMetadata) {
var metadata = typeMetadata.find(_isDirectiveMetadata); const metadata = typeMetadata.find(isDirectiveMetadata);
if (isPresent(metadata)) { if (metadata) {
var propertyMetadata = this._reflector.propMetadata(type); const propertyMetadata = this._reflector.propMetadata(type);
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
} }
} }
if (throwIfNotFound) { if (throwIfNotFound) {
throw new Error(`No Directive annotation found on ${stringify(type)}`); throw new Error(`No Directive annotation found on ${stringify(type)}`);
} }
return null; return null;
} }
private _mergeWithPropertyMetadata( private _mergeWithPropertyMetadata(
dm: Directive, propertyMetadata: {[key: string]: any[]}, dm: Directive, propertyMetadata: {[key: string]: any[]},
directiveType: Type<any>): Directive { directiveType: Type<any>): Directive {
var inputs: string[] = []; const inputs: string[] = [];
var outputs: string[] = []; const outputs: string[] = [];
var host: {[key: string]: string} = {}; const host: {[key: string]: string} = {};
var queries: {[key: string]: any} = {}; const queries: {[key: string]: any} = {};
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => { Object.keys(propertyMetadata).forEach((propName: string) => {
metadata.forEach(a => {
propertyMetadata[propName].forEach(a => {
if (a instanceof Input) { if (a instanceof Input) {
if (isPresent(a.bindingPropertyName)) { if (a.bindingPropertyName) {
inputs.push(`${propName}: ${a.bindingPropertyName}`); inputs.push(`${propName}: ${a.bindingPropertyName}`);
} else { } else {
inputs.push(propName); inputs.push(propName);
} }
} else if (a instanceof Output) { } else if (a instanceof Output) {
const output: Output = a; const output: Output = a;
if (isPresent(output.bindingPropertyName)) { if (output.bindingPropertyName) {
outputs.push(`${propName}: ${output.bindingPropertyName}`); outputs.push(`${propName}: ${output.bindingPropertyName}`);
} else { } else {
outputs.push(propName); outputs.push(propName);
} }
} else if (a instanceof HostBinding) { } else if (a instanceof HostBinding) {
const hostBinding: HostBinding = a; const hostBinding: HostBinding = a;
if (isPresent(hostBinding.hostPropertyName)) { if (hostBinding.hostPropertyName) {
host[`[${hostBinding.hostPropertyName}]`] = propName; host[`[${hostBinding.hostPropertyName}]`] = propName;
} else { } else {
host[`[${propName}]`] = propName; host[`[${propName}]`] = propName;
} }
} else if (a instanceof HostListener) { } else if (a instanceof HostListener) {
const hostListener: HostListener = a; const hostListener: HostListener = a;
var args = isPresent(hostListener.args) ? (<any[]>hostListener.args).join(', ') : ''; const args = hostListener.args || [];
host[`(${hostListener.eventName})`] = `${propName}(${args})`; host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
} else if (a instanceof Query) { } else if (a instanceof Query) {
queries[propName] = a; queries[propName] = a;
} }
@ -91,13 +90,14 @@ export class DirectiveResolver {
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); } private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
private _merge( 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 { 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[] = 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) => { inputs.forEach((inputDef: string) => {
const publicName = this._extractPublicName(inputDef); const publicName = this._extractPublicName(inputDef);
if (inputNames.indexOf(publicName) > -1) { if (inputNames.indexOf(publicName) > -1) {
@ -105,16 +105,15 @@ export class DirectiveResolver {
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`); `Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
} }
}); });
mergedInputs = dm.inputs.concat(inputs);
} else { mergedInputs.unshift(...directive.inputs);
mergedInputs = inputs;
} }
let mergedOutputs: string[]; let mergedOutputs: string[] = outputs;
if (isPresent(dm.outputs)) { if (directive.outputs) {
const outputNames: string[] = 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) => { outputs.forEach((outputDef: string) => {
const publicName = this._extractPublicName(outputDef); const publicName = this._extractPublicName(outputDef);
@ -123,47 +122,48 @@ export class DirectiveResolver {
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`); `Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
} }
}); });
mergedOutputs = dm.outputs.concat(outputs); mergedOutputs.unshift(...directive.outputs);
} else {
mergedOutputs = outputs;
} }
var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host; const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
var mergedQueries = const mergedQueries =
isPresent(dm.queries) ? StringMapWrapper.merge(dm.queries, queries) : queries; directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
if (dm instanceof Component) { if (directive instanceof Component) {
return new Component({ return new Component({
selector: dm.selector, selector: directive.selector,
inputs: mergedInputs, inputs: mergedInputs,
outputs: mergedOutputs, outputs: mergedOutputs,
host: mergedHost, host: mergedHost,
exportAs: dm.exportAs, exportAs: directive.exportAs,
moduleId: dm.moduleId, moduleId: directive.moduleId,
queries: mergedQueries, queries: mergedQueries,
changeDetection: dm.changeDetection, changeDetection: directive.changeDetection,
providers: dm.providers, providers: directive.providers,
viewProviders: dm.viewProviders, viewProviders: directive.viewProviders,
entryComponents: dm.entryComponents, entryComponents: directive.entryComponents,
template: dm.template, template: directive.template,
templateUrl: dm.templateUrl, templateUrl: directive.templateUrl,
styles: dm.styles, styles: directive.styles,
styleUrls: dm.styleUrls, styleUrls: directive.styleUrls,
encapsulation: dm.encapsulation, encapsulation: directive.encapsulation,
animations: dm.animations, animations: directive.animations,
interpolation: dm.interpolation interpolation: directive.interpolation
}); });
} else { } else {
return new Directive({ return new Directive({
selector: dm.selector, selector: directive.selector,
inputs: mergedInputs, inputs: mergedInputs,
outputs: mergedOutputs, outputs: mergedOutputs,
host: mergedHost, host: mergedHost,
exportAs: dm.exportAs, exportAs: directive.exportAs,
queries: mergedQueries, queries: mergedQueries,
providers: dm.providers providers: directive.providers
}); });
} }
} }
} }
function isDirectiveMetadata(type: any): type is Directive {
return type instanceof Directive;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ListWrapper} from '../facade/collection';
import {isBlank} from '../facade/lang'; import {isBlank} from '../facade/lang';
export class ParserError { export class ParserError {
@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor {
} }
visitAll(asts: any[]): any[] { visitAll(asts: any[]): any[] {
var res = ListWrapper.createFixedSize(asts.length); var res = new Array(asts.length);
for (var i = 0; i < asts.length; ++i) { for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this); res[i] = asts[i].visit(this);
} }

View File

@ -27,7 +27,6 @@ const _PLACEHOLDER_TAG = 'x';
const _SOURCE_TAG = 'source'; const _SOURCE_TAG = 'source';
const _TARGET_TAG = 'target'; const _TARGET_TAG = 'target';
const _UNIT_TAG = 'trans-unit'; 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/os/xliff-core.html
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.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'}); let transUnit = new xml.Tag(_UNIT_TAG, {id: id, datatype: 'html'});
transUnit.children.push( transUnit.children.push(
_CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), _CR(8), new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
new xml.Tag(_TARGET_TAG)); new xml.CR(8), new xml.Tag(_TARGET_TAG));
if (message.description) { if (message.description) {
transUnit.children.push( transUnit.children.push(
_CR(8), new xml.CR(8),
new xml.Tag( new xml.Tag(
'note', {priority: '1', from: 'description'}, [new xml.Text(message.description)])); 'note', {priority: '1', from: 'description'}, [new xml.Text(message.description)]));
} }
if (message.meaning) { if (message.meaning) {
transUnit.children.push( transUnit.children.push(
_CR(8), new xml.CR(8),
new xml.Tag('note', {priority: '1', from: 'meaning'}, [new xml.Text(message.meaning)])); 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( const file = new xml.Tag(
'file', {'source-language': _SOURCE_LANG, datatype: 'plaintext', original: 'ng2.template'}, 'file', {'source-language': _SOURCE_LANG, datatype: 'plaintext', original: 'ng2.template'},
[_CR(4), body, _CR(2)]); [new xml.CR(4), body, new xml.CR(2)]);
const xliff = new xml.Tag('xliff', {version: _VERSION, xmlns: _XMLNS}, [_CR(2), file, _CR()]); 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[]} { 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[] { 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) { if (ph.isVoid) {
// void tags have no children nor closing tags // void tags have no children nor closing tags
return [startTagPh]; 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]; return [startTagPh, ...this.serialize(ph.children), closeTagPh];
} }
@ -287,3 +291,14 @@ class _LoadVisitor implements ml.Visitor {
this._errors.push(new I18nError(node.sourceSpan, message)); 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}`;
}
}

View File

@ -43,7 +43,6 @@ export class Xmb implements Serializer {
write(messageMap: {[k: string]: i18n.Message}): string { write(messageMap: {[k: string]: i18n.Message}): string {
const visitor = new _Visitor(); const visitor = new _Visitor();
let rootNode = new xml.Tag(_MESSAGES_TAG); let rootNode = new xml.Tag(_MESSAGES_TAG);
rootNode.children.push(new xml.Text('\n'));
Object.keys(messageMap).forEach((id) => { Object.keys(messageMap).forEach((id) => {
const message = messageMap[id]; const message = messageMap[id];
@ -58,16 +57,18 @@ export class Xmb implements Serializer {
} }
rootNode.children.push( rootNode.children.push(
new xml.Text(' '), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)), new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
new xml.Text('\n'));
}); });
rootNode.children.push(new xml.CR());
return xml.serialize([ return xml.serialize([
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), new xml.Declaration({version: '1.0', encoding: 'UTF-8'}),
new xml.Text('\n'), new xml.CR(),
new xml.Doctype(_MESSAGES_TAG, _DOCTYPE), new xml.Doctype(_MESSAGES_TAG, _DOCTYPE),
new xml.Text('\n'), new xml.CR(),
rootNode, rootNode,
new xml.CR(),
]); ]);
} }

View File

@ -88,6 +88,10 @@ export class Text implements Node {
visit(visitor: IVisitor): any { return visitor.visitText(this); } 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][] = [ const _ESCAPED_CHARS: [RegExp, string][] = [
[/&/g, '&amp;'], [/&/g, '&amp;'],
[/"/g, '&quot;'], [/"/g, '&quot;'],

View File

@ -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 {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 {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'; import {assetUrl} from './util';
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view'); var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
@ -266,11 +266,6 @@ export class Identifiers {
moduleUrl: assetUrl('core', 'i18n/tokens'), moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: TRANSLATIONS_FORMAT_ runtime: TRANSLATIONS_FORMAT_
}; };
static AnimationOutput: IdentifierSpec = {
name: 'AnimationOutput',
moduleUrl: assetUrl('core', 'animation/animation_output'),
runtime: AnimationOutput
};
} }
export function resolveIdentifier(identifier: IdentifierSpec) { export function resolveIdentifier(identifier: IdentifierSpec) {

View File

@ -123,7 +123,7 @@ class _TreeBuilder {
// read = // read =
while (this._peek.type === lex.TokenType.EXPANSION_CASE_VALUE) { while (this._peek.type === lex.TokenType.EXPANSION_CASE_VALUE) {
let expCase = this._parseExpansionCase(); let expCase = this._parseExpansionCase();
if (isBlank(expCase)) return; // error if (!expCase) return; // error
cases.push(expCase); cases.push(expCase);
} }
@ -154,7 +154,7 @@ class _TreeBuilder {
const start = this._advance(); const start = this._advance();
const exp = this._collectExpansionExpTokens(start); const exp = this._collectExpansionExpTokens(start);
if (isBlank(exp)) return null; if (!exp) return null;
const end = this._advance(); const end = this._advance();
exp.push(new lex.Token(lex.TokenType.EOF, [], end.sourceSpan)); exp.push(new lex.Token(lex.TokenType.EOF, [], end.sourceSpan));

View File

@ -9,8 +9,8 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata'; import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang'; import {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 * as o from './output/output_ast';
import {convertValueToOutputAst} from './output/value_util'; import {convertValueToOutputAst} from './output/value_util';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util'; import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
@ -190,7 +190,7 @@ class _InjectorBuilder {
resolvedProviderValueExpr = providerValueExpressions[0]; resolvedProviderValueExpr = providerValueExpressions[0];
type = providerValueExpressions[0].type; type = providerValueExpressions[0].type;
} }
if (isBlank(type)) { if (!type) {
type = o.DYNAMIC_TYPE; type = o.DYNAMIC_TYPE;
} }
if (isEager) { if (isEager) {
@ -223,11 +223,11 @@ class _InjectorBuilder {
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) { resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
result = o.THIS_EXPR; result = o.THIS_EXPR;
} }
if (isBlank(result)) { if (!result) {
result = this._instances.get(dep.token.reference); result = this._instances.get(dep.token.reference);
} }
} }
if (isBlank(result)) { if (!result) {
var args = [createDiTokenExpression(dep.token)]; var args = [createDiTokenExpression(dep.token)];
if (dep.isOptional) { if (dep.isOptional) {
args.push(o.NULL_EXPR); args.push(o.NULL_EXPR);

View File

@ -8,7 +8,9 @@
import {SchemaMetadata} from '@angular/core'; 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 {DirectiveNormalizer} from './directive_normalizer';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers'; import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver'; import {CompileMetadataResolver} from './metadata_resolver';
@ -28,6 +30,9 @@ export class NgModulesSummary {
} }
export class OfflineCompiler { export class OfflineCompiler {
private _animationParser = new AnimationParser();
private _animationCompiler = new AnimationCompiler();
constructor( constructor(
private _metadataResolver: CompileMetadataResolver, private _metadataResolver: CompileMetadataResolver,
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
@ -162,14 +167,19 @@ export class OfflineCompiler {
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet, pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
fileSuffix: string, targetStatements: o.Statement[]): string { fileSuffix: string, targetStatements: o.Statement[]): string {
const parsedAnimations = this._animationParser.parseComponent(compMeta);
const parsedTemplate = this._templateParser.parse( const parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name); compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]); const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const viewResult = const compiledAnimations =
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes); this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
const viewResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
if (componentStyles) { if (componentStyles) {
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix)); targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
} }
compiledAnimations.forEach(
entry => { entry.statements.forEach(statement => { targetStatements.push(statement); }); });
targetStatements.push(..._resolveViewStatements(viewResult)); targetStatements.push(..._resolveViewStatements(viewResult));
return viewResult.viewFactoryVar; return viewResult.viewFactoryVar;
} }

View File

@ -400,7 +400,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
} }
visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void { visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void {
statements.forEach((stmt) => { return stmt.visitStatement(this, ctx); }); statements.forEach((stmt) => stmt.visitStatement(this, ctx));
} }
} }

View File

@ -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 {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter'; import {AbstractJsEmitterVisitor} from './abstract_js_emitter';

View File

@ -8,9 +8,7 @@
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection'; import {isPresent, isString} from '../facade/lang';
import {isBlank, isPresent, isString} from '../facade/lang';
import {ValueTransformer, visitValue} from '../util';
@ -21,7 +19,7 @@ export enum TypeModifier {
export abstract class Type { export abstract class Type {
constructor(public modifiers: TypeModifier[] = null) { constructor(public modifiers: TypeModifier[] = null) {
if (isBlank(modifiers)) { if (!modifiers) {
this.modifiers = []; this.modifiers = [];
} }
} }
@ -464,7 +462,7 @@ export enum StmtModifier {
export abstract class Statement { export abstract class Statement {
constructor(public modifiers: StmtModifier[] = null) { constructor(public modifiers: StmtModifier[] = null) {
if (isBlank(modifiers)) { if (!modifiers) {
this.modifiers = []; this.modifiers = [];
} }
} }
@ -519,7 +517,7 @@ export class ReturnStatement extends Statement {
export class AbstractClassPart { export class AbstractClassPart {
constructor(public type: Type = null, public modifiers: StmtModifier[]) { constructor(public type: Type = null, public modifiers: StmtModifier[]) {
if (isBlank(modifiers)) { if (!modifiers) {
this.modifiers = []; this.modifiers = [];
} }
} }

View File

@ -6,11 +6,6 @@
* found in the LICENSE file at https://angular.io/license * 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> // asset:<package-name>/<realm>/<path-to-module>
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/; var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/;

View File

@ -8,7 +8,6 @@
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection';
import {ValueTransformer, visitValue} from '../util'; import {ValueTransformer, visitValue} from '../util';
import * as o from './output_ast'; import * as o from './output_ast';
@ -24,9 +23,7 @@ class _ValueOutputAstTransformer implements ValueTransformer {
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression { visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
var entries: Array<string|o.Expression>[] = []; var entries: Array<string|o.Expression>[] = [];
StringMapWrapper.forEach(map, (value: any, key: string) => { Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); });
entries.push([key, visitValue(value, this, null)]);
});
return o.literalMap(entries, type); return o.literalMap(entries, type);
} }

View File

@ -12,7 +12,6 @@ export const isDefaultChangeDetectionStrategy: typeof r.isDefaultChangeDetection
r.isDefaultChangeDetectionStrategy; r.isDefaultChangeDetectionStrategy;
export type ChangeDetectorStatus = typeof r._ChangeDetectorStatus; export type ChangeDetectorStatus = typeof r._ChangeDetectorStatus;
export const ChangeDetectorStatus: typeof r.ChangeDetectorStatus = r.ChangeDetectorStatus; export const ChangeDetectorStatus: typeof r.ChangeDetectorStatus = r.ChangeDetectorStatus;
r.CHANGE_DETECTION_STRATEGY_VALUES;
export type LifecycleHooks = typeof r._LifecycleHooks; export type LifecycleHooks = typeof r._LifecycleHooks;
export const LifecycleHooks: typeof r.LifecycleHooks = r.LifecycleHooks; export const LifecycleHooks: typeof r.LifecycleHooks = r.LifecycleHooks;
export const LIFECYCLE_HOOKS_VALUES: typeof r.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES; 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 const AnimationKeyframe: typeof r.AnimationKeyframe = r.AnimationKeyframe;
export type AnimationStyles = typeof r._AnimationStyles; export type AnimationStyles = typeof r._AnimationStyles;
export const AnimationStyles: typeof r.AnimationStyles = 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 ANY_STATE = r.ANY_STATE;
export const DEFAULT_STATE = r.DEFAULT_STATE; export const DEFAULT_STATE = r.DEFAULT_STATE;
export const EMPTY_STATE = r.EMPTY_STATE; export const EMPTY_STATE = r.EMPTY_STATE;

View File

@ -12,7 +12,7 @@ import {ListWrapper, MapWrapper} from './facade/collection';
import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang'; import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers'; import {Identifiers, resolveIdentifierToken} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util'; 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 { export class ProviderError extends ParseError {
constructor(message: string, span: ParseSourceSpan) { super(span, message); } constructor(message: string, span: ParseSourceSpan) { super(span, message); }
@ -50,14 +50,14 @@ export class ProviderElementContext {
private _hasViewContainer: boolean = false; private _hasViewContainer: boolean = false;
constructor( constructor(
private _viewContext: ProviderViewContext, private _parent: ProviderElementContext, public viewContext: ProviderViewContext, private _parent: ProviderElementContext,
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], attrs: AttrAst[], private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], attrs: AttrAst[],
refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) { refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
this._attrs = {}; this._attrs = {};
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value); attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive); var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
this._allProviders = this._allProviders =
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors); _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
this._contentQueries = _getContentQueries(directivesMeta); this._contentQueries = _getContentQueries(directivesMeta);
var queriedTokens = new Map<any, boolean>(); var queriedTokens = new Map<any, boolean>();
MapWrapper.values(this._allProviders).forEach((provider) => { MapWrapper.values(this._allProviders).forEach((provider) => {
@ -124,7 +124,7 @@ export class ProviderElementContext {
} }
currentEl = currentEl._parent; currentEl = currentEl._parent;
} }
queries = this._viewContext.viewQueries.get(token.reference); queries = this.viewContext.viewQueries.get(token.reference);
if (isPresent(queries)) { if (isPresent(queries)) {
ListWrapper.addAll(result, queries); ListWrapper.addAll(result, queries);
} }
@ -136,8 +136,7 @@ export class ProviderElementContext {
requestingProviderType: ProviderAstType, token: CompileTokenMetadata, requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
eager: boolean): ProviderAst { eager: boolean): ProviderAst {
var resolvedProvider = this._allProviders.get(token.reference); var resolvedProvider = this._allProviders.get(token.reference);
if (isBlank(resolvedProvider) || if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive ||
((requestingProviderType === ProviderAstType.Directive ||
requestingProviderType === ProviderAstType.PublicService) && requestingProviderType === ProviderAstType.PublicService) &&
resolvedProvider.providerType === ProviderAstType.PrivateService) || resolvedProvider.providerType === ProviderAstType.PrivateService) ||
((requestingProviderType === ProviderAstType.PrivateService || ((requestingProviderType === ProviderAstType.PrivateService ||
@ -150,7 +149,7 @@ export class ProviderElementContext {
return transformedProviderAst; return transformedProviderAst;
} }
if (isPresent(this._seenProviders.get(token.reference))) { 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)); `Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
return null; return null;
} }
@ -239,12 +238,12 @@ export class ProviderElementContext {
result = this._getLocalDependency(requestingProviderType, dep, eager); result = this._getLocalDependency(requestingProviderType, dep, eager);
} }
if (dep.isSelf) { if (dep.isSelf) {
if (isBlank(result) && dep.isOptional) { if (!result && dep.isOptional) {
result = new CompileDiDependencyMetadata({isValue: true, value: null}); result = new CompileDiDependencyMetadata({isValue: true, value: null});
} }
} else { } else {
// check parent elements // check parent elements
while (isBlank(result) && isPresent(currElement._parent)) { while (!result && isPresent(currElement._parent)) {
var prevElement = currElement; var prevElement = currElement;
currElement = currElement._parent; currElement = currElement._parent;
if (prevElement._isViewRoot) { if (prevElement._isViewRoot) {
@ -253,10 +252,10 @@ export class ProviderElementContext {
result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager); result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
} }
// check @Host restriction // check @Host restriction
if (isBlank(result)) { if (!result) {
if (!dep.isHost || this._viewContext.component.type.isHost || if (!dep.isHost || this.viewContext.component.type.isHost ||
this._viewContext.component.type.reference === dep.token.reference || this.viewContext.component.type.reference === dep.token.reference ||
isPresent(this._viewContext.viewProviders.get(dep.token.reference))) { isPresent(this.viewContext.viewProviders.get(dep.token.reference))) {
result = dep; result = dep;
} else { } else {
result = dep.isOptional ? result = dep.isOptional ?
@ -265,8 +264,8 @@ export class ProviderElementContext {
} }
} }
} }
if (isBlank(result)) { if (!result) {
this._viewContext.errors.push( this.viewContext.errors.push(
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan)); new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan));
} }
return result; return result;
@ -311,7 +310,7 @@ export class NgModuleProviderAnalyzer {
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst { private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
var resolvedProvider = this._allProviders.get(token.reference); var resolvedProvider = this._allProviders.get(token.reference);
if (isBlank(resolvedProvider)) { if (!resolvedProvider) {
return null; return null;
} }
var transformedProviderAst = this._transformedProviders.get(token.reference); var transformedProviderAst = this._transformedProviders.get(token.reference);
@ -414,7 +413,7 @@ function _normalizeProviders(
providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>, providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>,
sourceSpan: ParseSourceSpan, targetErrors: ParseError[], sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] { targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
if (isBlank(targetProviders)) { if (!targetProviders) {
targetProviders = []; targetProviders = [];
} }
if (isPresent(providers)) { if (isPresent(providers)) {
@ -479,7 +478,7 @@ function _resolveProviders(
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`, `Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`,
sourceSpan)); sourceSpan));
} }
if (isBlank(resolvedProvider)) { if (!resolvedProvider) {
const lifecycleHooks = const lifecycleHooks =
provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ? provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ?
provider.token.identifier.lifecycleHooks : provider.token.identifier.lifecycleHooks :
@ -530,7 +529,7 @@ function _getContentQueries(directives: CompileDirectiveMetadata[]):
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) { function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
query.selectors.forEach((token: CompileTokenMetadata) => { query.selectors.forEach((token: CompileTokenMetadata) => {
var entry = map.get(token.reference); var entry = map.get(token.reference);
if (isBlank(entry)) { if (!entry) {
entry = []; entry = [];
map.set(token.reference, entry); map.set(token.reference, entry);
} }

View File

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license * 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 {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
import {isBlank, stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {CompileMetadataResolver} from './metadata_resolver'; import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler'; import {NgModuleCompiler} from './ng_module_compiler';
import * as ir from './output/output_ast'; import * as ir from './output/output_ast';
@ -23,8 +24,6 @@ import {TemplateParser} from './template_parser/template_parser';
import {SyncAsyncResult} from './util'; import {SyncAsyncResult} from './util';
import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler'; import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
/** /**
* An internal module of the Angular compiler that begins with component types, * An internal module of the Angular compiler that begins with component types,
* extracts templates, and eventually produces a compiled version of the component * 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 _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>(); private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>(); private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
private _animationParser = new AnimationParser();
private _animationCompiler = new AnimationCompiler();
constructor( constructor(
private _injector: Injector, private _metadataResolver: CompileMetadataResolver, private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
@ -190,7 +191,7 @@ export class RuntimeCompiler implements Compiler {
private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate { private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate {
var compiledTemplate = this._compiledHostTemplateCache.get(compType); var compiledTemplate = this._compiledHostTemplateCache.get(compType);
if (isBlank(compiledTemplate)) { if (!compiledTemplate) {
var compMeta = this._metadataResolver.getDirectiveMetadata(compType); var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta); assertComponent(compMeta);
var hostMeta = createHostComponentMeta(compMeta); var hostMeta = createHostComponentMeta(compMeta);
@ -205,7 +206,7 @@ export class RuntimeCompiler implements Compiler {
private _createCompiledTemplate( private _createCompiledTemplate(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata): CompiledTemplate { compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata): CompiledTemplate {
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.reference); var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.reference);
if (isBlank(compiledTemplate)) { if (!compiledTemplate) {
assertComponent(compMeta); assertComponent(compMeta);
compiledTemplate = new CompiledTemplate( compiledTemplate = new CompiledTemplate(
false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives, false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives,
@ -253,12 +254,15 @@ export class RuntimeCompiler implements Compiler {
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
const viewCompMetas = template.viewComponentTypes.map( const viewCompMetas = template.viewComponentTypes.map(
(compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta); (compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta);
const parsedAnimations = this._animationParser.parseComponent(compMeta);
const parsedTemplate = this._templateParser.parse( const parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas),
template.viewPipes, template.schemas, compMeta.type.name); template.viewPipes, template.schemas, compMeta.type.name);
const compiledAnimations =
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
const compileResult = this._viewCompiler.compileComponent( const compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
template.viewPipes); template.viewPipes, compiledAnimations);
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
let depTemplate: CompiledTemplate; let depTemplate: CompiledTemplate;
if (dep instanceof ViewFactoryDependency) { if (dep instanceof ViewFactoryDependency) {
@ -275,6 +279,8 @@ export class RuntimeCompiler implements Compiler {
}); });
const statements = const statements =
stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements); stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements);
compiledAnimations.forEach(
entry => { entry.statements.forEach(statement => { statements.push(statement); }); });
let factory: any; let factory: any;
if (!this._compilerConfig.useJit) { if (!this._compilerConfig.useJit) {
factory = interpretStatements(statements, compileResult.viewFactoryVar); factory = interpretStatements(statements, compileResult.viewFactoryVar);

View File

@ -344,4 +344,26 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; } getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; }
getDefaultComponentElementName(): string { return 'ng-component'; } 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};
}
}
} }

View File

@ -14,4 +14,6 @@ export abstract class ElementSchemaRegistry {
abstract securityContext(tagName: string, propName: string): any; abstract securityContext(tagName: string, propName: string): any;
abstract getMappedPropName(propName: string): string; abstract getMappedPropName(propName: string): string;
abstract getDefaultComponentElementName(): string; abstract getDefaultComponentElementName(): string;
abstract validateProperty(name: string): {error: boolean, msg?: string};
abstract validateAttribute(name: string): {error: boolean, msg?: string};
} }

View File

@ -6,18 +6,13 @@
* found in the LICENSE file at https://angular.io/license * 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'; import {getHtmlTagDefinition} from './ml_parser/html_tags';
const _EMPTY_ATTR_VALUE = '';
const _SELECTOR_REGEXP = new RegExp( const _SELECTOR_REGEXP = new RegExp(
'(\\:not\\()|' + //":not(" '(\\:not\\()|' + //":not("
'([-\\w]+)|' + // "tag" '([-\\w]+)|' + // "tag"
'(?:\\.([-\\w]+))|' + // ".class" '(?:\\.([-\\w]+))|' + // ".class"
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" or "[name*=value]" '(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
'(\\))|' + // ")" '(\\))|' + // ")"
'(\\s*,\\s*)', // "," '(\\s*,\\s*)', // ","
'g'); 'g');
@ -34,21 +29,21 @@ export class CssSelector {
notSelectors: CssSelector[] = []; notSelectors: CssSelector[] = [];
static parse(selector: string): CssSelector[] { static parse(selector: string): CssSelector[] {
var results: CssSelector[] = []; const results: CssSelector[] = [];
var _addResult = (res: CssSelector[], cssSel: CssSelector) => { const _addResult = (res: CssSelector[], cssSel: CssSelector) => {
if (cssSel.notSelectors.length > 0 && isBlank(cssSel.element) && if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
ListWrapper.isEmpty(cssSel.classNames) && ListWrapper.isEmpty(cssSel.attrs)) { cssSel.attrs.length == 0) {
cssSel.element = '*'; cssSel.element = '*';
} }
res.push(cssSel); res.push(cssSel);
}; };
var cssSelector = new CssSelector(); let cssSelector = new CssSelector();
var match: string[]; let match: string[];
var current = cssSelector; let current = cssSelector;
var inNot = false; let inNot = false;
_SELECTOR_REGEXP.lastIndex = 0; _SELECTOR_REGEXP.lastIndex = 0;
while (isPresent(match = _SELECTOR_REGEXP.exec(selector))) { while (match = _SELECTOR_REGEXP.exec(selector)) {
if (isPresent(match[1])) { if (match[1]) {
if (inNot) { if (inNot) {
throw new Error('Nesting :not is not allowed in a selector'); throw new Error('Nesting :not is not allowed in a selector');
} }
@ -56,20 +51,20 @@ export class CssSelector {
current = new CssSelector(); current = new CssSelector();
cssSelector.notSelectors.push(current); cssSelector.notSelectors.push(current);
} }
if (isPresent(match[2])) { if (match[2]) {
current.setElement(match[2]); current.setElement(match[2]);
} }
if (isPresent(match[3])) { if (match[3]) {
current.addClassName(match[3]); current.addClassName(match[3]);
} }
if (isPresent(match[4])) { if (match[4]) {
current.addAttribute(match[4], match[5]); current.addAttribute(match[4], match[5]);
} }
if (isPresent(match[6])) { if (match[6]) {
inNot = false; inNot = false;
current = cssSelector; current = cssSelector;
} }
if (isPresent(match[7])) { if (match[7]) {
if (inNot) { if (inNot) {
throw new Error('Multiple selectors in :not are not supported'); throw new Error('Multiple selectors in :not are not supported');
} }
@ -106,37 +101,22 @@ export class CssSelector {
`<${tagName}${classAttr}${attrs}></${tagName}>`; `<${tagName}${classAttr}${attrs}></${tagName}>`;
} }
addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) { addAttribute(name: string, value: string = '') {
this.attrs.push(name); this.attrs.push(name, value && value.toLowerCase() || '');
if (isPresent(value)) {
value = value.toLowerCase();
} else {
value = _EMPTY_ATTR_VALUE;
}
this.attrs.push(value);
} }
addClassName(name: string) { this.classNames.push(name.toLowerCase()); } addClassName(name: string) { this.classNames.push(name.toLowerCase()); }
toString(): string { toString(): string {
var res = ''; let res: string = this.element || '';
if (isPresent(this.element)) { if (this.classNames) {
res += this.element; this.classNames.forEach(klass => res += `.${klass}`);
} }
if (isPresent(this.classNames)) { if (this.attrs) {
for (var i = 0; i < this.classNames.length; i++) { for (let i = 0; i < this.attrs.length; i += 2) {
res += '.' + this.classNames[i]; const name = this.attrs[i];
} const value = this.attrs[i + 1];
} res += `[${name}${value ? '=' + value : ''}]`;
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 += ']';
} }
} }
this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`); this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
@ -150,26 +130,26 @@ export class CssSelector {
*/ */
export class SelectorMatcher { export class SelectorMatcher {
static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher { static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher {
var notMatcher = new SelectorMatcher(); const notMatcher = new SelectorMatcher();
notMatcher.addSelectables(notSelectors, null); notMatcher.addSelectables(notSelectors, null);
return notMatcher; return notMatcher;
} }
private _elementMap = new Map<string, SelectorContext[]>(); private _elementMap: {[k: string]: SelectorContext[]} = {};
private _elementPartialMap = new Map<string, SelectorMatcher>(); private _elementPartialMap: {[k: string]: SelectorMatcher} = {};
private _classMap = new Map<string, SelectorContext[]>(); private _classMap: {[k: string]: SelectorContext[]} = {};
private _classPartialMap = new Map<string, SelectorMatcher>(); private _classPartialMap: {[k: string]: SelectorMatcher} = {};
private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>(); private _attrValueMap: {[k: string]: {[k: string]: SelectorContext[]}} = {};
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>(); private _attrValuePartialMap: {[k: string]: {[k: string]: SelectorMatcher}} = {};
private _listContexts: SelectorListContext[] = []; private _listContexts: SelectorListContext[] = [];
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) { addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
var listContext: SelectorListContext = null; let listContext: SelectorListContext = null;
if (cssSelectors.length > 1) { if (cssSelectors.length > 1) {
listContext = new SelectorListContext(cssSelectors); listContext = new SelectorListContext(cssSelectors);
this._listContexts.push(listContext); 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); this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
} }
} }
@ -181,14 +161,14 @@ export class SelectorMatcher {
*/ */
private _addSelectable( private _addSelectable(
cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) { cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) {
var matcher: SelectorMatcher = this; let matcher: SelectorMatcher = this;
var element = cssSelector.element; const element = cssSelector.element;
var classNames = cssSelector.classNames; const classNames = cssSelector.classNames;
var attrs = cssSelector.attrs; const attrs = cssSelector.attrs;
var selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
if (isPresent(element)) { if (element) {
var isTerminal = attrs.length === 0 && classNames.length === 0; const isTerminal = attrs.length === 0 && classNames.length === 0;
if (isTerminal) { if (isTerminal) {
this._addTerminal(matcher._elementMap, element, selectable); this._addTerminal(matcher._elementMap, element, selectable);
} else { } else {
@ -196,10 +176,10 @@ export class SelectorMatcher {
} }
} }
if (isPresent(classNames)) { if (classNames) {
for (var index = 0; index < classNames.length; index++) { for (let i = 0; i < classNames.length; i++) {
var isTerminal = attrs.length === 0 && index === classNames.length - 1; const isTerminal = attrs.length === 0 && i === classNames.length - 1;
var className = classNames[index]; const className = classNames[i];
if (isTerminal) { if (isTerminal) {
this._addTerminal(matcher._classMap, className, selectable); this._addTerminal(matcher._classMap, className, selectable);
} else { } else {
@ -208,47 +188,47 @@ export class SelectorMatcher {
} }
} }
if (isPresent(attrs)) { if (attrs) {
for (var index = 0; index < attrs.length;) { for (let i = 0; i < attrs.length; i += 2) {
var isTerminal = index === attrs.length - 2; const isTerminal = i === attrs.length - 2;
var attrName = attrs[index++]; const name = attrs[i];
var attrValue = attrs[index++]; const value = attrs[i + 1];
if (isTerminal) { if (isTerminal) {
var terminalMap = matcher._attrValueMap; const terminalMap = matcher._attrValueMap;
var terminalValuesMap = terminalMap.get(attrName); let terminalValuesMap = terminalMap[name];
if (isBlank(terminalValuesMap)) { if (!terminalValuesMap) {
terminalValuesMap = new Map<string, SelectorContext[]>(); terminalValuesMap = {};
terminalMap.set(attrName, terminalValuesMap); terminalMap[name] = terminalValuesMap;
} }
this._addTerminal(terminalValuesMap, attrValue, selectable); this._addTerminal(terminalValuesMap, value, selectable);
} else { } else {
var parttialMap = matcher._attrValuePartialMap; let partialMap = matcher._attrValuePartialMap;
var partialValuesMap = parttialMap.get(attrName); let partialValuesMap = partialMap[name];
if (isBlank(partialValuesMap)) { if (!partialValuesMap) {
partialValuesMap = new Map<string, SelectorMatcher>(); partialValuesMap = {};
parttialMap.set(attrName, partialValuesMap); partialMap[name] = partialValuesMap;
} }
matcher = this._addPartial(partialValuesMap, attrValue); matcher = this._addPartial(partialValuesMap, value);
} }
} }
} }
} }
private _addTerminal( private _addTerminal(
map: Map<string, SelectorContext[]>, name: string, selectable: SelectorContext) { map: {[k: string]: SelectorContext[]}, name: string, selectable: SelectorContext) {
var terminalList = map.get(name); let terminalList = map[name];
if (isBlank(terminalList)) { if (!terminalList) {
terminalList = []; terminalList = [];
map.set(name, terminalList); map[name] = terminalList;
} }
terminalList.push(selectable); terminalList.push(selectable);
} }
private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher { private _addPartial(map: {[k: string]: SelectorMatcher}, name: string): SelectorMatcher {
var matcher = map.get(name); let matcher = map[name];
if (isBlank(matcher)) { if (!matcher) {
matcher = new SelectorMatcher(); matcher = new SelectorMatcher();
map.set(name, matcher); map[name] = matcher;
} }
return matcher; return matcher;
} }
@ -261,12 +241,12 @@ export class SelectorMatcher {
* @return boolean true if a match was found * @return boolean true if a match was found
*/ */
match(cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean { match(cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean {
var result = false; let result = false;
var element = cssSelector.element; const element = cssSelector.element;
var classNames = cssSelector.classNames; const classNames = cssSelector.classNames;
var attrs = cssSelector.attrs; 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; this._listContexts[i].alreadyMatched = false;
} }
@ -274,9 +254,9 @@ export class SelectorMatcher {
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
result; result;
if (isPresent(classNames)) { if (classNames) {
for (var index = 0; index < classNames.length; index++) { for (let i = 0; i < classNames.length; i++) {
var className = classNames[index]; const className = classNames[i];
result = result =
this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result; this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
result = result =
@ -285,28 +265,25 @@ export class SelectorMatcher {
} }
} }
if (isPresent(attrs)) { if (attrs) {
for (var index = 0; index < attrs.length;) { for (let i = 0; i < attrs.length; i += 2) {
var attrName = attrs[index++]; const name = attrs[i];
var attrValue = attrs[index++]; const value = attrs[i + 1];
var terminalValuesMap = this._attrValueMap.get(attrName); const terminalValuesMap = this._attrValueMap[name];
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) { if (value) {
result = this._matchTerminal( result =
terminalValuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
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;
} }
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; return result;
@ -314,24 +291,24 @@ export class SelectorMatcher {
/** @internal */ /** @internal */
_matchTerminal( _matchTerminal(
map: Map<string, SelectorContext[]>, name: string, cssSelector: CssSelector, map: {[k: string]: SelectorContext[]}, name: string, cssSelector: CssSelector,
matchedCallback: (c: CssSelector, a: any) => void): boolean { matchedCallback: (c: CssSelector, a: any) => void): boolean {
if (isBlank(map) || isBlank(name)) { if (!map || typeof name !== 'string') {
return false; return false;
} }
var selectables = map.get(name); let selectables = map[name];
var starSelectables = map.get('*'); const starSelectables = map['*'];
if (isPresent(starSelectables)) { if (starSelectables) {
selectables = selectables.concat(starSelectables); selectables = selectables.concat(starSelectables);
} }
if (isBlank(selectables)) { if (!selectables) {
return false; return false;
} }
var selectable: SelectorContext; let selectable: SelectorContext;
var result = false; let result = false;
for (var index = 0; index < selectables.length; index++) { for (let i = 0; i < selectables.length; i++) {
selectable = selectables[index]; selectable = selectables[i];
result = selectable.finalize(cssSelector, matchedCallback) || result; result = selectable.finalize(cssSelector, matchedCallback) || result;
} }
return result; return result;
@ -339,13 +316,14 @@ export class SelectorMatcher {
/** @internal */ /** @internal */
_matchPartial( _matchPartial(
map: Map<string, SelectorMatcher>, name: string, cssSelector: CssSelector, map: {[k: string]: SelectorMatcher}, name: string, cssSelector: CssSelector,
matchedCallback: (c: CssSelector, a: any) => void): boolean { matchedCallback: (c: CssSelector, a: any) => void): boolean {
if (isBlank(map) || isBlank(name)) { if (!map || typeof name !== 'string') {
return false; return false;
} }
var nestedSelector = map.get(name);
if (isBlank(nestedSelector)) { const nestedSelector = map[name];
if (!nestedSelector) {
return false; return false;
} }
// TODO(perf): get rid of recursion and measure again // 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 { finalize(cssSelector: CssSelector, callback: (c: CssSelector, a: any) => void): boolean {
var result = true; let result = true;
if (this.notSelectors.length > 0 && if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) { const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
var notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
result = !notMatcher.match(cssSelector, null); result = !notMatcher.match(cssSelector, null);
} }
if (result && isPresent(callback) && if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) { if (this.listContext) {
if (isPresent(this.listContext)) {
this.listContext.alreadyMatched = true; this.listContext.alreadyMatched = true;
} }
callback(this.selector, this.cbContext); callback(this.selector, this.cbContext);

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license * 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. * This file is a port of shadowCSS from webcomponents.js to TypeScript.
* *
@ -175,9 +173,8 @@ export class ShadowCss {
**/ **/
private _insertPolyfillDirectivesInCssText(cssText: string): string { private _insertPolyfillDirectivesInCssText(cssText: string): string {
// Difference with webcomponents.js: does not handle comments // Difference with webcomponents.js: does not handle comments
return StringWrapper.replaceAllMapped( return cssText.replace(
cssText, _cssContentNextSelectorRe, _cssContentNextSelectorRe, function(...m: string[]) { return m[2] + '{'; });
function(m: any /** TODO #9100 */) { return m[1] + '{'; });
} }
/* /*
@ -197,12 +194,9 @@ export class ShadowCss {
**/ **/
private _insertPolyfillRulesInCssText(cssText: string): string { private _insertPolyfillRulesInCssText(cssText: string): string {
// Difference with webcomponents.js: does not handle comments // Difference with webcomponents.js: does not handle comments
return StringWrapper.replaceAllMapped( return cssText.replace(_cssContentRuleRe, (...m: string[]) => {
cssText, _cssContentRuleRe, function(m: any /** TODO #9100 */) { const rule = m[0].replace(m[1], '').replace(m[2], '');
let rule = m[0]; return m[4] + rule;
rule = StringWrapper.replace(rule, m[1], '');
rule = StringWrapper.replace(rule, m[2], '');
return m[3] + rule;
}); });
} }
@ -215,15 +209,16 @@ export class ShadowCss {
* scopeName .foo { ... } * scopeName .foo { ... }
*/ */
private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string { 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._insertPolyfillHostInCssText(cssText);
cssText = this._convertColonHost(cssText); cssText = this._convertColonHost(cssText);
cssText = this._convertColonHostContext(cssText); cssText = this._convertColonHostContext(cssText);
cssText = this._convertShadowDOMSelectors(cssText); cssText = this._convertShadowDOMSelectors(cssText);
if (isPresent(scopeSelector)) { if (scopeSelector) {
cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector); cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
} }
cssText = cssText + '\n' + unscoped; cssText = cssText + '\n' + unscopedRules;
return cssText.trim(); return cssText.trim();
} }
@ -248,9 +243,7 @@ export class ShadowCss {
let m: RegExpExecArray; let m: RegExpExecArray;
_cssContentUnscopedRuleRe.lastIndex = 0; _cssContentUnscopedRuleRe.lastIndex = 0;
while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) { while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
let rule = m[0]; const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
rule = StringWrapper.replace(rule, m[2], '');
rule = StringWrapper.replace(rule, m[1], m[3]);
r += rule + '\n\n'; r += rule + '\n\n';
} }
return r; return r;
@ -261,7 +254,7 @@ export class ShadowCss {
* *
* to * to
* *
* scopeName.foo > .bar * .foo<scopeName> > .bar
*/ */
private _convertColonHost(cssText: string): string { private _convertColonHost(cssText: string): string {
return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer); return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer);
@ -272,7 +265,7 @@ export class ShadowCss {
* *
* to * to
* *
* scopeName.foo > .bar, .foo scopeName > .bar { } * .foo<scopeName> > .bar, .foo scopeName > .bar { }
* *
* and * and
* *
@ -280,7 +273,7 @@ export class ShadowCss {
* *
* to * to
* *
* scopeName.foo .bar { ... } * .foo<scopeName> .bar { ... }
*/ */
private _convertColonHostContext(cssText: string): string { private _convertColonHostContext(cssText: string): string {
return this._convertColonRule( return this._convertColonRule(
@ -288,14 +281,14 @@ export class ShadowCss {
} }
private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string { private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string {
// p1 = :host, p2 = contents of (), p3 rest of rule // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule
return StringWrapper.replaceAllMapped(cssText, regExp, function(m: any /** TODO #9100 */) { return cssText.replace(regExp, function(...m: string[]) {
if (isPresent(m[2])) { if (m[2]) {
const parts = m[2].split(','), r: any[] /** TODO #9100 */ = []; const parts = m[2].split(',');
const r: string[] = [];
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
let p = parts[i]; let p = parts[i].trim();
if (isBlank(p)) break; if (!p) break;
p = p.trim();
r.push(partReplacer(_polyfillHostNoCombinator, p, m[3])); r.push(partReplacer(_polyfillHostNoCombinator, p, m[3]));
} }
return r.join(','); return r.join(',');
@ -306,7 +299,7 @@ export class ShadowCss {
} }
private _colonHostContextPartReplacer(host: string, part: string, suffix: string): string { 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); return this._colonHostPartReplacer(host, part, suffix);
} else { } else {
return host + part + suffix + ', ' + part + ' ' + host + suffix; return host + part + suffix + ', ' + part + ' ' + host + suffix;
@ -314,7 +307,7 @@ export class ShadowCss {
} }
private _colonHostPartReplacer(host: string, part: string, suffix: string): string { 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. * by replacing with space.
*/ */
private _convertShadowDOMSelectors(cssText: string): string { private _convertShadowDOMSelectors(cssText: string): string {
return _shadowDOMSelectorsRe.reduce( return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
(result, pattern) => { return StringWrapper.replaceAll(result, pattern, ' '); }, cssText);
} }
// change a selector like 'div' to 'name div' // change a selector like 'div' to 'name div'
@ -331,10 +323,12 @@ export class ShadowCss {
return processRules(cssText, (rule: CssRule) => { return processRules(cssText, (rule: CssRule) => {
let selector = rule.selector; let selector = rule.selector;
let content = rule.content; let content = rule.content;
if (rule.selector[0] != '@' || rule.selector.startsWith('@page')) { if (rule.selector[0] != '@') {
selector = selector =
this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling); 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); content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
} }
return new CssRule(selector, content); return new CssRule(selector, content);
@ -369,8 +363,7 @@ export class ShadowCss {
private _makeScopeMatcher(scopeSelector: string): RegExp { private _makeScopeMatcher(scopeSelector: string): RegExp {
const lre = /\[/g; const lre = /\[/g;
const rre = /\]/g; const rre = /\]/g;
scopeSelector = StringWrapper.replaceAll(scopeSelector, lre, '\\['); scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
scopeSelector = StringWrapper.replaceAll(scopeSelector, rre, '\\]');
return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm'); return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
} }
@ -385,13 +378,14 @@ export class ShadowCss {
string { string {
// In Android browser, the lastIndex is not reset when the regex is used in String.replace() // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
_polyfillHostRe.lastIndex = 0; _polyfillHostRe.lastIndex = 0;
if (_polyfillHostRe.test(selector)) { if (_polyfillHostRe.test(selector)) {
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector; const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
selector = StringWrapper.replace(selector, _polyfillHostNoCombinator, replaceBy); return selector.replace(_polyfillHostNoCombinatorRe, (hnc, selector) => selector + replaceBy)
return StringWrapper.replaceAll(selector, _polyfillHostRe, replaceBy + ' '); .replace(_polyfillHostRe, replaceBy + ' ');
} else {
return scopeSelector + ' ' + selector;
} }
return scopeSelector + ' ' + selector;
} }
// return a selector with [name] suffix on each simple selector // return a selector with [name] suffix on each simple selector
@ -404,9 +398,9 @@ export class ShadowCss {
const attrName = '[' + scopeSelector + ']'; const attrName = '[' + scopeSelector + ']';
const _scopeSelectorPart = (p: string) => { const _scopeSelectorPart = (p: string) => {
var scopedP = p.trim(); let scopedP = p.trim();
if (scopedP.length == 0) { if (!scopedP) {
return ''; return '';
} }
@ -426,12 +420,23 @@ export class ShadowCss {
return scopedP; return scopedP;
}; };
const sep = /( |>|\+|~(?!=))\s*/g; let attrSelectorIndex = 0;
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator); 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 startIndex = 0;
let res: RegExpExecArray; let res: RegExpExecArray;
const sep = /( |>|\+|~(?!=))\s*/g;
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
while ((res = sep.exec(selector)) !== null) { while ((res = sep.exec(selector)) !== null) {
const separator = res[1]; 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 // 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 // matches on ancestor elements and not on elements in the host's shadow
const scopedPart = startIndex >= scopeAfter ? _scopeSelectorPart(part) : part; const scopedPart = startIndex >= scopeAfter ? _scopeSelectorPart(part) : part;
scoped += `${scopedPart} ${separator} `; scopedSelector += `${scopedPart} ${separator} `;
startIndex = sep.lastIndex; 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 { private _insertPolyfillHostInCssText(selector: string): string {
@ -451,10 +460,10 @@ export class ShadowCss {
} }
} }
const _cssContentNextSelectorRe = const _cssContentNextSelectorRe =
/polyfill-next-selector[^}]*content:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim; /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim; const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
const _cssContentUnscopedRuleRe = const _cssContentUnscopedRuleRe =
/(polyfill-unscoped-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim; /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
const _polyfillHost = '-shadowcsshost'; const _polyfillHost = '-shadowcsshost';
// note: :host-context pre-processed to -shadowcsshostcontext. // note: :host-context pre-processed to -shadowcsshostcontext.
const _polyfillHostContext = '-shadowcsscontext'; const _polyfillHostContext = '-shadowcsscontext';
@ -464,6 +473,7 @@ const _parenSuffix = ')(?:\\((' +
const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim'); const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim');
const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim'); const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim');
const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator'; const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
const _shadowDOMSelectorsRe = [ const _shadowDOMSelectorsRe = [
/::shadow/g, /::shadow/g,
/::content/g, /::content/g,
@ -480,7 +490,7 @@ const _colonHostContextRe = /:host-context/gim;
const _commentRe = /\/\*\s*[\s\S]*?\*\//g; const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
function stripComments(input: string): string { function stripComments(input: string): string {
return StringWrapper.replaceAllMapped(input, _commentRe, (_: any /** TODO #9100 */) => ''); return input.replace(_commentRe, '');
} }
// all comments except inline source mapping // all comments except inline source mapping
@ -501,18 +511,17 @@ export class CssRule {
constructor(public selector: string, public content: string) {} 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); const inputWithEscapedBlocks = escapeBlocks(input);
let nextBlockIndex = 0; let nextBlockIndex = 0;
return StringWrapper.replaceAllMapped( return inputWithEscapedBlocks.escapedString.replace(_ruleRe, function(...m: string[]) {
inputWithEscapedBlocks.escapedString, _ruleRe, function(m: any /** TODO #9100 */) {
const selector = m[2]; const selector = m[2];
let content = ''; let content = '';
let suffix = m[4]; let suffix = m[4];
let contentPrefix = ''; let contentPrefix = '';
if (isPresent(m[4]) && m[4].startsWith('{' + BLOCK_PLACEHOLDER)) { if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
suffix = m[4].substring(BLOCK_PLACEHOLDER.length + 1); suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
contentPrefix = '{'; contentPrefix = '{';
} }
const rule = ruleCallback(new CssRule(selector, content)); const rule = ruleCallback(new CssRule(selector, content));
@ -525,11 +534,11 @@ class StringWithEscapedBlocks {
} }
function escapeBlocks(input: string): StringWithEscapedBlocks { function escapeBlocks(input: string): StringWithEscapedBlocks {
const inputParts = StringWrapper.split(input, _curlyRe); const inputParts = input.split(_curlyRe);
const resultParts: any[] /** TODO #9100 */ = []; const resultParts: string[] = [];
const escapedBlocks: any[] /** TODO #9100 */ = []; const escapedBlocks: string[] = [];
let bracketCount = 0; let bracketCount = 0;
let currentBlockParts: any[] /** TODO #9100 */ = []; let currentBlockParts: string[] = [];
for (let partIndex = 0; partIndex < inputParts.length; partIndex++) { for (let partIndex = 0; partIndex < inputParts.length; partIndex++) {
const part = inputParts[partIndex]; const part = inputParts[partIndex];
if (part == CLOSE_CURLY) { if (part == CLOSE_CURLY) {

View File

@ -12,11 +12,8 @@ import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata}
import {AST} from '../expression_parser/ast'; import {AST} from '../expression_parser/ast';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util'; import {ParseSourceSpan} from '../parse_util';
import {LifecycleHooks} from '../private_import_core'; import {LifecycleHooks} from '../private_import_core';
/** /**
* An Abstract Syntax Tree node representing part of a parsed Angular template. * 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 { export class BoundElementPropertyAst implements TemplateAst {
constructor( constructor(
@ -71,14 +69,16 @@ export class BoundElementPropertyAst implements TemplateAst {
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElementProperty(this, context); 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 { export class BoundEventAst implements TemplateAst {
constructor( 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) {} public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitEvent(this, context); return visitor.visitEvent(this, context);
@ -90,6 +90,7 @@ export class BoundEventAst implements TemplateAst {
return this.name; return this.name;
} }
} }
get isAnimation(): boolean { return !!this.phase; }
} }
/** /**

View File

@ -8,11 +8,10 @@
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core'; 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 {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser'; import {Parser} from '../expression_parser/parser';
import {StringMapWrapper} from '../facade/collection'; import {isPresent, isString} from '../facade/lang';
import {isBlank, isPresent, isString} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser'; import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers'; import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
import * as html from '../ml_parser/ast'; 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 {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector'; import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver'; 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 {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'; import {PreparsedElementType, preparseElement} from './template_preparser';
// Group 1 = "bind-" // Group 1 = "bind-"
// Group 2 = "let-" // Group 2 = "let-"
// Group 3 = "ref-/#" // Group 3 = "ref-/#"
@ -142,7 +142,6 @@ export class TemplateParser {
const parseVisitor = new TemplateParseVisitor( const parseVisitor = new TemplateParseVisitor(
providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser, providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser,
this._schemaRegistry); this._schemaRegistry);
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
errors.push(...parseVisitor.errors, ...providerViewContext.errors); errors.push(...parseVisitor.errors, ...providerViewContext.errors);
} else { } else {
@ -444,6 +443,15 @@ class TemplateParseVisitor implements html.Visitor {
providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children, providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); 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) { if (hasInlineTemplates) {
@ -469,9 +477,36 @@ class TemplateParseVisitor implements html.Visitor {
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex, templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
element.sourceSpan); element.sourceSpan);
} }
return parsedElement; 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( private _parseInlineTemplateBinding(
attr: html.Attribute, targetMatchableAttrs: string[][], attr: html.Attribute, targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean { targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean {
@ -533,7 +568,7 @@ class TemplateParseVisitor implements html.Visitor {
this._parseReference(identifier, value, srcSpan, targetRefs); this._parseReference(identifier, value, srcSpan, targetRefs);
} else if (bindParts[KW_ON_IDX]) { } else if (bindParts[KW_ON_IDX]) {
this._parseEvent( this._parseEventOrAnimationEvent(
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_BINDON_IDX]) { } else if (bindParts[KW_BINDON_IDX]) {
@ -544,7 +579,7 @@ class TemplateParseVisitor implements html.Visitor {
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_AT_IDX]) { } else if (bindParts[KW_AT_IDX]) {
if (name[0] == '@' && isPresent(value) && value.length > 0) { if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) {
this._reportError( this._reportError(
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + `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.`, ` 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); targetAnimationProps);
} else if (bindParts[IDENT_EVENT_IDX]) { } else if (bindParts[IDENT_EVENT_IDX]) {
this._parseEvent( this._parseEventOrAnimationEvent(
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} }
} else { } else {
@ -608,7 +643,7 @@ class TemplateParseVisitor implements html.Visitor {
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[], targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[],
targetAnimationProps: BoundElementPropertyAst[]) { targetAnimationProps: BoundElementPropertyAst[]) {
const animatePropLength = ANIMATE_PROP_PREFIX.length; const animatePropLength = ANIMATE_PROP_PREFIX.length;
var isAnimationProp = name[0] == '@'; var isAnimationProp = _isAnimationLabel(name);
var animationPrefixLength = 1; var animationPrefixLength = 1;
if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) { if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) {
isAnimationProp = true; isAnimationProp = true;
@ -635,6 +670,7 @@ class TemplateParseVisitor implements html.Visitor {
if (!isPresent(expression) || expression.length == 0) { if (!isPresent(expression) || expression.length == 0) {
expression = 'null'; expression = 'null';
} }
const ast = this._parseBinding(expression, sourceSpan); const ast = this._parseBinding(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]); targetMatchableAttrs.push([name, ast.source]);
targetAnimationProps.push(new BoundElementPropertyAst( targetAnimationProps.push(new BoundElementPropertyAst(
@ -662,20 +698,56 @@ class TemplateParseVisitor implements html.Visitor {
private _parseAssignmentEvent( private _parseAssignmentEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan, name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
this._parseEvent( this._parseEventOrAnimationEvent(
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents); `${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( private _parseEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan, name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
// long format: 'target: eventName' // long format: 'target: eventName'
const parts = splitAtColon(name, [null, name]); const [target, eventName] = splitAtColon(name, [null, name]);
const target = parts[0];
const eventName = parts[1];
const ast = this._parseAction(expression, sourceSpan); const ast = this._parseAction(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]); 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, // Don't detect directives for event names for now,
// so don't add the event name to the matchableAttrs // 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, elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan,
targetPropertyAsts: BoundElementPropertyAst[]) { targetPropertyAsts: BoundElementPropertyAst[]) {
if (hostProps) { if (hostProps) {
StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => { Object.keys(hostProps).forEach(propName => {
const expression = hostProps[propName];
if (isString(expression)) { if (isString(expression)) {
const exprAst = this._parseBinding(expression, sourceSpan); const exprAst = this._parseBinding(expression, sourceSpan);
targetPropertyAsts.push( targetPropertyAsts.push(
@ -777,9 +850,10 @@ class TemplateParseVisitor implements html.Visitor {
hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan, hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan,
targetEventAsts: BoundEventAst[]) { targetEventAsts: BoundEventAst[]) {
if (hostListeners) { if (hostListeners) {
StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => { Object.keys(hostListeners).forEach(propName => {
const expression = hostListeners[propName];
if (isString(expression)) { if (isString(expression)) {
this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts); this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts);
} else { } else {
this._reportError( this._reportError(
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, `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>(); const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
boundProps.forEach(boundProp => { boundProps.forEach(boundProp => {
const prevValue = boundPropsByName.get(boundProp.name); 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 // give [a]="b" a higher precedence than a="b" on the same element
boundPropsByName.set(boundProp.name, boundProp); 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); const boundProp = boundPropsByName.get(elProp);
// Bindings are optional, so this binding only needs to be set up if an expression is given. // 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) => { props.forEach((prop: BoundElementOrDirectiveProperty) => {
if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) { if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
boundElementProps.push(this._createElementPropertyAst( boundElementProps.push(this._createElementPropertyAst(
elementName, prop.name, prop.expression, prop.sourceSpan)); elementName, prop.name, prop.expression, prop.sourceSpan));
} }
@ -846,7 +921,7 @@ class TemplateParseVisitor implements html.Visitor {
if (parts.length === 1) { if (parts.length === 1) {
var partValue = parts[0]; var partValue = parts[0];
if (partValue[0] == '@') { if (_isAnimationLabel(partValue)) {
boundPropertyName = partValue.substr(1); boundPropertyName = partValue.substr(1);
bindingType = PropertyBindingType.Animation; bindingType = PropertyBindingType.Animation;
securityContext = SecurityContext.NONE; securityContext = SecurityContext.NONE;
@ -854,7 +929,7 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue); boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName); securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
bindingType = PropertyBindingType.Property; bindingType = PropertyBindingType.Property;
this._assertNoEventBinding(boundPropertyName, sourceSpan); this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false);
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) { if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
let errorMsg = let errorMsg =
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`; `Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
@ -869,7 +944,7 @@ class TemplateParseVisitor implements html.Visitor {
} else { } else {
if (parts[0] == ATTRIBUTE_PREFIX) { if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1]; 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. // NB: For security purposes, use the mapped property name, not the attribute name.
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName); const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName); securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
@ -902,24 +977,29 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan); boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
} }
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void {
if (propName.toLowerCase().startsWith('on')) { /**
this._reportError( * @param propName the name of the property / attribute
`Binding to event attribute '${propName}' is disallowed ` + * @param sourceSpan
`for security reasons, please use (${propName.slice(2)})=...`, * @param isAttr true when binding to an attribute
sourceSpan, ParseErrorLevel.FATAL); * @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 _findComponentDirectiveNames(directives: DirectiveAst[]): string[] { private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
const componentTypeNames: string[] = []; return directives.filter(directive => directive.directive.isComponent);
directives.forEach(directive => {
const typeName = directive.directive.type.name;
if (directive.directive.isComponent) {
componentTypeNames.push(typeName);
} }
});
return componentTypeNames; private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
return this._findComponentDirectives(directives)
.map(directive => directive.directive.type.name);
} }
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
@ -969,7 +1049,8 @@ class TemplateParseVisitor implements html.Visitor {
const allDirectiveEvents = new Set<string>(); const allDirectiveEvents = new Set<string>();
directives.forEach(directive => { 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); allDirectiveEvents.add(eventName);
}); });
}); });
@ -1103,3 +1184,7 @@ export class PipeCollector extends RecursiveAstVisitor {
return null; return null;
} }
} }
function _isAnimationLabel(name: string): boolean {
return name[0] == '@';
}

View File

@ -7,7 +7,6 @@
*/ */
import {CompileTokenMetadata} from './compile_metadata'; import {CompileTokenMetadata} from './compile_metadata';
import {StringMapWrapper} from './facade/collection';
import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang'; import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
@ -17,13 +16,21 @@ var CAMEL_CASE_REGEXP = /([A-Z])/g;
export function camelCaseToDashCase(input: string): string { export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped( 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[] { export function splitAtColon(input: string, defaultValues: string[]): string[] {
const colonIndex = input.indexOf(':'); return _splitAt(input, ':', defaultValues);
if (colonIndex == -1) return defaultValues; }
return [input.slice(0, colonIndex).trim(), input.slice(colonIndex + 1).trim()];
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 { export function sanitizeIdentifier(name: string): string {
@ -55,9 +62,8 @@ export class ValueTransformer implements ValueVisitor {
} }
visitStringMap(map: {[key: string]: any}, context: any): any { visitStringMap(map: {[key: string]: any}, context: any): any {
var result = {}; var result = {};
StringMapWrapper.forEach(map, (value: any /** TODO #9100 */, key: any /** TODO #9100 */) => { Object.keys(map).forEach(
(result as any /** TODO #9100 */)[key] = visitValue(value, this, context); key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); });
});
return result; return result;
} }
visitPrimitive(value: any, context: any): any { return value; } visitPrimitive(value: any, context: any): any { return value; }

View File

@ -8,8 +8,8 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
import {ListWrapper, MapWrapper, StringMapWrapper} from '../facade/collection'; import {ListWrapper, MapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util'; import {convertValueToOutputAst} from '../output/value_util';
@ -27,7 +27,7 @@ export class CompileNode {
public parent: CompileElement, public view: CompileView, public nodeIndex: number, public parent: CompileElement, public view: CompileView, public nodeIndex: number,
public renderNode: o.Expression, public sourceAst: TemplateAst) {} 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; } isRootElement(): boolean { return this.view != this.parent.view; }
} }
@ -117,7 +117,7 @@ export class CompileElement extends CompileNode {
setComponentView(compViewExpr: o.Expression) { setComponentView(compViewExpr: o.Expression) {
this._compViewExpr = compViewExpr; this._compViewExpr = compViewExpr;
this.contentNodesByNgContentIndex = 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++) { for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
this.contentNodesByNgContentIndex[i] = []; this.contentNodesByNgContentIndex[i] = [];
} }
@ -192,7 +192,7 @@ export class CompileElement extends CompileNode {
queriesWithReads, queriesWithReads,
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token))); 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 token = this.referenceTokens[varName];
var varValue: o.Expression; var varValue: o.Expression;
if (isPresent(token)) { if (isPresent(token)) {
@ -313,12 +313,12 @@ export class CompileElement extends CompileNode {
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression { requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
var result: o.Expression = null; var result: o.Expression = null;
// constructor content query // constructor content query
if (isBlank(result) && isPresent(dep.query)) { if (!result && isPresent(dep.query)) {
result = this._addQuery(dep.query, null).queryList; result = this._addQuery(dep.query, null).queryList;
} }
// constructor view query // constructor view query
if (isBlank(result) && isPresent(dep.viewQuery)) { if (!result && isPresent(dep.viewQuery)) {
result = createQueryList( result = createQueryList(
dep.viewQuery, null, dep.viewQuery, null,
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`, `_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
@ -328,7 +328,7 @@ export class CompileElement extends CompileNode {
if (isPresent(dep.token)) { if (isPresent(dep.token)) {
// access builtins with special visibility // access builtins with special visibility
if (isBlank(result)) { if (!result) {
if (dep.token.reference === if (dep.token.reference ===
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) { resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
if (requestingProviderType === ProviderAstType.Component) { if (requestingProviderType === ProviderAstType.Component) {
@ -339,7 +339,7 @@ export class CompileElement extends CompileNode {
} }
} }
// access regular providers on the element // access regular providers on the element
if (isBlank(result)) { if (!result) {
let resolvedProvider = this._resolvedProviders.get(dep.token.reference); let resolvedProvider = this._resolvedProviders.get(dep.token.reference);
// don't allow directives / public services to access private services. // don't allow directives / public services to access private services.
// only components and private services can access private services. // only components and private services can access private services.
@ -361,20 +361,20 @@ export class CompileElement extends CompileNode {
if (dep.isValue) { if (dep.isValue) {
result = o.literal(dep.value); result = o.literal(dep.value);
} }
if (isBlank(result) && !dep.isSkipSelf) { if (!result && !dep.isSkipSelf) {
result = this._getLocalDependency(requestingProviderType, dep); result = this._getLocalDependency(requestingProviderType, dep);
} }
// check parent elements // check parent elements
while (isBlank(result) && !currElement.parent.isNull()) { while (!result && !currElement.parent.isNull()) {
currElement = currElement.parent; currElement = currElement.parent;
result = currElement._getLocalDependency( result = currElement._getLocalDependency(
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token})); ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
} }
if (isBlank(result)) { if (!result) {
result = injectFromViewParentInjector(dep.token, dep.isOptional); result = injectFromViewParentInjector(dep.token, dep.isOptional);
} }
if (isBlank(result)) { if (!result) {
result = o.NULL_EXPR; result = o.NULL_EXPR;
} }
return getPropertyInView(result, this.view, currElement.view); return getPropertyInView(result, this.view, currElement.view);
@ -411,7 +411,7 @@ function createProviderProperty(
resolvedProviderValueExpr = providerValueExpressions[0]; resolvedProviderValueExpr = providerValueExpressions[0];
type = providerValueExpressions[0].type; type = providerValueExpressions[0].type;
} }
if (isBlank(type)) { if (!type) {
type = o.DYNAMIC_TYPE; type = o.DYNAMIC_TYPE;
} }
if (isEager) { if (isEager) {

View File

@ -8,8 +8,7 @@
import {CompilePipeMetadata} from '../compile_metadata'; import {CompilePipeMetadata} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
@ -23,7 +22,7 @@ export class CompilePipe {
if (meta.pure) { if (meta.pure) {
// pure pipes live on the component view // pure pipes live on the component view
pipe = compView.purePipes.get(name); pipe = compView.purePipes.get(name);
if (isBlank(pipe)) { if (!pipe) {
pipe = new CompilePipe(compView, meta); pipe = new CompilePipe(compView, meta);
compView.purePipes.set(name, pipe); compView.purePipes.set(name, pipe);
compView.pipes.push(pipe); compView.pipes.push(pipe);
@ -85,7 +84,7 @@ function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata {
break; break;
} }
} }
if (isBlank(pipeMeta)) { if (!pipeMeta) {
throw new Error( throw new Error(
`Illegal state: Could not find pipe ${name} although the parser should have detected this error!`); `Illegal state: Could not find pipe ${name} although the parser should have detected this error!`);
} }

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license * 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 {ListWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
@ -101,9 +101,8 @@ function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
function mapNestedViews( function mapNestedViews(
declarationAppElement: o.Expression, view: CompileView, declarationAppElement: o.Expression, view: CompileView,
expressions: o.Expression[]): o.Expression { expressions: o.Expression[]): o.Expression {
var adjustedExpressions: o.Expression[] = expressions.map((expr) => { var adjustedExpressions: o.Expression[] = expressions.map(
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr); (expr) => o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr));
});
return declarationAppElement.callMethod('mapNestedViews', [ return declarationAppElement.callMethod('mapNestedViews', [
o.variable(view.className), o.variable(view.className),
o.fn( o.fn(
@ -129,7 +128,7 @@ export function createQueryList(
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) { export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
query.meta.selectors.forEach((selector) => { query.meta.selectors.forEach((selector) => {
var entry = map.get(selector.reference); var entry = map.get(selector.reference);
if (isBlank(entry)) { if (!entry) {
entry = []; entry = [];
map.set(selector.reference, entry); map.set(selector.reference, entry);
} }

View File

@ -6,15 +6,14 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompiledAnimationTriggerResult} from '../animation/animation_compiler'; import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {ListWrapper, MapWrapper} from '../facade/collection'; import {ListWrapper, MapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core'; import {ViewType} from '../private_import_core';
import {createDiTokenExpression} from '../util';
import {CompileBinding} from './compile_binding'; import {CompileBinding} from './compile_binding';
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
@ -23,7 +22,7 @@ import {CompilePipe} from './compile_pipe';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {EventHandlerVars} from './constants'; import {EventHandlerVars} from './constants';
import {NameResolver} from './expression_converter'; import {NameResolver} from './expression_converter';
import {createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewParentInjector} from './util'; import {createPureProxy, getPropertyInView, getViewFactoryName} from './util';
export class CompileView implements NameResolver { export class CompileView implements NameResolver {
public viewType: ViewType; public viewType: ViewType;
@ -72,7 +71,7 @@ export class CompileView implements NameResolver {
constructor( constructor(
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig, public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression, 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[][]) { public declarationElement: CompileElement, public templateVariableBindings: string[][]) {
this.createMethod = new CompileMethod(this); this.createMethod = new CompileMethod(this);
this.animationBindingsMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this);
@ -139,7 +138,7 @@ export class CompileView implements NameResolver {
} }
var currView: CompileView = this; var currView: CompileView = this;
var result = currView.locals.get(name); var result = currView.locals.get(name);
while (isBlank(result) && isPresent(currView.declarationElement.view)) { while (!result && isPresent(currView.declarationElement.view)) {
currView = currView.declarationElement.view; currView = currView.declarationElement.view;
result = currView.locals.get(name); result = currView.locals.get(name);
} }

View File

@ -9,7 +9,6 @@
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
import {isBlank} from '../facade/lang';
import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';

View File

@ -7,11 +7,9 @@
*/ */
import {CompileDirectiveMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata} from '../compile_metadata';
import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {StringWrapper, isPresent} from '../facade/lang';
import {StringWrapper, isBlank, isPresent} from '../facade/lang'; import {identifierToken} from '../identifiers';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {AnimationOutput} from '../private_import_core';
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
import {CompileBinding} from './compile_binding'; import {CompileBinding} from './compile_binding';
@ -20,10 +18,6 @@ import {CompileMethod} from './compile_method';
import {EventHandlerVars, ViewProperties} from './constants'; import {EventHandlerVars, ViewProperties} from './constants';
import {convertCdStatementToIr} from './expression_converter'; import {convertCdStatementToIr} from './expression_converter';
export class CompileElementAnimationOutput {
constructor(public listener: CompileEventListener, public output: AnimationOutput) {}
}
export class CompileEventListener { export class CompileEventListener {
private _method: CompileMethod; private _method: CompileMethod;
private _hasComponentHostListener: boolean = false; private _hasComponentHostListener: boolean = false;
@ -32,13 +26,14 @@ export class CompileEventListener {
private _actionResultExprs: o.Expression[] = []; private _actionResultExprs: o.Expression[] = [];
static getOrCreate( static getOrCreate(
compileElement: CompileElement, eventTarget: string, eventName: string, compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string,
targetEventListeners: CompileEventListener[]): CompileEventListener { targetEventListeners: CompileEventListener[]): CompileEventListener {
var listener = targetEventListeners.find( var listener = targetEventListeners.find(
listener => listener.eventTarget == eventTarget && listener.eventName == eventName); listener => listener.eventTarget == eventTarget && listener.eventName == eventName &&
if (isBlank(listener)) { listener.eventPhase == eventPhase);
if (!listener) {
listener = new CompileEventListener( listener = new CompileEventListener(
compileElement, eventTarget, eventName, targetEventListeners.length); compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length);
targetEventListeners.push(listener); targetEventListeners.push(listener);
} }
return listener; return listener;
@ -48,7 +43,7 @@ export class CompileEventListener {
constructor( constructor(
public compileElement: CompileElement, public eventTarget: string, public eventName: string, public compileElement: CompileElement, public eventTarget: string, public eventName: string,
listenerIndex: number) { public eventPhase: string, listenerIndex: number) {
this._method = new CompileMethod(compileElement.view); this._method = new CompileMethod(compileElement.view);
this._methodName = this._methodName =
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`; `_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
@ -119,7 +114,7 @@ export class CompileEventListener {
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private])); disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
} }
listenToAnimation(output: AnimationOutput) { listenToAnimation() {
var outputListener = o.THIS_EXPR.callMethod( var outputListener = o.THIS_EXPR.callMethod(
'eventHandler', 'eventHandler',
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]); [o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
@ -129,11 +124,8 @@ export class CompileEventListener {
.callMethod( .callMethod(
'registerAnimationOutput', 'registerAnimationOutput',
[ [
this.compileElement.renderNode, this.compileElement.renderNode, o.literal(this.eventName),
o.importExpr(resolveIdentifier(Identifiers.AnimationOutput)).instantiate([ o.literal(this.eventPhase), outputListener
o.literal(output.name), o.literal(output.phase)
]),
outputListener
]) ])
.toStmt(); .toStmt();
this.compileElement.view.createMethod.addStmt(stmt); this.compileElement.view.createMethod.addStmt(stmt);
@ -160,7 +152,7 @@ export function collectEventListeners(
hostEvents.forEach((hostEvent) => { hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate( var listener = CompileEventListener.getOrCreate(
compileElement, hostEvent.target, hostEvent.name, eventListeners); compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
listener.addAction(hostEvent, null, null); listener.addAction(hostEvent, null, null);
}); });
dirs.forEach((directiveAst) => { dirs.forEach((directiveAst) => {
@ -169,7 +161,7 @@ export function collectEventListeners(
directiveAst.hostEvents.forEach((hostEvent) => { directiveAst.hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate( var listener = CompileEventListener.getOrCreate(
compileElement, hostEvent.target, hostEvent.name, eventListeners); compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
listener.addAction(hostEvent, directiveAst.directive, directiveInstance); listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
}); });
}); });
@ -180,9 +172,8 @@ export function collectEventListeners(
export function bindDirectiveOutputs( export function bindDirectiveOutputs(
directiveAst: DirectiveAst, directiveInstance: o.Expression, directiveAst: DirectiveAst, directiveInstance: o.Expression,
eventListeners: CompileEventListener[]) { eventListeners: CompileEventListener[]) {
StringMapWrapper.forEach( Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
directiveAst.directive.outputs, const eventName = directiveAst.directive.outputs[observablePropName];
(eventName: any /** TODO #9100 */, observablePropName: any /** TODO #9100 */) => {
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => { eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
listener.listenToDirective(directiveInstance, observablePropName); listener.listenToDirective(directiveInstance, observablePropName);
}); });
@ -190,11 +181,13 @@ export function bindDirectiveOutputs(
} }
export function bindRenderOutputs(eventListeners: CompileEventListener[]) { export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
eventListeners.forEach(listener => listener.listenToRenderer()); eventListeners.forEach(listener => {
if (listener.eventPhase) {
listener.listenToAnimation();
} else {
listener.listenToRenderer();
} }
});
export function bindAnimationOutputs(eventListeners: CompileElementAnimationOutput[]) {
eventListeners.forEach(entry => { entry.listener.listenToAnimation(entry.output); });
} }
function convertStmtIntoExpression(stmt: o.Statement): o.Expression { function convertStmtIntoExpression(stmt: o.Statement): o.Expression {

Some files were not shown because too many files have changed in this diff Show More