Compare commits

..

16 Commits

Author SHA1 Message Date
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
463 changed files with 6619 additions and 7495 deletions

View File

@ -1,7 +1,3 @@
<!--
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
-->
**I'm submitting a ...** (check one with "x")
```
[ ] bug report => search github for a similar issue or PR before submitting
@ -9,18 +5,14 @@ IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOU
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
```
**Current behavior**
**Current behavior**
<!-- Describe how the bug manifests. -->
**Expected behavior**
<!-- Describe what the behavior would be without the bug. -->
**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).
-->
**Reproduction of the problem**
<!-- If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). -->
**What is the motivation / use case for changing the behavior?**
<!-- Describe the motivation or the concrete use case -->
@ -28,12 +20,12 @@ https://plnkr.co or similar (you can use this template as a starting point: http
**Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... -->
* **Angular version:** 2.0.X
* **Angular version:** 2.0.X
<!-- Check whether this is still an issue in the most recent Angular version -->
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
<!-- All browsers where this could be reproduced -->
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
* **Node (for AoT issues):** `node --version` =

View File

@ -1,54 +1,3 @@
<a name="2.1.0-rc.0"></a>
# [2.1.0-rc.0](https://github.com/angular/angular/compare/2.1.0-beta.0...2.1.0-rc.0) (2016-10-05)
### Features
* **animations:** provide aliases for :enter and :leave transitions ([#11991](https://github.com/angular/angular/issues/11991)) ([e884f48](https://github.com/angular/angular/commit/e884f48))
Note: 2.1.0-rc.0 release also contains all the changes present in the 2.0.2 release.
<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))
* **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)
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/co
* **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)
* **http:** remove url params if provided value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
mmit/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))
* **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)
<a name="2.1.0-beta.0"></a>
# [2.1.0-beta.0](https://github.com/angular/angular/compare/2.0.0...2.1.0-beta.0) (2016-09-23)
### Features
* **router:** add router preloader to optimistically preload routes ([5a84982](https://github.com/angular/angular/commit/5a84982))
### Bug Fixes
* **router:** update the router not to reset router state when updating root component ([#11799](https://github.com/angular/angular/issues/11799)) ([31dce72](https://github.com/angular/angular/commit/31dce72))
Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 release.
<a name="2.0.1"></a>
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
@ -60,7 +9,7 @@ Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 re
* **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)
* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#1645](https://github.com/angular/angular/issues/1645)
* **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))

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.
- CircleCI fails if your code is not formatted properly,
- Travis CI fails if any of the test suites described above fails.
- Travis CI fails if any of the test suite describe above fails.
## Update the public API tests

View File

@ -4,6 +4,7 @@
[![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)
[![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)
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*

View File

@ -1,7 +1,7 @@
# Triage Process and Github Labels for Angular 2
This document describes how the Angular team uses labels and milestones
to triage issues on github. The basic idea of the process is that
to triage issues on github. The basic idea of the new process is that
caretaker only assigns a component and type (bug, feature) label. The
owner of the component than is in full control of how the issues should
be triaged further.
@ -17,9 +17,9 @@ with it.
* `comp: animations`: `@matsko`
* `comp: benchpress`: `@tbosch`
* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts
* `comp: build/ci`: `@IgorMinar` -- All build and CI scripts
* `comp: common`: `@mhevery` -- This includes core components / pipes.
* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very
* `comp: core/compiler`: `@tbosch` -- Because core and compiler are very
intertwined, we will be treating them as one.
* `comp: forms`: `@kara`
* `comp: http`: `@jeffbcross`
@ -29,14 +29,14 @@ with it.
* `comp: testing`: `@juliemr`
* `comp: upgrade`: `@mhevery`
* `comp: web-worker`: `@vicb`
* `comp: zones`: `@mhevery`
* `comp: zone`: `@mhevery`
There are few components which are cross-cutting. They don't have
a clear location in the source tree. We will treat them as a component
even thought no specific source tree is associated with them.
* `comp: docs`: `@naomiblack`
* `comp: packaging`: `@IgorMinar`
* `comp: documentation`: `@naomiblack`
* `comp: packaging`: `@mhevery`
* `comp: performance`: `@tbosch`
* `comp: security`: `@IgorMinar`
@ -53,11 +53,11 @@ What kind of problem is this?
## Caretaker Triage Process
It is the caretaker's responsibility to assign `comp: *` to each new
issue as they come in. The reason why we limit the responsibility of the
caretaker to this one label is that it is likely that without domain
knowledge the caretaker could mislabel issues or lack knowledge of
duplicate issues.
It is the caretaker's responsibility to assign `comp: *` and `type: *`
to each new issue as they come in. The reason why we limit the
responsibility of the caretaker to these two labels is that it is
unlikely that without domain knowledge the caretaker could add any
additional labels of value.
## Component's owner Triage Process
@ -68,37 +68,11 @@ process for their component.
It will be up to the component owner to determine the order in which the
issues within the component will be resolved.
Several owners have adopted the issue categorization based on
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
used by Angular 1. In this system every issue is assigned frequency and
severity based on which the total user pain score is calculated.
Following is the definition of various frequency and severity levels:
1. `freq(score): *` How often does this issue come up? How many developers does this affect?
* low (1) - obscure issue affecting a handful of developers
* moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected
* high (3) - impacts primary usage patterns, affecting most Angular apps
* critical (4) - impacts all Angular apps
1. `severity(score): *` - How bad is the issue?
* inconvenience (1) - causes ugly/boilerplate code in apps
* confusing (2) - unexpected or inconsistent behavior; hard-to-debug
* broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do
* memory leak (4)
* regression (5) - functionality that used to work no longer works in a new release due to an unintentional change
* security issue (6)
These criteria are then used to calculate a "user pain" score as follows:
`pain = severity × frequency`
### Assigning Issues to Milestones
Any issue that is being worked on must have:
* An `Assignee`: The person doing the work.
* An `assignee`: The person doing the work.
* A `Milestone`: When we expect to complete this work.
We aim to only have at most three milestones open at a time:

View File

@ -1,98 +1,242 @@
/**
* @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).
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
// If the target is set to null, then the browser is not run anywhere during CI.
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented
// out in Travis configuration.
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
var CIconfiguration = {
'Chrome': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
// Currently deactivated due to https://github.com/angular/angular/issues/7560
'ChromeBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
'FirefoxBeta': {unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
'ChromeDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Edge': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Android4.1': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.2': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.3': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
};
var customLaunchers = {
'DartiumWithWebPlatform':
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
'SL_SAFARI8': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10', version: '8.0'},
'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'},
'SL_SAFARI10':
{base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'},
'SL_IOS7': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '7.1'},
'SL_IOS8': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'},
'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
'SL_IE9':
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
'DartiumWithWebPlatform': {
base: 'Dartium',
flags: ['--enable-experimental-web-platform-features'] },
'ChromeNoSandbox': {
base: 'Chrome',
flags: ['--no-sandbox'] },
'SL_CHROME': {
base: 'SauceLabs',
browserName: 'chrome',
version: '52'
},
'SL_CHROMEBETA': {
base: 'SauceLabs',
browserName: 'chrome',
version: 'beta'
},
'SL_CHROMEDEV': {
base: 'SauceLabs',
browserName: 'chrome',
version: 'dev'
},
'SL_FIREFOX': {
base: 'SauceLabs',
browserName: 'firefox',
version: '46'
},
'SL_FIREFOXBETA': {
base: 'SauceLabs',
browserName: 'firefox',
version: 'beta'
},
'SL_FIREFOXDEV': {
base: 'SauceLabs',
browserName: 'firefox',
version: 'dev'
},
'SL_SAFARI7': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.9',
version: '7.0'
},
'SL_SAFARI8': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.10',
version: '8.0'
},
'SL_SAFARI9': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.11',
version: '9.0'
},
'SL_SAFARI10': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.12',
version: '10.0'
},
'SL_IOS7': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '7.1'
},
'SL_IOS8': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '8.4'
},
'SL_IOS9': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '9.3'
},
'SL_IOS10': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '10.0'
},
'SL_IE9': {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 2008',
version: '9'
},
'SL_IE10': {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 2012',
version: '10'
},
'SL_IE11':
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
'SL_IE11': {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 8.1',
version: '11'
},
'SL_EDGE': {
base: 'SauceLabs',
browserName: 'MicrosoftEdge',
platform: 'Windows 10',
version: '13.10586'
},
'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'},
'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'},
'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'},
'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'},
'SL_ANDROID4.1': {
base: 'SauceLabs',
browserName: 'android',
platform: 'Linux',
version: '4.1'
},
'SL_ANDROID4.2': {
base: 'SauceLabs',
browserName: 'android',
platform: 'Linux',
version: '4.2'
},
'SL_ANDROID4.3': {
base: 'SauceLabs',
browserName: 'android',
platform: 'Linux',
version: '4.3'
},
'SL_ANDROID4.4': {
base: 'SauceLabs',
browserName: 'android',
platform: 'Linux',
version: '4.4'
},
'SL_ANDROID5': {
base: 'SauceLabs',
browserName: 'android',
platform: 'Linux',
version: '5.1'
},
'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'},
'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'},
'BS_SAFARI9': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'El Capitan'},
'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'},
'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'},
'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'},
'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'},
'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
'BS_IE9':
{base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
'BS_CHROME': {
base: 'BrowserStack',
browser: 'chrome',
os: 'OS X',
os_version: 'Yosemite'
},
'BS_FIREFOX': {
base: 'BrowserStack',
browser: 'firefox',
os: 'Windows',
os_version: '10'
},
'BS_SAFARI7': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Mavericks'
},
'BS_SAFARI8': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Yosemite'
},
'BS_SAFARI9': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'El Capitan'
},
'BS_SAFARI10': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Sierra'
},
'BS_IOS7': {
base: 'BrowserStack',
device: 'iPhone 5S',
os: 'ios',
os_version: '7.0'
},
'BS_IOS8': {
base: 'BrowserStack',
device: 'iPhone 6',
os: 'ios',
os_version: '8.3'
},
'BS_IOS9': {
base: 'BrowserStack',
device: 'iPhone 6S',
os: 'ios',
os_version: '9.1'
},
'BS_IOS10': {
base: 'BrowserStack',
device: 'iPhone SE',
os: 'ios',
os_version: '10.0'
},
'BS_IE9': {
base: 'BrowserStack',
browser: 'ie',
browser_version: '9.0',
os: 'Windows',
os_version: '7'
},
'BS_IE10': {
base: 'BrowserStack',
browser: 'ie',
@ -107,31 +251,54 @@ var customLaunchers = {
os: 'Windows',
os_version: '10'
},
'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
'BS_WINDOWSPHONE':
{base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'},
'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'},
'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'},
'BS_ANDROID4.3':
{base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'},
'BS_ANDROID4.2':
{base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'},
'BS_ANDROID4.1':
{base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.1'}
'BS_EDGE': {
base: 'BrowserStack',
browser: 'edge',
os: 'Windows',
os_version: '10'
},
'BS_WINDOWSPHONE' : {
base: 'BrowserStack',
device: 'Nokia Lumia 930',
os: 'winphone',
os_version: '8.1'
},
'BS_ANDROID5': {
base: 'BrowserStack',
device: 'Google Nexus 5',
os: 'android',
os_version: '5.0'
},
'BS_ANDROID4.4': {
base: 'BrowserStack',
device: 'HTC One M8',
os: 'android',
os_version: '4.4'
},
'BS_ANDROID4.3': {
base: 'BrowserStack',
device: 'Samsung Galaxy S4',
os: 'android',
os_version: '4.3'
},
'BS_ANDROID4.2': {
base: 'BrowserStack',
device: 'Google Nexus 4',
os: 'android',
os_version: '4.2'
},
'BS_ANDROID4.1': {
base: 'BrowserStack',
device: 'Google Nexus 7',
os: 'android',
os_version: '4.1'
}
};
var sauceAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {
return customLaunchers[item].base == 'SauceLabs';
}),
'DESKTOP': [
'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7',
'SL_SAFARI8', 'SL_SAFARI9', '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'
],
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
@ -143,16 +310,9 @@ var sauceAliases = {
};
var browserstackAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {
return customLaunchers[item].base == 'BrowserStack';
}),
'DESKTOP': [
'BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7',
'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'
],
'MOBILE': [
'BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'
],
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'],
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
@ -169,9 +329,11 @@ module.exports = {
function buildConfiguration(type, target, required) {
return Object.keys(CIconfiguration)
.filter((item) => {
var conf = CIconfiguration[item][type];
return conf.required === required && conf.target === target;
})
.map((item) => target + '_' + item.toUpperCase());
.filter((item) => {
var conf = CIconfiguration[item][type];
return conf.required === required && conf.target === target;
})
.map((item) => {
return target + '_' + item.toUpperCase();
});
}

View File

@ -1,11 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
'use strict';
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
@ -21,14 +13,12 @@ const os = require('os');
// clang-format entry points
const srcsToFmt = [
'modules/@angular/**/*.{js,ts}',
'modules/benchmarks/**/*.{js,ts}',
'modules/e2e_util/**/*.{js,ts}',
'modules/playground/**/*.{js,ts}',
'tools/**/*.{js,ts}',
'modules/@angular/**/*.ts',
'modules/benchmarks/**/*.ts',
'modules/e2e_util/**/*.ts',
'modules/playground/**/*.ts',
'tools/**/*.ts',
'!tools/public_api_guard/**/*.d.ts',
'./*.{js,ts}',
'!shims_for_IE.js',
];
// Check source code for formatting errors (clang-format)
@ -36,16 +26,15 @@ gulp.task('format:enforce', () => {
const format = require('gulp-clang-format');
const clangFormat = require('clang-format');
return gulp.src(srcsToFmt).pipe(
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
});
// Format the source code with clang-format (see .clang-format)
gulp.task('format', () => {
const format = require('gulp-clang-format');
const clangFormat = require('clang-format');
return gulp.src(srcsToFmt, {base: '.'})
.pipe(format.format('file', clangFormat))
.pipe(gulp.dest('.'));
return gulp.src(srcsToFmt, { base: '.' }).pipe(
format.format('file', clangFormat)).pipe(gulp.dest('.'));
});
const entrypoints = [
@ -73,18 +62,12 @@ const entrypoints = [
];
const publicApiDir = path.normalize('tools/public_api_guard');
const publicApiArgs = [
'--rootDir',
'dist/packages-dist',
'--stripExportPattern',
'^__',
'--allowModuleIdentifiers',
'jasmine',
'--allowModuleIdentifiers',
'protractor',
'--allowModuleIdentifiers',
'angular',
'--onStabilityMissing',
'error',
'--rootDir', 'dist/packages-dist',
'--stripExportPattern', '^__',
'--allowModuleIdentifiers', 'jasmine',
'--allowModuleIdentifiers', 'protractor',
'--allowModuleIdentifiers', 'angular',
'--onStabilityMissing', 'error',
].concat(entrypoints);
// Build angular
@ -100,17 +83,17 @@ gulp.task('public-api:enforce', (done) => {
const childProcess = require('child_process');
childProcess
.spawn(
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
.on('close', (errorCode) => {
if (errorCode !== 0) {
done(new Error(
'Public API differs from golden file. Please run `gulp public-api:update`.'));
} else {
done();
}
});
.spawn(
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
.on('close', (errorCode) => {
if (errorCode !== 0) {
done(new Error(
'Public API differs from golden file. Please run `gulp public-api:update`.'));
} else {
done();
}
});
});
// Generate the public API golden files
@ -118,24 +101,20 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
const childProcess = require('child_process');
childProcess
.spawn(
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
.on('close', done);
.spawn(
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
.on('close', done);
});
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
// focused tests is found.
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
// tests in our code base.
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the focused tests is found.
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded tests in our code base.
gulp.task('check-tests', function() {
const ddescribeIit = require('gulp-ddescribe-iit');
return gulp
.src([
'modules/**/*.spec.ts',
'modules/**/*_spec.ts',
])
.pipe(ddescribeIit({allowDisabledTests: true}));
return gulp.src([
'modules/**/*.spec.ts',
'modules/**/*_spec.ts',
]).pipe(ddescribeIit({allowDisabledTests: true}));
});
// Check the coding standards and programming errors
@ -144,21 +123,14 @@ gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
// Built-in rules are at
// https://github.com/palantir/tslint#supported-rules
const tslintConfig = require('./tslint.json');
return gulp
.src([
// todo(vicb): add .js files when supported
// see https://github.com/palantir/tslint/pull/1515
'modules/@angular/**/*.ts',
'modules/benchpress/**/*.ts',
'./*.ts',
])
.pipe(tslint({
tslint: require('tslint').default,
configuration: tslintConfig,
rulesDirectory: 'dist/tools/tslint',
formatter: 'prose',
}))
.pipe(tslint.report({emitError: true}));
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
.pipe(tslint({
tslint: require('tslint').default,
configuration: tslintConfig,
rulesDirectory: 'dist/tools/tslint',
formatter: 'prose',
}))
.pipe(tslint.report({emitError: true}));
});
gulp.task('tools:build', (done) => { tsc('tools/', done); });
@ -170,7 +142,7 @@ gulp.task('check-cycle', (done) => {
const dependencyObject = madge(['dist/all/'], {
format: 'cjs',
extensions: ['.js'],
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
});
const circularDependencies = dependencyObject.circular().getArray();
if (circularDependencies.length > 0) {
@ -201,11 +173,11 @@ gulp.task('serve-examples', () => {
const cors = require('cors');
connect.server({
root: `${__dirname}/dist/examples`,
port: 8001,
livereload: false,
open: false,
middleware: (connect, opt) => [cors()],
root: `${__dirname}/dist/examples`,
port: 8001,
livereload: false,
open: false,
middleware: (connect, opt) => [cors()],
});
});
@ -215,13 +187,16 @@ gulp.task('changelog', () => {
const conventionalChangelog = require('gulp-conventional-changelog');
return gulp.src('CHANGELOG.md')
.pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
// Conventional Changelog Context
// We have to manually set version number so it doesn't get prefixed with `v`
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
currentTag: require('./package.json').version
}))
.pipe(gulp.dest('./'));
.pipe(conventionalChangelog({
preset: 'angular',
releaseCount: 1
}, {
// Conventional Changelog Context
// We have to manually set version number so it doesn't get prefixed with `v`
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
currentTag: require('./package.json').version
}))
.pipe(gulp.dest('./'));
});
function tsc(projectPath, done) {
@ -230,7 +205,8 @@ function tsc(projectPath, done) {
childProcess
.spawn(
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
['-p', path.join(__dirname, projectPath)],
{stdio: 'inherit'})
.on('close', done);
}

View File

@ -1,11 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
var browserProvidersConf = require('./browser-providers.conf.js');
var internalAngularReporter = require('./tools/karma/reporter.js');
@ -25,25 +17,24 @@ module.exports = function(config) {
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js',
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js',
'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js',
'shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js',
{pattern: 'dist/all/empty.*', included: false, watched: false}, {
pattern: 'modules/@angular/platform-browser/test/static_assets/**',
included: false,
watched: false
},
{
pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**',
included: false,
watched: false,
}
'node_modules/reflect-metadata/Reflect.js',
'tools/build/file2modulename.js',
'test-main.js',
{pattern: 'dist/all/empty.*', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
],
exclude: [
@ -53,7 +44,7 @@ module.exports = function(config) {
'dist/all/@angular/benchpress/**',
'dist/all/angular1_router.js',
'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/examples/**/e2e_test/**',
'dist/examples/**/e2e_test/**'
],
customLaunchers: browserProvidersConf.customLaunchers,
@ -64,11 +55,11 @@ module.exports = function(config) {
'karma-sauce-launcher',
'karma-chrome-launcher',
'karma-sourcemap-loader',
internalAngularReporter,
internalAngularReporter
],
preprocessors: {
'**/*.js': ['sourcemap'],
'**/*.js': ['sourcemap']
},
reporters: ['internal-angular'],
@ -82,7 +73,7 @@ module.exports = function(config) {
'selenium-version': '2.53.0',
'command-timeout': 600,
'idle-timeout': 600,
'max-duration': 5400,
'max-duration': 5400
}
},
@ -91,21 +82,20 @@ module.exports = function(config) {
startTunnel: false,
retryLimit: 3,
timeout: 600,
pollingTimeout: 10000,
pollingTimeout: 10000
},
browsers: ['Chrome'],
port: 9876,
captureTimeout: 60000,
browserDisconnectTimeout: 60000,
browserDisconnectTolerance: 3,
browserNoActivityTimeout: 60000,
browserDisconnectTimeout : 60000,
browserDisconnectTolerance : 3,
browserNoActivityTimeout : 60000,
});
if (process.env.TRAVIS) {
var buildId =
'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
if (process.env.CI_MODE.startsWith('saucelabs')) {
config.sauceLabs.build = buildId;
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;

View File

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

View File

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

View File

@ -9,6 +9,7 @@
import {Inject, Injectable} from '@angular/core';
import {Options} from '../common_options';
import {StringMapWrapper} from '../facade/collection';
import {isNumber} from '../facade/lang';
import {Metric} from '../metric';
import {WebDriverAdapter} from '../web_driver_adapter';
@ -39,7 +40,7 @@ export class UserMetric extends Metric {
reject = rej;
});
let adapter = this._wdAdapter;
let names = Object.keys(this._userMetrics);
let names = StringMapWrapper.keys(this._userMetrics);
function getAndClearValues() {
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))

View File

@ -7,7 +7,10 @@
*/
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 {Reporter} from '../reporter';
import {SampleDescription} from '../sample_description';

View File

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

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StringMapWrapper} from '../facade/collection';
import {NumberWrapper} from '../facade/lang';
import {MeasureValues} from '../measure_values';
import {Statistic} from '../statistic';
@ -17,7 +17,7 @@ export function formatNum(n: number) {
export function sortedProps(obj: {[key: string]: any}) {
var props: string[] = [];
props.push(...Object.keys(obj));
StringMapWrapper.forEach(obj, (value, prop) => props.push(prop));
props.sort();
return props;
}
@ -30,4 +30,4 @@ export function formatStats(validSamples: MeasureValues[], metricName: string):
// Note: Don't use the unicode character for +- as it might cause
// hickups for consoles...
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
}
}

View File

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

View File

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

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from './common_options';
import {isPresent} from './facade/lang';
import {Date, DateWrapper, isBlank, isPresent} from './facade/lang';
import {MeasureValues} from './measure_values';
import {Metric} from './metric';
import {Reporter} from './reporter';

View File

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

View File

@ -169,7 +169,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
eventCategories: string[], eventName: string, expectedCategories: string[],
expectedName: string = null): boolean {
var hasCategories = expectedCategories.reduce(
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
}

View File

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

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
export function main() {
function createMetric(ids: any[]) {
var m = ReflectiveInjector
.resolveAndCreate([
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }),
MultiMetric.provideWith(ids)
])
.get(MultiMetric);

View File

@ -7,10 +7,11 @@
*/
import {Provider} from '@angular/core';
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
import {isPresent} from '../../src/facade/lang';
import {StringMapWrapper} from '../../src/facade/collection';
import {isBlank, isPresent} from '../../src/facade/lang';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -27,11 +28,11 @@ export function main() {
requestCount?: boolean
} = {}): Metric {
commandLog = [];
if (!perfLogFeatures) {
if (isBlank(perfLogFeatures)) {
perfLogFeatures =
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (!microMetrics) {
if (isBlank(microMetrics)) {
microMetrics = {};
}
var providers: Provider[] = [
@ -67,7 +68,7 @@ export function main() {
function sortedKeys(stringMap: {[key: string]: any}) {
var res: string[] = [];
res.push(...Object.keys(stringMap));
StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); });
res.sort();
return res;
}

View File

@ -7,9 +7,11 @@
*/
import {Provider, ReflectiveInjector} from '@angular/core';
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
import {Injector, Metric, MultiMetric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, UserMetric, WebDriverAdapter, WebDriverExtension} from '../../index';
import {StringMapWrapper} from '../../src/facade/collection';
import {Json, isBlank, isPresent} from '../../src/facade/lang';
export function main() {
var wdAdapter: MockDriverAdapter;
@ -17,11 +19,11 @@ export function main() {
function createMetric(
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
if (!perfLogFeatures) {
if (isBlank(perfLogFeatures)) {
perfLogFeatures =
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (!userMetrics) {
if (isBlank(userMetrics)) {
userMetrics = {};
}
wdAdapter = new MockDriverAdapter();

View File

@ -7,10 +7,10 @@
*/
import {Provider} from '@angular/core';
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
import {isBlank, isPresent} from '../../src/facade/lang';
import {ConsoleReporter, MeasureValues, ReflectiveInjector, Reporter, SampleDescription, SampleState} from '../../index';
import {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang';
export function main() {
describe('console reporter', () => {
@ -25,7 +25,7 @@ export function main() {
metrics?: {[key: string]: any}
}) {
log = [];
if (!descriptions) {
if (isBlank(descriptions)) {
descriptions = [];
}
if (isBlank(sampleId)) {
@ -90,5 +90,5 @@ export function main() {
}
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
return new MeasureValues(runIndex, new Date(time), values);
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
}

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
import {Json, isPresent} from '../../src/facade/lang';
import {DateWrapper, Json, isPresent} from '../../src/facade/lang';
export function main() {
describe('file reporter', () => {
@ -27,7 +27,7 @@ export function main() {
useValue: new SampleDescription(sampleId, descriptions, metrics)
},
{provide: JsonFileReporter.PATH, useValue: path},
{provide: Options.NOW, useValue: () => new Date(1234)}, {
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, {
provide: Options.WRITE_FILE,
useValue: (filename: string, content: string) => {
loggedFile = {'filename': filename, 'content': content};
@ -77,5 +77,5 @@ export function main() {
}
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
return new MeasureValues(runIndex, new Date(time), values);
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
}

View File

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

View File

@ -6,9 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
import {isBlank} from '../src/facade/lang';
export function main() {
describe('runner', () => {
@ -16,7 +17,7 @@ export function main() {
var runner: Runner;
function createRunner(defaultProviders: any[] = null): Runner {
if (!defaultProviders) {
if (isBlank(defaultProviders)) {
defaultProviders = [];
}
runner = new Runner([

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
import {isBlank, isPresent} from '../src/facade/lang';
import {Date, DateWrapper, isBlank, isPresent, stringify} from '../src/facade/lang';
export function main() {
var EMPTY_EXECUTE = () => {};
@ -26,10 +26,10 @@ export function main() {
execute?: any
} = {}) {
var time = 1000;
if (!metric) {
if (isBlank(metric)) {
metric = new MockMetric([]);
}
if (!reporter) {
if (isBlank(reporter)) {
reporter = new MockReporter([]);
}
if (isBlank(driver)) {
@ -39,7 +39,7 @@ export function main() {
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
{provide: Options.NOW, useValue: () => new Date(time++)}
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(time++)}
];
if (isPresent(prepare)) {
providers.push({provide: Options.PREPARE, useValue: prepare});
@ -60,8 +60,8 @@ export function main() {
createSampler({
driver: driver,
validator: createCountingValidator(2),
prepare: () => count++,
execute: () => count++,
prepare: () => { return count++; },
execute: () => { return count++; }
});
sampler.sample().then((_) => {
expect(count).toBe(4);
@ -204,7 +204,7 @@ export function main() {
}
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
return new MeasureValues(runIndex, new Date(time), values);
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
}
function createCountingValidator(
@ -221,7 +221,7 @@ function createCountingValidator(
function createCountingMetric(log: any[] = []) {
var scriptTime = 0;
return new MockMetric(log, () => ({'script': scriptTime++}));
return new MockMetric(log, () => { return {'script': scriptTime++}; });
}
class MockDriverAdapter extends WebDriverAdapter {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Statistic} from '../src/statistic';
export function main() {

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
import {ListWrapper} from '../../src/facade/collection';
import {Date, DateWrapper} from '../../src/facade/lang';
export function main() {
describe('regression slope validator', () => {
@ -61,5 +62,5 @@ export function main() {
}
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
return new MeasureValues(runIndex, new Date(time), values);
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
}

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
import {MeasureValues, ReflectiveInjector, SizeValidator, Validator} from '../../index';
import {ListWrapper} from '../../src/facade/collection';
import {Date, DateWrapper} from '../../src/facade/lang';
export function main() {
describe('size validator', () => {
@ -46,5 +47,5 @@ export function main() {
}
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
return new MeasureValues(runIndex, new Date(time), values);
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
import {StringWrapper, isPresent} from '../src/facade/lang';
@ -17,7 +17,7 @@ export function main() {
try {
res(ReflectiveInjector
.resolveAndCreate([
ids.map((id) => ({provide: id, useValue: new MockExtension(id)})),
ids.map((id) => { return {provide: id, useValue: new MockExtension(id)}; }),
{provide: Options.CAPABILITIES, useValue: caps},
WebDriverExtension.provideFirstSupported(ids)
])

View File

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

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {Json} from '../../src/facade/lang';
import {Json, isBlank, isPresent} from '../../src/facade/lang';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -20,7 +20,7 @@ export function main() {
var normEvents = new TraceEventFactory('timeline', 'pid0');
function createExtension(perfRecords: any[] = null): WebDriverExtension {
if (!perfRecords) {
if (isBlank(perfRecords)) {
perfRecords = [];
}
log = [];
@ -156,7 +156,7 @@ function timeEndRecord(name: string, time: number) {
}
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
if (!children) {
if (isBlank(children)) {
children = [];
}
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};

View File

@ -1,10 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export default {
entry: '../../../dist/packages-dist/common/testing/index.js',
@ -17,4 +10,4 @@ export default {
'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx'
}
};
}

View File

@ -1,10 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export default {
entry: '../../../dist/packages-dist/common/index.js',
@ -14,6 +7,6 @@ export default {
globals: {
'@angular/core': 'ng.core',
'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx',
'rxjs/Subject': 'Rx'
}
};
}

View File

@ -67,7 +67,7 @@ export class NgStyle implements DoCheck {
private _setStyle(nameAndUnit: string, value: string): void {
const [name, unit] = nameAndUnit.split('.');
value = value && unit ? `${value}${unit}` : value;
value = value !== null && value !== void(0) && unit ? `${value}${unit}` : value;
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
}

View File

@ -8,7 +8,7 @@
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('binding to CSS class list', () => {

View File

@ -8,7 +8,7 @@
import {LOCALE_ID} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';

View File

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

View File

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

View File

@ -8,7 +8,7 @@
import {I18nPluralPipe, NgLocalization} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('I18nPluralPipe', () => {

View File

@ -8,7 +8,7 @@
import {I18nSelectPipe} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('I18nSelectPipe', () => {

View File

@ -7,7 +7,7 @@
*/
import {LowerCasePipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('LowerCasePipe', () => {

View File

@ -7,7 +7,7 @@
*/
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
export function main() {

View File

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

View File

@ -7,7 +7,7 @@
*/
import {UpperCasePipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('UpperCasePipe', () => {

View File

@ -7,7 +7,7 @@
*/
import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref';
import {SpyObject} from '@angular/core/testing/testing_internal';
import {SpyObject, proxy} from '@angular/core/testing/testing_internal';
export class SpyChangeDetectorRef extends SpyObject {
constructor() {

View File

@ -36,27 +36,25 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<messagebundle>
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
</messagebundle>
`;
</messagebundle>`;
const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source>
<target/>
<note priority="1" from="description">desc</note>
<note priority="1" from="meaning">meaning</note>
</trans-unit>
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
<source>Welcome</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>
`;
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source>
<target/>
<note priority="1" from="description">desc</note>
<note priority="1" from="meaning">meaning</note>
</trans-unit>
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
<source>Welcome</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>`;
describe('template i18n extraction output', () => {
const outDir = '';

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, 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, Query, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from './private_import_core';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
import * as ts from 'typescript';
import {ReflectorHost} from '../src/reflector_host';

View File

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

View File

@ -1,10 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export default {
entry: '../../../dist/packages-dist/compiler/testing/index.js',
@ -18,4 +11,4 @@ export default {
'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx'
}
};
}

View File

@ -1,10 +1,3 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export default {
entry: '../../../dist/packages-dist/compiler/index.js',
@ -14,9 +7,9 @@ export default {
globals: {
'@angular/core': 'ng.core',
'rxjs/Observable': 'Rx',
'rxjs/Subject': 'Rx',
'rxjs/Subject': 'Rx'
},
plugins: [
// nodeResolve({ jsnext: true, main: true }),
// nodeResolve({ jsnext: true, main: true }),
]
};
}

View File

@ -6,26 +6,75 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isPresent} from '../facade/lang';
import {CompileDirectiveMetadata} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
import {ANY_STATE, AnimationOutput, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
import * as t from '../template_parser/template_ast';
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser';
export class AnimationEntryCompileResult {
constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
const animationCompilationCache =
new Map<CompileDirectiveMetadata, CompiledAnimationTriggerResult[]>();
export class CompiledAnimationTriggerResult {
constructor(
public name: string, public statesMapStatement: o.Statement,
public statesVariableName: string, public fnStatement: o.Statement,
public fnVariable: o.Expression) {}
}
export class CompiledComponentAnimationResult {
constructor(
public outputs: AnimationOutput[], public triggers: CompiledAnimationTriggerResult[]) {}
}
export class AnimationCompiler {
compile(factoryNamePrefix: string, parsedAnimations: AnimationEntryAst[]):
AnimationEntryCompileResult[] {
return parsedAnimations.map(entry => {
const factoryName = `${factoryNamePrefix}_${entry.name}`;
const visitor = new _AnimationBuilder(entry.name, factoryName);
return visitor.build(entry);
compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]):
CompiledComponentAnimationResult {
var compiledAnimations: CompiledAnimationTriggerResult[] = [];
var groupedErrors: string[] = [];
var triggerLookup: {[key: string]: CompiledAnimationTriggerResult} = {};
var componentName = component.type.name;
component.template.animations.forEach(entry => {
var result = parseAnimationEntry(entry);
var triggerName = entry.name;
if (result.errors.length > 0) {
var errorMessage =
`Unable to parse the animation sequence for "${triggerName}" due to the following errors:`;
result.errors.forEach(
(error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; });
groupedErrors.push(errorMessage);
}
if (triggerLookup[triggerName]) {
groupedErrors.push(
`The animation trigger "${triggerName}" has already been registered on "${componentName}"`);
} else {
var factoryName = `${componentName}_${entry.name}`;
var visitor = new _AnimationBuilder(triggerName, factoryName);
var compileResult = visitor.build(result.ast);
compiledAnimations.push(compileResult);
triggerLookup[entry.name] = compileResult;
}
});
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);
}
}
@ -61,7 +110,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
}
ast.styles.forEach(entry => {
stylesArr.push(o.literalMap(Object.keys(entry).map(key => [key, o.literal(entry[key])])));
stylesArr.push(
o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])])));
});
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
@ -132,8 +182,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
visitAnimationStateDeclaration(
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
var flatStyles: {[key: string]: string | number} = {};
_getStylesArray(ast).forEach(
entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
_getStylesArray(ast).forEach(entry => {
StringMapWrapper.forEach(entry, (value: string, key: string) => { flatStyles[key] = value; });
});
context.stateMap.registerState(ast.stateName, flatStyles);
}
@ -283,27 +334,28 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements);
}
build(ast: AnimationAst): AnimationEntryCompileResult {
build(ast: AnimationAst): CompiledAnimationTriggerResult {
var context = new _AnimationBuilderContext();
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
var fnVariable = o.variable(this._fnVarName);
var lookupMap: any[] = [];
Object.keys(context.stateMap.states).forEach(stateName => {
const value = context.stateMap.states[stateName];
var variableValue = EMPTY_MAP;
if (isPresent(value)) {
let styleMap: any[] = [];
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
variableValue = o.literalMap(styleMap);
}
lookupMap.push([stateName, variableValue]);
});
StringMapWrapper.forEach(
context.stateMap.states, (value: {[key: string]: string}, stateName: string) => {
var variableValue = EMPTY_MAP;
if (isPresent(value)) {
let styleMap: any[] = [];
StringMapWrapper.forEach(value, (value: string, key: string) => {
styleMap.push([key, o.literal(value)]);
});
variableValue = o.literalMap(styleMap);
}
lookupMap.push([stateName, variableValue]);
});
const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);
var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
return new CompiledAnimationTriggerResult(
this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable);
}
}
@ -319,7 +371,7 @@ class _AnimationBuilderStateMap {
get states() { return this._states; }
registerState(name: string, value: {[prop: string]: string | number} = null): void {
var existingEntry = this._states[name];
if (!existingEntry) {
if (isBlank(existingEntry)) {
this._states[name] = value;
}
}
@ -345,7 +397,7 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
var styles1 = _getStylesArray(step.keyframes[0])[0];
var styles2 = _getStylesArray(step.keyframes[1])[0];
return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0;
return StringMapWrapper.isEmpty(styles1) && StringMapWrapper.isEmpty(styles2);
}
return false;
}
@ -353,3 +405,99 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
function _getStylesArray(obj: any): {[key: string]: any}[] {
return obj.styles.styles;
}
function _validateAnimationProperties(
compiledAnimations: CompiledAnimationTriggerResult[],
template: t.TemplateAst[]): AnimationPropertyValidationOutput {
var visitor = new _AnimationTemplatePropertyVisitor(compiledAnimations);
t.templateVisitAll(visitor, template);
return new AnimationPropertyValidationOutput(visitor.outputs, visitor.errors);
}
export class AnimationPropertyValidationOutput {
constructor(public outputs: AnimationOutput[], public errors: AnimationParseError[]) {}
}
class _AnimationTemplatePropertyVisitor implements t.TemplateAstVisitor {
private _animationRegistry: {[key: string]: boolean};
public errors: AnimationParseError[] = [];
public outputs: AnimationOutput[] = [];
constructor(animations: CompiledAnimationTriggerResult[]) {
this._animationRegistry = this._buildCompileAnimationLookup(animations);
}
private _buildCompileAnimationLookup(animations: CompiledAnimationTriggerResult[]):
{[key: string]: boolean} {
var map: {[key: string]: boolean} = {};
animations.forEach(entry => { map[entry.name] = true; });
return map;
}
private _validateAnimationInputOutputPairs(
inputAsts: t.BoundElementPropertyAst[], outputAsts: t.BoundEventAst[],
animationRegistry: {[key: string]: any}, isHostLevel: boolean): void {
var detectedAnimationInputs: {[key: string]: boolean} = {};
inputAsts.forEach(input => {
if (input.type == t.PropertyBindingType.Animation) {
var triggerName = input.name;
if (isPresent(animationRegistry[triggerName])) {
detectedAnimationInputs[triggerName] = true;
} else {
this.errors.push(
new AnimationParseError(`Couldn't find an animation entry for ${triggerName}`));
}
}
});
outputAsts.forEach(output => {
if (output.name[0] == '@') {
var normalizedOutputData = parseAnimationOutputName(output.name.substr(1), this.errors);
let triggerName = normalizedOutputData.name;
let triggerEventPhase = normalizedOutputData.phase;
if (!animationRegistry[triggerName]) {
this.errors.push(new AnimationParseError(
`Couldn't find the corresponding ${isHostLevel ? 'host-level ' : '' }animation trigger definition for (@${triggerName})`));
} else if (!detectedAnimationInputs[triggerName]) {
this.errors.push(new AnimationParseError(
`Unable to listen on (@${triggerName}.${triggerEventPhase}) because the animation trigger [@${triggerName}] isn't being used on the same element`));
} else {
this.outputs.push(normalizedOutputData);
}
}
});
}
visitElement(ast: t.ElementAst, ctx: any): any {
this._validateAnimationInputOutputPairs(
ast.inputs, ast.outputs, this._animationRegistry, false);
var componentOnElement: t.DirectiveAst =
ast.directives.find(directive => directive.directive.isComponent);
if (componentOnElement) {
let cachedComponentAnimations = animationCompilationCache.get(componentOnElement.directive);
if (cachedComponentAnimations) {
this._validateAnimationInputOutputPairs(
componentOnElement.hostProperties, componentOnElement.hostEvents,
this._buildCompileAnimationLookup(cachedComponentAnimations), true);
}
}
t.templateVisitAll(this, ast.children);
}
visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst, ctx: any): any {
t.templateVisitAll(this, ast.children);
}
visitEvent(ast: t.BoundEventAst, ctx: any): any {}
visitBoundText(ast: t.BoundTextAst, ctx: any): any {}
visitText(ast: t.TextAst, ctx: any): any {}
visitNgContent(ast: t.NgContentAst, ctx: any): any {}
visitAttr(ast: t.AttrAst, ctx: any): any {}
visitDirective(ast: t.DirectiveAst, ctx: any): any {}
visitReference(ast: t.ReferenceAst, ctx: any): any {}
visitVariable(ast: t.VariableAst, ctx: any): any {}
visitDirectiveProperty(ast: t.BoundDirectivePropertyAst, ctx: any): any {}
visitElementProperty(ast: t.BoundElementPropertyAst, ctx: any): any {}
}

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata} from '../compile_metadata';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang';
import {Math} from '../facade/math';
import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
import {ANY_STATE, AnimationOutput, FILL_STYLE_FLAG} from '../private_import_core';
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
import {StylesCollection} from './styles_collection';
@ -20,85 +20,73 @@ const _INITIAL_KEYFRAME = 0;
const _TERMINAL_KEYFRAME = 1;
const _ONE_SECOND = 1000;
declare type Styles = {
[key: string]: string | number
};
export class AnimationParseError extends ParseError {
constructor(message: string) { super(null, message); }
constructor(message: any /** TODO #9100 */) { super(null, message); }
toString(): string { return `${this.msg}`; }
}
export class AnimationEntryParseResult {
export class ParsedAnimationResult {
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
}
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;
});
export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult {
var errors: AnimationParseError[] = [];
var stateStyles: {[key: string]: AnimationStylesAst} = {};
var transitions: CompileAnimationStateTransitionMetadata[] = [];
if (errors.length > 0) {
const errorString = errors.join('\n');
throw new Error(`Animation parse errors:\n${errorString}`);
var stateDeclarationAsts: any[] /** TODO #9100 */ = [];
entry.definitions.forEach(def => {
if (def instanceof CompileAnimationStateDeclarationMetadata) {
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
stateDeclarationAsts.push(ast);
stateStyles[ast.stateName] = ast.styles;
});
} else {
transitions.push(<CompileAnimationStateTransitionMetadata>def);
}
});
return asts;
}
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
var errors: AnimationParseError[] = [];
var stateStyles: {[key: string]: AnimationStylesAst} = {};
var transitions: CompileAnimationStateTransitionMetadata[] = [];
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
entry.definitions.forEach(def => {
if (def instanceof CompileAnimationStateDeclarationMetadata) {
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
stateDeclarationAsts.push(ast);
stateStyles[ast.stateName] = ast.styles;
});
} else {
transitions.push(<CompileAnimationStateTransitionMetadata>def);
}
});
var stateTransitionAsts =
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
return new AnimationEntryParseResult(ast, errors);
var stateTransitionAsts =
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
return new ParsedAnimationResult(ast, errors);
}
export function parseAnimationOutputName(
outputName: string, errors: AnimationParseError[]): AnimationOutput {
var values = outputName.split('.');
var name: string;
var phase: string = '';
if (values.length > 1) {
name = values[0];
let parsedPhase = values[1];
switch (parsedPhase) {
case 'start':
case 'done':
phase = parsedPhase;
break;
default:
errors.push(new AnimationParseError(
`The provided animation output phase value "${parsedPhase}" for "@${name}" is not supported (use start or done)`));
}
} else {
name = outputName;
errors.push(new AnimationParseError(
`The animation trigger output event (@${name}) is missing its phase value name (start or done are currently supported)`));
}
return new AnimationOutput(name, phase, outputName);
}
function _parseAnimationDeclarationStates(
stateMetadata: CompileAnimationStateDeclarationMetadata,
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
var styleValues: Styles[] = [];
var styleValues: {[key: string]: string | number}[] = [];
stateMetadata.styles.styles.forEach(stylesEntry => {
// TODO (matsko): change this when we get CSS class integration support
if (isStringMap(stylesEntry)) {
styleValues.push(stylesEntry as Styles);
styleValues.push(<{[key: string]: string | number}>stylesEntry);
} else {
errors.push(new AnimationParseError(
`State based animations cannot contain references to other states`));
@ -136,25 +124,9 @@ function _parseAnimationStateTransition(
return new AnimationStateTransitionAst(transitionExprs, stepsAst);
}
function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): string {
switch (alias) {
case ':enter':
return 'void => *';
case ':leave':
return '* => void';
default:
errors.push(
new AnimationParseError(`the transition alias value "${alias}" is not supported`));
return '* => *';
}
}
function _parseAnimationTransitionExpr(
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
var expressions: AnimationStateTransitionExpression[] = [];
if (eventStr[0] == ':') {
eventStr = _parseAnimationAlias(eventStr, errors);
}
var expressions: any[] /** TODO #9100 */ = [];
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
if (!isPresent(match) || match.length < 4) {
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
@ -173,6 +145,16 @@ function _parseAnimationTransitionExpr(
return expressions;
}
function _fetchSylesFromState(stateName: string, stateStyles: {[key: string]: AnimationStylesAst}):
CompileAnimationStyleMetadata {
var entry = stateStyles[stateName];
if (isPresent(entry)) {
var styles = <{[key: string]: string | number}[]>entry.styles;
return new CompileAnimationStyleMetadata(0, styles);
}
return null;
}
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
CompileAnimationMetadata {
return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) :
@ -228,7 +210,7 @@ function _normalizeStyleStepEntry(
}
var newSteps: CompileAnimationMetadata[] = [];
var combinedStyles: Styles[];
var combinedStyles: {[key: string]: string | number}[];
steps.forEach(step => {
if (step instanceof CompileAnimationStyleMetadata) {
// this occurs when a style step is followed by a previous style step
@ -284,7 +266,7 @@ function _normalizeStyleStepEntry(
function _resolveStylesFromState(
stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
errors: AnimationParseError[]) {
var styles: Styles[] = [];
var styles: {[key: string]: string | number}[] = [];
if (stateName[0] != ':') {
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
} else {
@ -296,7 +278,7 @@ function _resolveStylesFromState(
} else {
value.styles.forEach(stylesEntry => {
if (isStringMap(stylesEntry)) {
styles.push(stylesEntry as Styles);
styles.push(<{[key: string]: string | number}>stylesEntry);
}
});
}
@ -330,13 +312,15 @@ function _parseAnimationKeyframes(
var lastOffset = 0;
keyframeSequence.steps.forEach(styleMetadata => {
var offset = styleMetadata.offset;
var keyframeStyles: Styles = {};
var keyframeStyles: {[key: string]: string | number} = {};
styleMetadata.styles.forEach(entry => {
Object.keys(entry).forEach(prop => {
if (prop != 'offset') {
keyframeStyles[prop] = (entry as Styles)[prop];
}
});
StringMapWrapper.forEach(
<{[key: string]: string | number}>entry,
(value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (prop != 'offset') {
keyframeStyles[prop] = value;
}
});
});
if (isPresent(offset)) {
@ -373,22 +357,24 @@ function _parseAnimationKeyframes(
let entry = rawKeyframes[i];
let styles = entry[1];
Object.keys(styles).forEach(prop => {
if (!isPresent(firstKeyframeStyles[prop])) {
firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
}
});
StringMapWrapper.forEach(
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (!isPresent(firstKeyframeStyles[prop])) {
firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
}
});
}
for (i = limit - 1; i >= 0; i--) {
let entry = rawKeyframes[i];
let styles = entry[1];
Object.keys(styles).forEach(prop => {
if (!isPresent(lastKeyframeStyles[prop])) {
lastKeyframeStyles[prop] = styles[prop];
}
});
StringMapWrapper.forEach(
styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (!isPresent(lastKeyframeStyles[prop])) {
lastKeyframeStyles[prop] = value;
}
});
}
return rawKeyframes.map(
@ -412,9 +398,11 @@ function _parseTransitionAnimation(
if (entry instanceof CompileAnimationStyleMetadata) {
entry.styles.forEach(stylesEntry => {
// by this point we know that we only have stringmap values
var map = stylesEntry as Styles;
Object.keys(map).forEach(
prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
var map = <{[key: string]: string | number}>stylesEntry;
StringMapWrapper.forEach(
map, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
collectedStyles.insertAtTime(prop, time, value);
});
});
previousStyles = entry.styles;
return;
@ -460,7 +448,7 @@ function _parseTransitionAnimation(
} else {
let styleData = <CompileAnimationStyleMetadata>styles;
let offset = _TERMINAL_KEYFRAME;
let styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
let styleAst = new AnimationStylesAst(<{[key: string]: string | number}[]>styleData.styles);
var keyframe = new AnimationKeyframeAst(offset, styleAst);
keyframes = [keyframe];
}
@ -472,8 +460,9 @@ function _parseTransitionAnimation(
keyframes.forEach(
(keyframe: any /** TODO #9100 */) => keyframe.styles.styles.forEach(
(entry: any /** TODO #9100 */) => Object.keys(entry).forEach(
prop => { collectedStyles.insertAtTime(prop, currentTime, entry[prop]); })));
(entry: any /** TODO #9100 */) => StringMapWrapper.forEach(
entry, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) =>
collectedStyles.insertAtTime(prop, currentTime, value))));
} else {
// if the code reaches this stage then an error
// has already been populated within the _normalizeStyleSteps()
@ -546,11 +535,10 @@ function _parseTimeExpression(
function _createStartKeyframeFromEndKeyframe(
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
var values: Styles = {};
var values: {[key: string]: string | number} = {};
var endTime = startTime + duration;
endKeyframe.styles.styles.forEach((styleData: Styles) => {
Object.keys(styleData).forEach(prop => {
const val = styleData[prop];
endKeyframe.styles.styles.forEach((styleData: {[key: string]: string | number}) => {
StringMapWrapper.forEach(styleData, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
if (prop == 'offset') return;
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@ const _PLACEHOLDER_TAG = 'x';
const _SOURCE_TAG = 'source';
const _TARGET_TAG = 'target';
const _UNIT_TAG = 'trans-unit';
const _CR = (ws: number = 0) => new xml.Text(`\n${new Array(ws).join(' ')}`);
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
@ -43,37 +44,34 @@ export class Xliff implements Serializer {
let transUnit = new xml.Tag(_UNIT_TAG, {id: id, datatype: 'html'});
transUnit.children.push(
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
new xml.CR(8), new xml.Tag(_TARGET_TAG));
_CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), _CR(8),
new xml.Tag(_TARGET_TAG));
if (message.description) {
transUnit.children.push(
new xml.CR(8),
_CR(8),
new xml.Tag(
'note', {priority: '1', from: 'description'}, [new xml.Text(message.description)]));
}
if (message.meaning) {
transUnit.children.push(
new xml.CR(8),
_CR(8),
new xml.Tag('note', {priority: '1', from: 'meaning'}, [new xml.Text(message.meaning)]));
}
transUnit.children.push(new xml.CR(6));
transUnit.children.push(_CR(6));
transUnits.push(new xml.CR(6), transUnit);
transUnits.push(_CR(6), transUnit);
});
const body = new xml.Tag('body', {}, [...transUnits, new xml.CR(4)]);
const body = new xml.Tag('body', {}, [...transUnits, _CR(4)]);
const file = new xml.Tag(
'file', {'source-language': _SOURCE_LANG, datatype: 'plaintext', original: 'ng2.template'},
[new xml.CR(4), body, new xml.CR(2)]);
const xliff = new xml.Tag(
'xliff', {version: _VERSION, xmlns: _XMLNS}, [new xml.CR(2), file, new xml.CR()]);
[_CR(4), body, _CR(2)]);
const xliff = new xml.Tag('xliff', {version: _VERSION, xmlns: _XMLNS}, [_CR(2), file, _CR()]);
return xml.serialize([
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), new xml.CR(), xliff, new xml.CR()
]);
return xml.serialize([new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), _CR(), xliff]);
}
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} {
@ -139,15 +137,13 @@ class _WriteVisitor implements i18n.Visitor {
}
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
const ctype = getCtypeForTag(ph.tag);
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype});
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype: ph.tag});
if (ph.isVoid) {
// void tags have no children nor closing tags
return [startTagPh];
}
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype});
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype: ph.tag});
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
}
@ -291,14 +287,3 @@ class _LoadVisitor implements ml.Visitor {
this._errors.push(new I18nError(node.sourceSpan, message));
}
}
function getCtypeForTag(tag: string): string {
switch (tag.toLowerCase()) {
case 'br':
return 'lb';
case 'img':
return 'image';
default:
return `x-${tag}`;
}
}

View File

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

View File

@ -88,10 +88,6 @@ export class Text implements Node {
visit(visitor: IVisitor): any { return visitor.visitText(this); }
}
export class CR extends Text {
constructor(ws: number = 0) { super(`\n${new Array(ws + 1).join(' ')}`); }
}
const _ESCAPED_CHARS: [RegExp, string][] = [
[/&/g, '&amp;'],
[/"/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 {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
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 {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 {assetUrl} from './util';
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
@ -266,6 +266,11 @@ export class Identifiers {
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: TRANSLATIONS_FORMAT_
};
static AnimationOutput: IdentifierSpec = {
name: 'AnimationOutput',
moduleUrl: assetUrl('core', 'animation/animation_output'),
runtime: AnimationOutput
};
}
export function resolveIdentifier(identifier: IdentifierSpec) {

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
*/
import {isBlank, isPresent} from '../facade/lang';
import {StringWrapper, evalExpression, isBlank, isPresent, isString} from '../facade/lang';
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';

View File

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

View File

@ -6,6 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {Math, isBlank, isPresent} from '../facade/lang';
// asset:<package-name>/<realm>/<path-to-module>
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/;

View File

@ -75,22 +75,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
super.visitLiteralExpr(ast, ctx, '(null as any)');
}
// Temporary workaround to support strictNullCheck enabled consumers of ngc emit.
// In SNC mode, [] have the type never[], so we cast here to any[].
// TODO: narrow the cast to a more explicit type, or use a pattern that does not
// start with [].concat. see https://github.com/angular/angular/pull/11846
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
if (ast.entries.length === 0) {
ctx.print('(');
}
const result = super.visitLiteralArrayExpr(ast, ctx);
if (ast.entries.length === 0) {
ctx.print(' as any[])');
}
return result;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;

View File

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

View File

@ -12,6 +12,7 @@ export const isDefaultChangeDetectionStrategy: typeof r.isDefaultChangeDetection
r.isDefaultChangeDetectionStrategy;
export type ChangeDetectorStatus = typeof r._ChangeDetectorStatus;
export const ChangeDetectorStatus: typeof r.ChangeDetectorStatus = r.ChangeDetectorStatus;
r.CHANGE_DETECTION_STRATEGY_VALUES;
export type LifecycleHooks = typeof r._LifecycleHooks;
export const LifecycleHooks: typeof r.LifecycleHooks = r.LifecycleHooks;
export const LIFECYCLE_HOOKS_VALUES: typeof r.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES;
@ -74,6 +75,8 @@ export type AnimationKeyframe = typeof r._AnimationKeyframe;
export const AnimationKeyframe: typeof r.AnimationKeyframe = r.AnimationKeyframe;
export type AnimationStyles = typeof r._AnimationStyles;
export const AnimationStyles: typeof r.AnimationStyles = r.AnimationStyles;
export type AnimationOutput = typeof r._AnimationOutput;
export const AnimationOutput: typeof r.AnimationOutput = r.AnimationOutput;
export const ANY_STATE = r.ANY_STATE;
export const DEFAULT_STATE = r.DEFAULT_STATE;
export const EMPTY_STATE = r.EMPTY_STATE;

View File

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

View File

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

View File

@ -344,26 +344,4 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; }
getDefaultComponentElementName(): string { return 'ng-component'; }
validateProperty(name: string): {error: boolean, msg?: string} {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
`please use (${name.slice(2)})=...` +
`\nIf '${name}' is a directive input, make sure the directive is imported by the` +
` current module.`;
return {error: true, msg: msg};
} else {
return {error: false};
}
}
validateAttribute(name: string): {error: boolean, msg?: string} {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
`please use (${name.slice(2)})=...`;
return {error: true, msg: msg};
} else {
return {error: false};
}
}
}

View File

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

View File

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

View File

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

View File

@ -12,8 +12,11 @@ import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata}
import {AST} from '../expression_parser/ast';
import {isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
import {LifecycleHooks} from '../private_import_core';
/**
* An Abstract Syntax Tree node representing part of a parsed Angular template.
*/
@ -58,8 +61,7 @@ export class AttrAst implements TemplateAst {
}
/**
* A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
* `[@trigger]="stateExp"`)
* A binding for an element property (e.g. `[property]="expression"`).
*/
export class BoundElementPropertyAst implements TemplateAst {
constructor(
@ -69,16 +71,14 @@ export class BoundElementPropertyAst implements TemplateAst {
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElementProperty(this, context);
}
get isAnimation(): boolean { return this.type === PropertyBindingType.Animation; }
}
/**
* A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g.
* `(@trigger.phase)="callback($event)"`).
* A binding for an element event (e.g. `(event)="handler()"`).
*/
export class BoundEventAst implements TemplateAst {
constructor(
public name: string, public target: string, public phase: string, public handler: AST,
public name: string, public target: string, public handler: AST,
public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitEvent(this, context);
@ -90,7 +90,6 @@ export class BoundEventAst implements TemplateAst {
return this.name;
}
}
get isAnimation(): boolean { return !!this.phase; }
}
/**

View File

@ -8,10 +8,11 @@
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core';
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser';
import {isPresent, isString} from '../facade/lang';
import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent, isString} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
import * as html from '../ml_parser/ast';
@ -25,13 +26,12 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver';
import {splitAtColon, splitAtPeriod} from '../util';
import {splitAtColon} from '../util';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
import {PreparsedElementType, preparseElement} from './template_preparser';
// Group 1 = "bind-"
// Group 2 = "let-"
// Group 3 = "ref-/#"
@ -142,6 +142,7 @@ export class TemplateParser {
const parseVisitor = new TemplateParseVisitor(
providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser,
this._schemaRegistry);
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
errors.push(...parseVisitor.errors, ...providerViewContext.errors);
} else {
@ -443,15 +444,6 @@ class TemplateParseVisitor implements html.Visitor {
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
this._findComponentDirectives(directiveAsts)
.forEach(
componentDirectiveAst => this._validateElementAnimationInputOutputs(
componentDirectiveAst.hostProperties, componentDirectiveAst.hostEvents,
componentDirectiveAst.directive.template));
const componentTemplate = providerContext.viewContext.component.template;
this._validateElementAnimationInputOutputs(elementProps, events, componentTemplate);
}
if (hasInlineTemplates) {
@ -477,36 +469,9 @@ class TemplateParseVisitor implements html.Visitor {
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
element.sourceSpan);
}
return parsedElement;
}
private _validateElementAnimationInputOutputs(
inputs: BoundElementPropertyAst[], outputs: BoundEventAst[],
template: CompileTemplateMetadata) {
const triggerLookup = new Set<string>();
template.animations.forEach(entry => { triggerLookup.add(entry.name); });
const animationInputs = inputs.filter(input => input.isAnimation);
animationInputs.forEach(input => {
const name = input.name;
if (!triggerLookup.has(name)) {
this._reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan);
}
});
outputs.forEach(output => {
if (output.isAnimation) {
const found = animationInputs.find(input => input.name == output.name);
if (!found) {
this._reportError(
`Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`,
output.sourceSpan);
}
}
});
}
private _parseInlineTemplateBinding(
attr: html.Attribute, targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean {
@ -568,7 +533,7 @@ class TemplateParseVisitor implements html.Visitor {
this._parseReference(identifier, value, srcSpan, targetRefs);
} else if (bindParts[KW_ON_IDX]) {
this._parseEventOrAnimationEvent(
this._parseEvent(
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_BINDON_IDX]) {
@ -579,7 +544,7 @@ class TemplateParseVisitor implements html.Visitor {
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_AT_IDX]) {
if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) {
if (name[0] == '@' && isPresent(value) && value.length > 0) {
this._reportError(
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
@ -600,7 +565,7 @@ class TemplateParseVisitor implements html.Visitor {
targetAnimationProps);
} else if (bindParts[IDENT_EVENT_IDX]) {
this._parseEventOrAnimationEvent(
this._parseEvent(
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
}
} else {
@ -643,7 +608,7 @@ class TemplateParseVisitor implements html.Visitor {
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[],
targetAnimationProps: BoundElementPropertyAst[]) {
const animatePropLength = ANIMATE_PROP_PREFIX.length;
var isAnimationProp = _isAnimationLabel(name);
var isAnimationProp = name[0] == '@';
var animationPrefixLength = 1;
if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) {
isAnimationProp = true;
@ -670,7 +635,6 @@ class TemplateParseVisitor implements html.Visitor {
if (!isPresent(expression) || expression.length == 0) {
expression = 'null';
}
const ast = this._parseBinding(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]);
targetAnimationProps.push(new BoundElementPropertyAst(
@ -698,56 +662,20 @@ class TemplateParseVisitor implements html.Visitor {
private _parseAssignmentEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
this._parseEventOrAnimationEvent(
this._parseEvent(
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
}
private _parseEventOrAnimationEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
if (_isAnimationLabel(name)) {
name = name.substr(1);
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
} else {
this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
}
}
private _parseAnimationEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan,
targetEvents: BoundEventAst[]) {
const matches = splitAtPeriod(name, [name, '']);
const eventName = matches[0];
const phase = matches[1].toLowerCase();
if (phase) {
switch (phase) {
case 'start':
case 'done':
const ast = this._parseAction(expression, sourceSpan);
targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan));
break;
default:
this._reportError(
`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`,
sourceSpan);
break;
}
} else {
this._reportError(
`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`,
sourceSpan);
}
}
private _parseEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
// long format: 'target: eventName'
const [target, eventName] = splitAtColon(name, [null, name]);
const parts = splitAtColon(name, [null, name]);
const target = parts[0];
const eventName = parts[1];
const ast = this._parseAction(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]);
targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan));
targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
// Don't detect directives for event names for now,
// so don't add the event name to the matchableAttrs
}
@ -831,8 +759,7 @@ class TemplateParseVisitor implements html.Visitor {
elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan,
targetPropertyAsts: BoundElementPropertyAst[]) {
if (hostProps) {
Object.keys(hostProps).forEach(propName => {
const expression = hostProps[propName];
StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => {
if (isString(expression)) {
const exprAst = this._parseBinding(expression, sourceSpan);
targetPropertyAsts.push(
@ -850,10 +777,9 @@ class TemplateParseVisitor implements html.Visitor {
hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan,
targetEventAsts: BoundEventAst[]) {
if (hostListeners) {
Object.keys(hostListeners).forEach(propName => {
const expression = hostListeners[propName];
StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => {
if (isString(expression)) {
this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts);
this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
} else {
this._reportError(
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
@ -870,14 +796,13 @@ class TemplateParseVisitor implements html.Visitor {
const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
boundProps.forEach(boundProp => {
const prevValue = boundPropsByName.get(boundProp.name);
if (!prevValue || prevValue.isLiteral) {
if (isBlank(prevValue) || prevValue.isLiteral) {
// give [a]="b" a higher precedence than a="b" on the same element
boundPropsByName.set(boundProp.name, boundProp);
}
});
Object.keys(directiveProperties).forEach(dirProp => {
const elProp = directiveProperties[dirProp];
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
const boundProp = boundPropsByName.get(elProp);
// Bindings are optional, so this binding only needs to be set up if an expression is given.
@ -902,7 +827,7 @@ class TemplateParseVisitor implements html.Visitor {
});
props.forEach((prop: BoundElementOrDirectiveProperty) => {
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) {
boundElementProps.push(this._createElementPropertyAst(
elementName, prop.name, prop.expression, prop.sourceSpan));
}
@ -921,7 +846,7 @@ class TemplateParseVisitor implements html.Visitor {
if (parts.length === 1) {
var partValue = parts[0];
if (_isAnimationLabel(partValue)) {
if (partValue[0] == '@') {
boundPropertyName = partValue.substr(1);
bindingType = PropertyBindingType.Animation;
securityContext = SecurityContext.NONE;
@ -929,7 +854,7 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
bindingType = PropertyBindingType.Property;
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false);
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
let errorMsg =
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
@ -944,7 +869,7 @@ class TemplateParseVisitor implements html.Visitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1];
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, true);
this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
// NB: For security purposes, use the mapped property name, not the attribute name.
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
@ -977,29 +902,35 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
}
/**
* @param propName the name of the property / attribute
* @param sourceSpan
* @param isAttr true when binding to an attribute
* @private
*/
private _validatePropertyOrAttributeName(
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
this._schemaRegistry.validateProperty(propName);
if (report.error) {
this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean):
void {
if (propName.toLowerCase().startsWith('on')) {
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
`please use (${propName.slice(2)})=...`;
if (!isAttr) {
msg +=
`\nIf '${propName}' is a directive input, make sure the directive is imported by the` +
` current module.`;
}
this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
}
}
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
return directives.filter(directive => directive.directive.isComponent);
}
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
return this._findComponentDirectives(directives)
.map(directive => directive.directive.type.name);
const componentTypeNames: string[] = [];
directives.forEach(directive => {
const typeName = directive.directive.type.name;
if (directive.directive.isComponent) {
componentTypeNames.push(typeName);
}
});
return componentTypeNames;
}
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
@ -1049,8 +980,7 @@ class TemplateParseVisitor implements html.Visitor {
const allDirectiveEvents = new Set<string>();
directives.forEach(directive => {
Object.keys(directive.directive.outputs).forEach(k => {
const eventName = directive.directive.outputs[k];
StringMapWrapper.forEach(directive.directive.outputs, (eventName: string) => {
allDirectiveEvents.add(eventName);
});
});
@ -1184,7 +1114,3 @@ export class PipeCollector extends RecursiveAstVisitor {
return null;
}
}
function _isAnimationLabel(name: string): boolean {
return name[0] == '@';
}

View File

@ -7,6 +7,7 @@
*/
import {CompileTokenMetadata} from './compile_metadata';
import {StringMapWrapper} from './facade/collection';
import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
import * as o from './output/output_ast';
@ -16,21 +17,13 @@ var CAMEL_CASE_REGEXP = /([A-Z])/g;
export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped(
input, CAMEL_CASE_REGEXP, (m: string[]) => '-' + m[1].toLowerCase());
input, CAMEL_CASE_REGEXP, (m: string[]) => { return '-' + m[1].toLowerCase(); });
}
export function splitAtColon(input: string, defaultValues: string[]): string[] {
return _splitAt(input, ':', defaultValues);
}
export function splitAtPeriod(input: string, defaultValues: string[]): string[] {
return _splitAt(input, '.', defaultValues);
}
function _splitAt(input: string, character: string, defaultValues: string[]): string[] {
const characterIndex = input.indexOf(character);
if (characterIndex == -1) return defaultValues;
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
const colonIndex = input.indexOf(':');
if (colonIndex == -1) return defaultValues;
return [input.slice(0, colonIndex).trim(), input.slice(colonIndex + 1).trim()];
}
export function sanitizeIdentifier(name: string): string {
@ -62,8 +55,9 @@ export class ValueTransformer implements ValueVisitor {
}
visitStringMap(map: {[key: string]: any}, context: any): any {
var result = {};
Object.keys(map).forEach(
key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); });
StringMapWrapper.forEach(map, (value: any /** TODO #9100 */, key: any /** TODO #9100 */) => {
(result as any /** TODO #9100 */)[key] = visitValue(value, this, context);
});
return result;
}
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 {ListWrapper, MapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {ListWrapper, MapWrapper, StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util';
@ -27,7 +27,7 @@ export class CompileNode {
public parent: CompileElement, public view: CompileView, public nodeIndex: number,
public renderNode: o.Expression, public sourceAst: TemplateAst) {}
isNull(): boolean { return !this.renderNode; }
isNull(): boolean { return isBlank(this.renderNode); }
isRootElement(): boolean { return this.view != this.parent.view; }
}
@ -192,7 +192,7 @@ export class CompileElement extends CompileNode {
queriesWithReads,
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
});
Object.keys(this.referenceTokens).forEach(varName => {
StringMapWrapper.forEach(this.referenceTokens, (_: CompileTokenMetadata, varName: string) => {
var token = this.referenceTokens[varName];
var varValue: o.Expression;
if (isPresent(token)) {
@ -313,12 +313,12 @@ export class CompileElement extends CompileNode {
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
var result: o.Expression = null;
// constructor content query
if (!result && isPresent(dep.query)) {
if (isBlank(result) && isPresent(dep.query)) {
result = this._addQuery(dep.query, null).queryList;
}
// constructor view query
if (!result && isPresent(dep.viewQuery)) {
if (isBlank(result) && isPresent(dep.viewQuery)) {
result = createQueryList(
dep.viewQuery, null,
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
@ -328,7 +328,7 @@ export class CompileElement extends CompileNode {
if (isPresent(dep.token)) {
// access builtins with special visibility
if (!result) {
if (isBlank(result)) {
if (dep.token.reference ===
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
if (requestingProviderType === ProviderAstType.Component) {
@ -339,7 +339,7 @@ export class CompileElement extends CompileNode {
}
}
// access regular providers on the element
if (!result) {
if (isBlank(result)) {
let resolvedProvider = this._resolvedProviders.get(dep.token.reference);
// don't allow directives / public services to access private services.
// only components and private services can access private services.
@ -361,20 +361,20 @@ export class CompileElement extends CompileNode {
if (dep.isValue) {
result = o.literal(dep.value);
}
if (!result && !dep.isSkipSelf) {
if (isBlank(result) && !dep.isSkipSelf) {
result = this._getLocalDependency(requestingProviderType, dep);
}
// check parent elements
while (!result && !currElement.parent.isNull()) {
while (isBlank(result) && !currElement.parent.isNull()) {
currElement = currElement.parent;
result = currElement._getLocalDependency(
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
}
if (!result) {
if (isBlank(result)) {
result = injectFromViewParentInjector(dep.token, dep.isOptional);
}
if (!result) {
if (isBlank(result)) {
result = o.NULL_EXPR;
}
return getPropertyInView(result, this.view, currElement.view);
@ -411,7 +411,7 @@ function createProviderProperty(
resolvedProviderValueExpr = providerValueExpressions[0];
type = providerValueExpressions[0].type;
}
if (!type) {
if (isBlank(type)) {
type = o.DYNAMIC_TYPE;
}
if (isEager) {

View File

@ -8,7 +8,8 @@
import {CompilePipeMetadata} from '../compile_metadata';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast';
import {CompileView} from './compile_view';
@ -22,7 +23,7 @@ export class CompilePipe {
if (meta.pure) {
// pure pipes live on the component view
pipe = compView.purePipes.get(name);
if (!pipe) {
if (isBlank(pipe)) {
pipe = new CompilePipe(compView, meta);
compView.purePipes.set(name, pipe);
compView.pipes.push(pipe);
@ -84,7 +85,7 @@ function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata {
break;
}
}
if (!pipeMeta) {
if (isBlank(pipeMeta)) {
throw new 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
*/
import {CompileQueryMetadata} from '../compile_metadata';
import {CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
import {ListWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
@ -101,8 +101,9 @@ function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
function mapNestedViews(
declarationAppElement: o.Expression, view: CompileView,
expressions: o.Expression[]): o.Expression {
var adjustedExpressions: o.Expression[] = expressions.map(
(expr) => o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr));
var adjustedExpressions: o.Expression[] = expressions.map((expr) => {
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr);
});
return declarationAppElement.callMethod('mapNestedViews', [
o.variable(view.className),
o.fn(
@ -128,7 +129,7 @@ export function createQueryList(
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
query.meta.selectors.forEach((selector) => {
var entry = map.get(selector.reference);
if (!entry) {
if (isBlank(entry)) {
entry = [];
map.set(selector.reference, entry);
}

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@
import {SecurityContext} from '@angular/core';
import * as cdAst from '../expression_parser/ast';
import {isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core';
@ -31,13 +31,15 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
}
const _animationViewCheckedFlagMap = new Map<CompileView, boolean>();
function bind(
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod, bindingIndex: number) {
var checkExpression = convertCdExpressionToIr(
view, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex);
if (!checkExpression.expression) {
if (isBlank(checkExpression.expression)) {
// e.g. an empty expression was given
return;
}

View File

@ -8,7 +8,7 @@
import {CompileDirectiveMetadata, CompileTokenMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {createDiTokenExpression} from '../util';
@ -84,7 +84,7 @@ export function createPureProxy(
view.fields.push(new o.ClassField(pureProxyProp.name, null));
var pureProxyId =
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
if (!pureProxyId) {
if (isBlank(pureProxyId)) {
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
}
view.createMethod.addStmt(o.THIS_EXPR.prop(pureProxyProp.name)

View File

@ -5,17 +5,20 @@
* 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
*/
import {ListWrapper} from '../facade/collection';
import {identifierToken} from '../identifiers';
import {AnimationOutput} from '../private_import_core';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
import {CompileElement, CompileNode} from './compile_element';
import {CompileView} from './compile_view';
import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
import {CompileElementAnimationOutput, CompileEventListener, bindAnimationOutputs, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
var visitor = new ViewBinderVisitor(view);
export function bindView(
view: CompileView, parsedTemplate: TemplateAst[], animationOutputs: AnimationOutput[]): void {
var visitor = new ViewBinderVisitor(view, animationOutputs);
templateVisitAll(visitor, parsedTemplate);
view.pipes.forEach(
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
@ -23,8 +26,12 @@ export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void
class ViewBinderVisitor implements TemplateAstVisitor {
private _nodeIndex: number = 0;
private _animationOutputsMap: {[key: string]: AnimationOutput} = {};
constructor(public view: CompileView) {}
constructor(public view: CompileView, public animationOutputs: AnimationOutput[]) {
animationOutputs.forEach(
entry => { this._animationOutputsMap[entry.fullPropertyName] = entry; });
}
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
var node = this.view.nodes[this._nodeIndex++];
@ -41,9 +48,22 @@ class ViewBinderVisitor implements TemplateAstVisitor {
visitElement(ast: ElementAst, parent: CompileElement): any {
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
var eventListeners: CompileEventListener[] = [];
var animationEventListeners: CompileElementAnimationOutput[] = [];
collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => {
eventListeners.push(entry);
// TODO: figure out how to abstract this `if` statement elsewhere
if (entry.eventName[0] == '@') {
let animationOutputName = entry.eventName.substr(1);
let output = this._animationOutputsMap[animationOutputName];
// no need to report an error here since the parser will
// have caught the missing animation trigger definition
if (output) {
animationEventListeners.push(new CompileElementAnimationOutput(entry, output));
}
} else {
eventListeners.push(entry);
}
});
bindAnimationOutputs(animationEventListeners);
bindRenderInputs(ast.inputs, compileElement);
bindRenderOutputs(eventListeners);
ast.directives.forEach((directiveAst) => {
@ -88,7 +108,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
var providerInstance = compileElement.instances.get(providerAst.token.reference);
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
});
bindView(compileElement.embeddedView, ast.children);
bindView(compileElement.embeddedView, ast.children, this.animationOutputs);
return null;
}

View File

@ -6,15 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ViewEncapsulation} from '@angular/core';
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {ListWrapper} from '../facade/collection';
import {AnimationCompiler} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata, CompileTypeMetadata} from '../compile_metadata';
import {ListWrapper, SetWrapper, StringMapWrapper} from '../facade/collection';
import {StringWrapper, isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast';
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ProviderAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {createDiTokenExpression} from '../util';
import {CompileElement, CompileNode} from './compile_element';
@ -64,6 +65,8 @@ export function finishView(view: CompileView, targetStatements: o.Statement[]) {
class ViewBuilderVisitor implements TemplateAstVisitor {
nestedViewCount: number = 0;
private _animationCompiler = new AnimationCompiler();
constructor(
public view: CompileView,
public targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
@ -276,10 +279,12 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
ast.hasViewContainer, true, ast.references);
this.view.nodes.push(compileElement);
var compiledAnimations = this._animationCompiler.compileComponent(this.view.component, [ast]);
this.nestedViewCount++;
var embeddedView = new CompileView(
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement,
compiledAnimations.triggers, this.view.viewIndex + this.nestedViewCount, compileElement,
templateVariableBindings);
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
@ -346,10 +351,10 @@ function _mergeHtmlAndDirectiveAttrs(
declaredHtmlAttrs: {[key: string]: string},
directives: CompileDirectiveMetadata[]): string[][] {
var result: {[key: string]: string} = {};
Object.keys(declaredHtmlAttrs).forEach(key => { result[key] = declaredHtmlAttrs[key]; });
StringMapWrapper.forEach(
declaredHtmlAttrs, (value: string, key: string) => { result[key] = value; });
directives.forEach(directiveMeta => {
Object.keys(directiveMeta.hostAttributes).forEach(name => {
const value = directiveMeta.hostAttributes[name];
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value: string, name: string) => {
var prevValue = result[name];
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
});
@ -373,7 +378,9 @@ function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: s
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
var entryArray: string[][] = [];
Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); });
StringMapWrapper.forEach(data, (value: string, name: string) => {
entryArray.push([name, value]);
});
// We need to sort to get a defined output order
// for tests and for caching generated artifacts...
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
@ -419,11 +426,11 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
if (isPresent(compileElement.component)) {
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
}
Object.keys(compileElement.referenceTokens).forEach(varName => {
const token = compileElement.referenceTokens[varName];
varTokenEntries.push(
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
});
StringMapWrapper.forEach(
compileElement.referenceTokens, (token: CompileTokenMetadata, varName: string) => {
varTokenEntries.push(
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
});
}
return o.importExpr(resolveIdentifier(Identifiers.StaticNodeDebugInfo))
.instantiate(
@ -511,20 +518,19 @@ function createViewFactory(
templateUrlInfo = view.component.template.templateUrl;
}
if (view.viewIndex === 0) {
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp]));
initRenderCompTypeStmts = [new o.IfStmt(
renderCompTypeVar.identical(o.NULL_EXPR),
[renderCompTypeVar
.set(ViewConstructorVars.viewUtils.callMethod(
'createRenderComponentType',
[
view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''),
o.literal(view.component.template.ngContentSelectors.length),
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
view.styles,
animationsExpr,
]))
.toStmt()])];
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnVariable]));
initRenderCompTypeStmts = [new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR), [
renderCompTypeVar
.set(ViewConstructorVars.viewUtils.callMethod(
'createRenderComponentType',
[
o.literal(templateUrlInfo),
o.literal(view.component.template.ngContentSelectors.length),
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), view.styles,
animationsExpr
]))
.toStmt()
])];
}
return o
.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([new o.ReturnStatement(
@ -596,15 +602,15 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
var varStmts: any[] = [];
var readVars = o.findReadVarNames(stmts);
if (readVars.has(DetectChangesVars.changed.name)) {
if (SetWrapper.has(readVars, DetectChangesVars.changed.name)) {
varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
}
if (readVars.has(DetectChangesVars.changes.name)) {
if (SetWrapper.has(readVars, DetectChangesVars.changes.name)) {
varStmts.push(
DetectChangesVars.changes.set(o.NULL_EXPR)
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))));
}
if (readVars.has(DetectChangesVars.valUnwrapper.name)) {
if (SetWrapper.has(readVars, DetectChangesVars.valUnwrapper.name)) {
varStmts.push(
DetectChangesVars.valUnwrapper
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import {AnimationCompiler, AnimationEntryCompileResult} from '../animation/animation_compiler';
import {AnimationCompiler} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config';
import * as o from '../output/output_ast';
@ -34,18 +34,22 @@ export class ViewCompiler {
compileComponent(
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
pipes: CompilePipeMetadata[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
const view = new CompileView(
component, this._genConfig, pipes, styles, compiledAnimations, 0,
pipes: CompilePipeMetadata[]): ViewCompileResult {
var dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
var compiledAnimations = this._animationCompiler.compileComponent(component, template);
var statements: o.Statement[] = [];
var animationTriggers = compiledAnimations.triggers;
animationTriggers.forEach(entry => {
statements.push(entry.statesMapStatement);
statements.push(entry.fnStatement);
});
var view = new CompileView(
component, this._genConfig, pipes, styles, animationTriggers, 0,
CompileElement.createNull(), []);
const statements: o.Statement[] = [];
buildView(view, template, dependencies);
// Need to separate binding from creation to be able to refer to
// variables that have been declared after usage.
bindView(view, template);
bindView(view, template, compiledAnimations.outputs);
finishView(view, statements);
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationMetadata, animate, sequence, style, transition, trigger} from '@angular/core';
import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler';
import {AnimationParser} from '../../src/animation/animation_parser';
import {AnimationMetadata, animate, group, sequence, style, transition, trigger} from '@angular/core';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {StringMapWrapper} from '../../../platform-browser-dynamic/src/facade/collection';
import {AnimationCompiler, CompiledAnimationTriggerResult} from '../../src/animation/animation_compiler';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata';
import {CompileMetadataResolver} from '../../src/metadata_resolver';
@ -19,13 +20,12 @@ export function main() {
beforeEach(
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
const parser = new AnimationParser();
const compiler = new AnimationCompiler();
var compiler = new AnimationCompiler();
var compileAnimations =
(component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => {
const parsedAnimations = parser.parseComponent(component);
return compiler.compile(component.type.name, parsedAnimations);
(component: CompileDirectiveMetadata): CompiledAnimationTriggerResult => {
var result = compiler.compileComponent(component, []);
return result.triggers[0];
};
var compileTriggers = (input: any[]) => {
@ -66,5 +66,14 @@ export function main() {
expect(capturedErrorMessage)
.toMatch(/Animation states via styles must be prefixed with a ":"/);
});
it('should throw an error when two or more animation triggers contain the same name', () => {
var t1Data: any[] = [];
var t2Data: any[] = [];
expect(() => {
compileTriggers([['myTrigger', t1Data], ['myTrigger', t2Data]]);
}).toThrowError(/The animation trigger "myTrigger" has already been registered on "myCmp"/);
});
});
}

View File

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationMetadata, animate, group, keyframes, sequence, style, transition, trigger} from '@angular/core';
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
import {AnimationAnimateMetadata, AnimationGroupMetadata, AnimationMetadata, AnimationSequenceMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast';
import {AnimationParser} from '../../src/animation/animation_parser';
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast';
import {parseAnimationEntry} from '../../src/animation/animation_parser';
import {StringMapWrapper} from '../../src/facade/collection';
import {CompileMetadataResolver} from '../../src/metadata_resolver';
import {FILL_STYLE_FLAG, flattenStyles} from '../private_import_core';
@ -20,13 +21,15 @@ export function main() {
var combineStyles = (styles: AnimationStylesAst): {[key: string]: string | number} => {
var flatStyles: {[key: string]: string | number} = {};
styles.styles.forEach(
entry => Object.keys(entry).forEach(prop => { flatStyles[prop] = entry[prop]; }));
entry => StringMapWrapper.forEach(
entry, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
flatStyles[prop] = val;
}));
return flatStyles;
};
var collectKeyframeStyles =
(keyframe: AnimationKeyframeAst): {[key: string]: string | number} =>
combineStyles(keyframe.styles);
var collectKeyframeStyles = (keyframe: AnimationKeyframeAst):
{[key: string]: string | number} => { return combineStyles(keyframe.styles); };
var collectStepStyles = (step: AnimationStepAst): Array<{[key: string]: string | number}> => {
var keyframes = step.keyframes;
@ -43,17 +46,17 @@ export function main() {
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
var parseAnimation = (data: AnimationMetadata[]) => {
const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]);
const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry);
const parser = new AnimationParser();
return parser.parseEntry(compiledAnimationEntry);
var entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]);
var compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry);
return parseAnimationEntry(compiledAnimationEntry);
};
var getAnimationAstFromEntryAst =
(ast: AnimationEntryAst) => { return ast.stateTransitions[0].animation; };
var parseAnimationAst = (data: AnimationMetadata[]) =>
getAnimationAstFromEntryAst(parseAnimation(data).ast);
var parseAnimationAst = (data: AnimationMetadata[]) => {
return getAnimationAstFromEntryAst(parseAnimation(data).ast);
};
var parseAnimationAndGetErrors = (data: AnimationMetadata[]) => parseAnimation(data).errors;

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