Merge branch 'master' into ts2dart
This commit is contained in:
commit
e37f58a228
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
packages/
|
packages/
|
||||||
.buildlog
|
.buildlog
|
||||||
node_modules
|
node_modules
|
||||||
|
bower_components
|
||||||
.pub
|
.pub
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|
||||||
|
@ -27,3 +27,12 @@ script:
|
|||||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||||
after_script:
|
after_script:
|
||||||
- ./scripts/ci/print-logs.sh
|
- ./scripts/ci/print-logs.sh
|
||||||
|
notifications:
|
||||||
|
webhooks:
|
||||||
|
urls:
|
||||||
|
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||||
|
on_success: change # options: [always|never|change] default: always
|
||||||
|
on_failure: always # options: [always|never|change] default: always
|
||||||
|
on_start: false # default: false
|
||||||
|
slack:
|
||||||
|
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||||
|
233
CONTRIBUTING.md
Normal file
233
CONTRIBUTING.md
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# Contributing to Angular 2
|
||||||
|
|
||||||
|
We would love for you to contribute to Angular 2 and help make it even better than it is
|
||||||
|
today! As a contributor, here are the guidelines we would like you to follow:
|
||||||
|
|
||||||
|
- [Code of Conduct](#coc)
|
||||||
|
- [Question or Problem?](#question)
|
||||||
|
- [Issues and Bugs](#issue)
|
||||||
|
- [Feature Requests](#feature)
|
||||||
|
- [Submission Guidelines](#submit)
|
||||||
|
- [Coding Rules](#rules)
|
||||||
|
- [Commit Message Guidelines](#commit)
|
||||||
|
- [Signing the CLA](#cla)
|
||||||
|
|
||||||
|
## <a name="coc"></a> Code of Conduct
|
||||||
|
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
|
||||||
|
|
||||||
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
|
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||||
|
discussion list or [StackOverflow][stackoverflow]. We are also available on [Gitter][gitter].
|
||||||
|
|
||||||
|
## <a name="issue"></a> Found an Issue?
|
||||||
|
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||||
|
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||||
|
[submit a Pull Request](#submit-pr) with a fix.
|
||||||
|
|
||||||
|
## <a name="feature"></a> Want a Feature?
|
||||||
|
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||||
|
Repository][github]. If you would like to *implement* a new feature then consider what kind of
|
||||||
|
change it is:
|
||||||
|
|
||||||
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
|
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||||
|
and help you to craft the change so that it is successfully accepted into the project.
|
||||||
|
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||||
|
|
||||||
|
## <a name="docs"></a> Want a Doc Fix?
|
||||||
|
If you want to help improve the docs, then consider what kind of improvement it is:
|
||||||
|
|
||||||
|
* For **Major Changes**, it's a good idea to let others know what you're working on to
|
||||||
|
minimize duplication of effort. Before starting, check out the issue queue for
|
||||||
|
issues labeled [#docs](https://github.com/angular/angular/labels/%23docs).
|
||||||
|
Comment on an issue to let others know what you're working on, or [create a new issue](#submit-issue)
|
||||||
|
if your work doesn't fit within the scope of any of the existing doc issues.
|
||||||
|
Please build and test the documentation before [submitting the Pull Request](#submit-pr), to be sure
|
||||||
|
you haven't accidentally introduced any layout or formatting issues. Also ensure that your commit
|
||||||
|
message is labeled "docs" and follows the [Commit Message Guidelines](#commit) given below.
|
||||||
|
* For **Small Changes**, there is no need to file an issue first. Simply [submit a Pull Request](#submit-pr).
|
||||||
|
|
||||||
|
## <a name="submit"></a> Submission Guidelines
|
||||||
|
|
||||||
|
### <a name="submit-issue"></a> Submitting an Issue
|
||||||
|
Before you submit an issue, search the archive, maybe your question was already answered.
|
||||||
|
|
||||||
|
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||||
|
Help us to maximize the effort we can spend fixing issues and adding new
|
||||||
|
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||||
|
chances of your issue being dealt with quickly:
|
||||||
|
|
||||||
|
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||||
|
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||||
|
* **Angular Version(s)** - is it a regression?
|
||||||
|
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||||
|
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||||
|
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps.
|
||||||
|
* **Related Issues** - has a similar issue been reported before?
|
||||||
|
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||||
|
causing the problem (line of code or commit)
|
||||||
|
|
||||||
|
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||||
|
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||||
|
|
||||||
|
* Search [GitHub](https://github.com/angular/angular.dart/pulls) for an open or closed PR
|
||||||
|
that relates to your submission. You don't want to duplicate effort.
|
||||||
|
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||||
|
We cannot accept code without this.
|
||||||
|
* Make your changes in a new git branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git checkout -b my-fix-branch master
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create your patch, **including appropriate test cases**.
|
||||||
|
* Follow our [Coding Rules](#rules).
|
||||||
|
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||||
|
and ensure that all tests pass.
|
||||||
|
* Commit your changes using a descriptive commit message that follows our
|
||||||
|
[commit message conventions](#commit). Adherence to these conventions
|
||||||
|
is necessary because release notes are automatically generated from these messages.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit -a
|
||||||
|
```
|
||||||
|
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||||
|
|
||||||
|
* Push your branch to GitHub:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git push origin my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
* In GitHub, send a pull request to `angular:master`.
|
||||||
|
* If we suggest changes then:
|
||||||
|
* Make the required updates.
|
||||||
|
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
|
||||||
|
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git rebase master -i
|
||||||
|
git push -f
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Thank you for your contribution!
|
||||||
|
|
||||||
|
#### After your pull request is merged
|
||||||
|
|
||||||
|
After your pull request is merged, you can safely delete your branch and pull the changes
|
||||||
|
from the main (upstream) repository:
|
||||||
|
|
||||||
|
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git push origin --delete my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
* Check out the master branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git checkout master -f
|
||||||
|
```
|
||||||
|
|
||||||
|
* Delete the local branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git branch -D my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
* Update your master with the latest upstream version:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git pull --ff upstream master
|
||||||
|
```
|
||||||
|
|
||||||
|
## <a name="rules"></a> Coding Rules
|
||||||
|
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||||
|
|
||||||
|
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||||
|
* All public API methods **must be documented**. (Details TBC).
|
||||||
|
* With the exceptions listed below, we follow the rules contained in
|
||||||
|
[Google's JavaScript Style Guide][js-style-guide]:
|
||||||
|
* Wrap all code at **100 characters**.
|
||||||
|
|
||||||
|
## <a name="commit"></a> Commit Message Guidelines
|
||||||
|
|
||||||
|
We have very precise rules over how our git commit messages can be formatted. This leads to **more
|
||||||
|
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||||
|
we use the git commit messages to **generate the Angular change log**.
|
||||||
|
|
||||||
|
### Commit Message Format
|
||||||
|
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||||
|
format that includes a **type**, a **scope** and a **subject**:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||||
|
to read on GitHub as well as in various git tools.
|
||||||
|
|
||||||
|
### Type
|
||||||
|
Must be one of the following:
|
||||||
|
|
||||||
|
* **feat**: A new feature
|
||||||
|
* **fix**: A bug fix
|
||||||
|
* **docs**: Documentation only changes
|
||||||
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||||
|
semi-colons, etc)
|
||||||
|
* **refactor**: A code change that neither fixes a bug or adds a feature
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
|
* **test**: Adding missing tests
|
||||||
|
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||||
|
generation
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
The scope could be anything specifying place of the commit change. For example
|
||||||
|
`Compiler`, `ElementInjector`, etc.
|
||||||
|
|
||||||
|
### Subject
|
||||||
|
The subject contains succinct description of the change:
|
||||||
|
|
||||||
|
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
|
* don't capitalize first letter
|
||||||
|
* no dot (.) at the end
|
||||||
|
|
||||||
|
### Body
|
||||||
|
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
||||||
|
The body should include the motivation for the change and contrast this with previous behavior.
|
||||||
|
|
||||||
|
### Footer
|
||||||
|
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||||
|
reference GitHub issues that this commit **Closes**.
|
||||||
|
|
||||||
|
|
||||||
|
A detailed explanation can be found in this [document][commit-message-format].
|
||||||
|
|
||||||
|
## <a name="cla"></a> Signing the CLA
|
||||||
|
|
||||||
|
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||||
|
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||||
|
|
||||||
|
* For individuals we have a [simple click-through form][individual-cla].
|
||||||
|
* For corporations we'll need you to
|
||||||
|
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||||
|
|
||||||
|
|
||||||
|
[angular-group]: https://groups.google.com/forum/#!forum/angular
|
||||||
|
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||||
|
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||||
|
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
|
[dev-doc]: https://github.com/angular/angular/blob/master/DEVELOPER.md
|
||||||
|
[github]: https://github.com/angular/angular
|
||||||
|
[gitter]: https://gitter.im/angular/angular
|
||||||
|
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
|
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||||
|
[jsfiddle]: http://jsfiddle.net/
|
||||||
|
[plunker]: http://plnkr.co/edit
|
||||||
|
[runnable]: http://runnable.com/
|
||||||
|
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
235
DEVELOPER.md
Normal file
235
DEVELOPER.md
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
# Building and Testing Angular 2 for JS and Dart
|
||||||
|
|
||||||
|
This document describes how to set up your development environment to build and test Angular, both
|
||||||
|
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||||
|
|
||||||
|
See the [contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||||
|
for how to contribute your own code to
|
||||||
|
|
||||||
|
1. [Prerequisite Software](#prerequisite-software)
|
||||||
|
2. [Getting the Sources](#getting-the-sources)
|
||||||
|
3. [Environment Variable Setup](#environment-variable-setup)
|
||||||
|
4. [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||||
|
5. [Running Tests Locally](#running-tests-locally)
|
||||||
|
6. [Project Information](#project-information)
|
||||||
|
7. [CI using Travis](#ci-using-travis)
|
||||||
|
8. [Debugging](#debugging)
|
||||||
|
|
||||||
|
## Prerequisite Software
|
||||||
|
|
||||||
|
Before you can build and test Angular, you must install and configure the
|
||||||
|
following products on your development machine:
|
||||||
|
|
||||||
|
* [Dart](https://www.dartlang.org) (version `>=1.9.0-dev.8.0`), specifically the Dart-SDK and
|
||||||
|
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||||
|
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||||
|
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||||
|
download [page for instructions](https://www.dartlang.org/tools/download.html); note that you can
|
||||||
|
download both **stable** and **dev** channel versions from the [download
|
||||||
|
archive](https://www.dartlang.org/tools/download-archive).
|
||||||
|
|
||||||
|
* [Git](http://git-scm.com) and/or the **Github app** (for [Mac](http://mac.github.com) or
|
||||||
|
[Windows](http://windows.github.com)): the [Github Guide to Installing
|
||||||
|
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||||
|
|
||||||
|
* [Node.js](http://nodejs.org) which is used to run a development web server, run tests, and
|
||||||
|
generate distributable files. We also use Node's Package Manager (`npm`). Depending on your
|
||||||
|
system, you can install Node either from source or as a pre-packaged bundle.
|
||||||
|
|
||||||
|
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||||
|
bleeding edge functionality, built especially for developers (and early adopters).
|
||||||
|
|
||||||
|
|
||||||
|
## Getting the Sources
|
||||||
|
|
||||||
|
Forking and cloning the Angular repository:
|
||||||
|
|
||||||
|
1. Login to your Github account or create one by following the instructions given
|
||||||
|
[here](https://github.com/signup/free).
|
||||||
|
2. [Fork](http://help.github.com/forking) the [main Angular
|
||||||
|
repository](https://github.com/angular/angular).
|
||||||
|
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
|
||||||
|
the Angular repository that you forked in the first place:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Clone your Github repository:
|
||||||
|
git clone git@github.com:<github username>/angular.git
|
||||||
|
|
||||||
|
# Go to the Angular directory:
|
||||||
|
cd angular
|
||||||
|
|
||||||
|
# Add the main Angular repository as an upstream remote to your repository:
|
||||||
|
git remote add upstream https://github.com/angular/angular.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variable Setup
|
||||||
|
|
||||||
|
Define the environment variables listed below. These are mainly needed for the testing. The
|
||||||
|
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
|
||||||
|
your favorite shell.
|
||||||
|
|
||||||
|
Examples given below of possible values for initializing the environment variables assume **Mac OS
|
||||||
|
X** and that you have installed the Dart Editor in the directory named by
|
||||||
|
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
|
||||||
|
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
|
||||||
|
[here](https://www.dartlang.org/tools/pub/installing.html)):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# DART_SDK: path to a Dart SDK directory
|
||||||
|
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
||||||
|
|
||||||
|
# Update PATH to include the Dart SDK bin directory
|
||||||
|
PATH+=":$DART_SDK/bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing NPM Modules and Dart Packages
|
||||||
|
|
||||||
|
Next, install the modules and packages needed to build Angular and run tests:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Install Angular project dependencies (package.json)
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Ensure protractor has the latest webdriver
|
||||||
|
$(npm bin)/webdriver-manager update
|
||||||
|
|
||||||
|
# Install Dart packages
|
||||||
|
pub install
|
||||||
|
```
|
||||||
|
|
||||||
|
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
|
||||||
|
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
|
||||||
|
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by
|
||||||
|
globally installing these two packages as follows:
|
||||||
|
|
||||||
|
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
|
||||||
|
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
|
||||||
|
|
||||||
|
Since global installs can become stale, we avoid their use in these instructions.
|
||||||
|
|
||||||
|
## Build commands
|
||||||
|
|
||||||
|
To build Angular and prepare tests run
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$(npm bin)/gulp build
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* Results are put in the `dist` folder.
|
||||||
|
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
||||||
|
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`
|
||||||
|
|
||||||
|
To clean out the `dist` folder use:
|
||||||
|
```shell
|
||||||
|
$(npm bin)/gulp clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Tests Locally
|
||||||
|
|
||||||
|
### Basic tests
|
||||||
|
|
||||||
|
1. `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
|
||||||
|
watches the test files for changes and re-runs tests when files are updated).
|
||||||
|
2. `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS (requires a build before)
|
||||||
|
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||||
|
|
||||||
|
If you prefer running tests in "single-run" mode rather than watch mode use
|
||||||
|
|
||||||
|
* `$(npm bin)/gulp test.unit.js/ci`
|
||||||
|
* `$(npm bin)/gulp test.unit.dart/ci`
|
||||||
|
|
||||||
|
**Note**: If you want to only run a single test you can alter the test you wish
|
||||||
|
to run by changing `it` to `iit` or `describe` to `ddescribe`. This will only
|
||||||
|
run that individual test and make it much easier to debug. `xit` and `xdescribe`
|
||||||
|
can also be useful to exclude a test and a group of tests respectively.
|
||||||
|
|
||||||
|
**Note** for transpiler tests: The karma preprocessor is setup in a way so that after every test
|
||||||
|
run the transpiler is reloaded. With that it is possible to make changes to the preprocessor and
|
||||||
|
run the tests without exiting karma (just touch a test file that you would like to run).
|
||||||
|
|
||||||
|
### E2e tests
|
||||||
|
|
||||||
|
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||||
|
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver).
|
||||||
|
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
||||||
|
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: Dart2JS e2e tests.
|
||||||
|
|
||||||
|
Angular specific command line options when running protractor:
|
||||||
|
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||||
|
|
||||||
|
### Performance tests
|
||||||
|
|
||||||
|
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
||||||
|
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
||||||
|
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
||||||
|
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: Dart2JS performance tests
|
||||||
|
|
||||||
|
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||||
|
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||||
|
|
||||||
|
## Project Information
|
||||||
|
|
||||||
|
### Folder structure
|
||||||
|
|
||||||
|
* `modules/*`: modules that will be loaded in the browser
|
||||||
|
* `tools/*`: tools that are needed to build Angular
|
||||||
|
* `dist/*`: build files are placed here.
|
||||||
|
|
||||||
|
### File endings
|
||||||
|
|
||||||
|
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
|
||||||
|
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
|
||||||
|
* `*.es5`: javascript files that don't get transpiled
|
||||||
|
* `*.dart`: dart files that don't get transpiled
|
||||||
|
|
||||||
|
## CI using Travis
|
||||||
|
|
||||||
|
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||||
|
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Debug the transpiler
|
||||||
|
|
||||||
|
If you need to debug the transpiler:
|
||||||
|
|
||||||
|
- add a `debugger;` statement in the transpiler code,
|
||||||
|
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
|
||||||
|
debugger
|
||||||
|
- press "c" to execute the program until you reach the `debugger;` statement,
|
||||||
|
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
||||||
|
|
||||||
|
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
|
||||||
|
code you want to debug (the former will process the "modules" folder while the later processes
|
||||||
|
the transpiler specs).
|
||||||
|
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
||||||
|
the developer tools are opened in the browser running Karma.
|
||||||
|
|
||||||
|
### Debug the tests
|
||||||
|
|
||||||
|
If you need to debug the tests:
|
||||||
|
|
||||||
|
- add a `debugger;` statement to the test you want to debug (oe the source code),
|
||||||
|
- execute karma `$(npm bin)/gulp test.js`,
|
||||||
|
- press the top right "DEBUG" button,
|
||||||
|
- open the dev tools and press F5,
|
||||||
|
- the execution halt at the `debugger;` statement
|
||||||
|
|
||||||
|
**Note (WebStorm users)**:
|
||||||
|
You can create a Karma run config from WebStorm.
|
||||||
|
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
|
||||||
|
on the `debugger;` statement.
|
||||||
|
You can then step into the code and add watches.
|
||||||
|
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
||||||
|
the original source files are not supported at the moment.
|
||||||
|
|
139
README.md
139
README.md
@ -1,125 +1,52 @@
|
|||||||
Angular [](https://travis-ci.org/angular/angular)
|
Angular [](https://travis-ci.org/angular/angular) [](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you
|
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||||
should go to [angular/angular.js](https://github.com/angular/angular.js)
|
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||||
|
|
||||||
## Build
|
Angular 2 is currently in **Alpha Preview**. We recommend using Angular 1.X for production
|
||||||
|
applications:
|
||||||
|
|
||||||
### Prerequisites
|
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||||
|
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||||
|
|
||||||
If you don't already have `npm`, get it by installing [node.js](http://nodejs.org/).
|
|
||||||
|
|
||||||
1. `npm install`
|
## Setup & Install Angular 2
|
||||||
2. `npm install -g gulp` (you might need to prefix this command with `sudo`)
|
|
||||||
3. `npm install -g protractor` (you might need to prefix this command with `sudo`)
|
|
||||||
4. `webdriver-manager update`
|
|
||||||
5. If you plan to use Dart:
|
|
||||||
1. [Install the Dart SDK](https://www.dartlang.org/tools/sdk/) - Includes the `pub` command line tool. This repository requires `pub` in version `>=1.9.0-dev.8.0 <2.0.0`
|
|
||||||
2. [Add the Dart SDK's `bin` directory to your system path](https://www.dartlang.org/tools/pub/installing.html)
|
|
||||||
3. Get the pub packages you need: `pub get`
|
|
||||||
6. `gulp build`
|
|
||||||
|
|
||||||
### Folder structure
|
Follow the instructions given on the [Angular download page][download].
|
||||||
|
|
||||||
* `modules/*`: modules that will be loaded in the browser
|
|
||||||
* `tools/*`: tools that are needed to build Angular
|
|
||||||
|
|
||||||
### File endings
|
## Want to help?
|
||||||
|
|
||||||
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
|
Want to file a bug, or contribute some code or improve documentation? Excellent! Read up on our
|
||||||
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
|
guidelines for [contributing][contributing].
|
||||||
* `*.es5`: javascript files that don't get transpiled
|
|
||||||
* `*.dart`: dart files that don't get transpiled
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
1. `gulp build` -> result is in `dist` folder
|
## Examples
|
||||||
|
|
||||||
* will also run `pub get` for the subfolders in `modules`
|
To see the examples, first build the project as described
|
||||||
and run `dartanalyzer` for every file that matches
|
[here](http://github.com/angular/angular/blob/master/DEVELOPER.md).
|
||||||
`<module>/src/<module>.dart`, e.g. `di/src/di.dart`
|
|
||||||
|
|
||||||
2. `gulp clean` -> cleans the `dist` folder
|
### Hello World Example
|
||||||
|
|
||||||
### Unit tests
|
This example consists of three basic pieces - a component, a decorator and a
|
||||||
|
service. They are all constructed via injection. For more information see the
|
||||||
1. `gulp test.unit.js`: JS tests
|
comments in the source `modules/examples/src/hello_world/index.js`.
|
||||||
2. `gulp test.unit.dart`: Dart tests
|
|
||||||
|
|
||||||
Notes for transpiler tests:
|
|
||||||
|
|
||||||
The karma preprocessor is setup in a way so that after every test run
|
|
||||||
the transpiler is reloaded. With that it is possible to make changes
|
|
||||||
to the preprocessor and run the tests without exiting karma
|
|
||||||
(just touch a test file that you would like to run).
|
|
||||||
|
|
||||||
### E2e tests
|
|
||||||
|
|
||||||
1. `gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
|
||||||
2. `gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
|
||||||
3. `protractor protractor-js.conf.js`: JS e2e tests
|
|
||||||
4. `protractor protractor-dart2js.conf.js`: Dart2JS e2e tests
|
|
||||||
|
|
||||||
Angular specific command line options when running protractor:
|
|
||||||
- `protractor protractor-{js|dart2js}-conf.js --ng-help`
|
|
||||||
|
|
||||||
### Performance tests
|
|
||||||
|
|
||||||
1. `gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
|
||||||
2. `gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
|
||||||
3. `protractor protractor-js.conf.js --benchmark`: JS performance tests
|
|
||||||
4. `protractor protractor-dart2js.conf.js --benchmark`: Dart2JS performance tests
|
|
||||||
|
|
||||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
|
||||||
`protractor protractor-{js|dart2js}-conf.js --ng-help`
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
To see the examples, first build the project as described above.
|
|
||||||
|
|
||||||
#### Hello World Example
|
|
||||||
This example consists of three basic pieces - a component, a decorator and a service.
|
|
||||||
They are all constructed via injection. For more information see the comments in the
|
|
||||||
source `modules/examples/src/hello_world/index.js`.
|
|
||||||
|
|
||||||
You can build this example as either JS or Dart app:
|
You can build this example as either JS or Dart app:
|
||||||
* (JS) `gulp serve.js.dev` and open `localhost:8000/examples/src/hello_world/` in Chrome.
|
|
||||||
* (Dart) `gulp serve/examples.dart` and open `localhost:8080/src/hello_world` in Chrome (for dart2js) or Dartium (for Dart VM).
|
|
||||||
|
|
||||||
## Debug the transpiler
|
* JS:
|
||||||
|
* `$(npm bin)/gulp serve.js.dev`, and
|
||||||
|
* open `localhost:8000/examples/src/hello_world/` in Chrome.
|
||||||
|
* Dart:
|
||||||
|
* `$(npm bin)/gulp serve/examples.dart`, and
|
||||||
|
* open `localhost:8080/src/hello_world` in Chrome (for dart2js) or
|
||||||
|
[Dartium][dartium] (for Dart VM).
|
||||||
|
|
||||||
If you need to debug the transpiler:
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
|
[dart]: http://www.dartlang.org
|
||||||
- add a `debugger;` statement in the transpiler code,
|
[dartium]: http://www.dartlang.org/tools/dartium
|
||||||
- from the root folder, execute `node debug node_modules/.bin/gulp build` to enter the node
|
[download]: http://angular.io/download
|
||||||
debugger
|
[ng2]: http://angular.io
|
||||||
- press "c" to execute the program until you reach the `debugger;` statement,
|
[ngDart]: http://angulardart.org
|
||||||
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
[ngJS]: http://angularjs.org
|
||||||
|
|
||||||
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
- You can also execute `node node_modules/.bin/karma start karma-dart.conf.js` depending on which
|
|
||||||
code you want to debug (the former will process the "modules" folder while the later processes
|
|
||||||
the transpiler specs),
|
|
||||||
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
|
||||||
the developer tools are opened in the browser running Karma.
|
|
||||||
|
|
||||||
## Debug the tests
|
|
||||||
|
|
||||||
If you need to debug the tests:
|
|
||||||
|
|
||||||
- add a `debugger;` statement to the test you want to debug (oe the source code),
|
|
||||||
- execute karma `gulp test.js`,
|
|
||||||
- press the top right "DEBUG" button,
|
|
||||||
- open the dev tools and press F5,
|
|
||||||
- the execution halt at the `debugger;` statement
|
|
||||||
|
|
||||||
Note (WebStorm users):
|
|
||||||
You can create a Karma run config from WebStorm.
|
|
||||||
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
|
|
||||||
on the `debugger;` statement.
|
|
||||||
You can then step into the code and add watches.
|
|
||||||
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
|
||||||
the original source files are not supported at the moment.
|
|
||||||
|
7
bower.json
Normal file
7
bower.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "angular2",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"polymer": "dart-lang/polymer_js#0.8.0-preview"
|
||||||
|
}
|
||||||
|
}
|
@ -5,11 +5,11 @@ angular.module('code', [])
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
terminal: true,
|
terminal: true,
|
||||||
compile: function(element) {
|
compile: function(element) {
|
||||||
var linenums = element.hasClass('linenum');// || element.parent()[0].nodeName === 'PRE';
|
var linenums = element.hasClass('linenum');
|
||||||
var match = /lang-(\S+)/.exec(element[0].className);
|
var match = /lang-(\S+)/.exec(element[0].className);
|
||||||
var lang = match && match[1];
|
var lang = match && match[1];
|
||||||
var html = element.html();
|
var html = element.html();
|
||||||
element.html(window.prettyPrintOne(html, lang, linenums));
|
element.html(window.prettyPrintOne(html, lang, linenums));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ var GUIDES_PATH = PARTIAL_PATH + '/guides';
|
|||||||
module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||||
|
|
||||||
// Register the services and file readers
|
// Register the services and file readers
|
||||||
|
.factory(require('./services/modules'))
|
||||||
.factory(require('./services/atParser'))
|
.factory(require('./services/atParser'))
|
||||||
.factory(require('./services/getJSDocComment'))
|
.factory(require('./services/getJSDocComment'))
|
||||||
.factory(require('./services/SourceFile'))
|
.factory(require('./services/SourceFile'))
|
||||||
@ -44,6 +45,7 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
|||||||
readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
|
readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
|
||||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||||
readFilesProcessor.sourceFiles = [
|
readFilesProcessor.sourceFiles = [
|
||||||
|
{ include: 'modules/*/*.js', basePath: 'modules' },
|
||||||
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
|
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
|
||||||
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
|
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
|
||||||
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
|
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
|
||||||
|
@ -5,8 +5,10 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
|||||||
return {
|
return {
|
||||||
$runAfter: ['processModuleDocs'],
|
$runAfter: ['processModuleDocs'],
|
||||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||||
|
ignorePrivateMembers: false,
|
||||||
$process: function(docs) {
|
$process: function(docs) {
|
||||||
var memberDocs = [];
|
var memberDocs = [];
|
||||||
|
var ignorePrivateMembers = this.ignorePrivateMembers;
|
||||||
_.forEach(docs, function(classDoc) {
|
_.forEach(docs, function(classDoc) {
|
||||||
if ( classDoc.docType === 'class' ) {
|
if ( classDoc.docType === 'class' ) {
|
||||||
|
|
||||||
@ -15,6 +17,8 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
|||||||
// Create a new doc for each member of the class
|
// Create a new doc for each member of the class
|
||||||
_.forEach(classDoc.elements, function(memberDoc) {
|
_.forEach(classDoc.elements, function(memberDoc) {
|
||||||
|
|
||||||
|
if (ignorePrivateMembers && memberDoc.name.literalToken.value.charAt(0) === '_') return;
|
||||||
|
|
||||||
classDoc.members.push(memberDoc);
|
classDoc.members.push(memberDoc);
|
||||||
memberDocs.push(memberDoc);
|
memberDocs.push(memberDoc);
|
||||||
|
|
||||||
@ -22,6 +26,7 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
|||||||
memberDoc.classDoc = classDoc;
|
memberDoc.classDoc = classDoc;
|
||||||
memberDoc.name = memberDoc.name.literalToken.value;
|
memberDoc.name = memberDoc.name.literalToken.value;
|
||||||
|
|
||||||
|
|
||||||
if (memberDoc.commentBefore ) {
|
if (memberDoc.commentBefore ) {
|
||||||
// If this export has a comment, remove it from the list of
|
// If this export has a comment, remove it from the list of
|
||||||
// comments collected in the module
|
// comments collected in the module
|
||||||
|
@ -7,7 +7,7 @@ var path = require('canonical-path');
|
|||||||
* This file reader will create a simple doc for each
|
* This file reader will create a simple doc for each
|
||||||
* file including a code AST of the AtScript in the file.
|
* file including a code AST of the AtScript in the file.
|
||||||
*/
|
*/
|
||||||
module.exports = function atScriptFileReader(log, atParser) {
|
module.exports = function atScriptFileReader(log, atParser, modules) {
|
||||||
var reader = {
|
var reader = {
|
||||||
name: 'atScriptFileReader',
|
name: 'atScriptFileReader',
|
||||||
defaultPattern: /\.js$/,
|
defaultPattern: /\.js$/,
|
||||||
@ -18,6 +18,8 @@ module.exports = function atScriptFileReader(log, atParser) {
|
|||||||
moduleDoc.id = moduleDoc.moduleTree.moduleName;
|
moduleDoc.id = moduleDoc.moduleTree.moduleName;
|
||||||
moduleDoc.aliases = [moduleDoc.id];
|
moduleDoc.aliases = [moduleDoc.id];
|
||||||
|
|
||||||
|
modules[moduleDoc.id] = moduleDoc;
|
||||||
|
|
||||||
// Readers return a collection of docs read from the file
|
// Readers return a collection of docs read from the file
|
||||||
// but in this read there is only one document (module) to return
|
// but in this read there is only one document (module) to return
|
||||||
return [moduleDoc];
|
return [moduleDoc];
|
||||||
|
@ -16,7 +16,8 @@ module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
|
|||||||
|
|
||||||
if (this.currentComment) log.silly('comment: ' +
|
if (this.currentComment) log.silly('comment: ' +
|
||||||
this.currentComment.range.start.line + ' - ' +
|
this.currentComment.range.start.line + ' - ' +
|
||||||
this.currentComment.range.end.line);
|
this.currentComment.range.end.line + ' : ' +
|
||||||
|
this.currentComment.range.toString());
|
||||||
|
|
||||||
ParseTreeVisitor.prototype.visit.call(this, tree);
|
ParseTreeVisitor.prototype.visit.call(this, tree);
|
||||||
},
|
},
|
||||||
@ -24,14 +25,18 @@ module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
|
|||||||
// Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
|
// Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
|
||||||
// it is easier to simply override the prototype's method on the instance
|
// it is easier to simply override the prototype's method on the instance
|
||||||
visitAny: function(tree) {
|
visitAny: function(tree) {
|
||||||
if (tree && tree.location && tree.location.start && this.currentComment) {
|
if (tree && tree.location && tree.location.start && this.currentComment &&
|
||||||
if (this.currentComment.range.end.offset < tree.location.start.offset) {
|
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||||
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
|
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
|
||||||
|
while (this.currentComment &&
|
||||||
|
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||||
|
log.silly('comment: ' + this.currentComment.range.start.line + ' - ' +
|
||||||
|
this.currentComment.range.end.line + ' : ' +
|
||||||
|
this.currentComment.range.toString());
|
||||||
tree.commentBefore = this.currentComment;
|
tree.commentBefore = this.currentComment;
|
||||||
this.currentComment.treeAfter = tree;
|
this.currentComment.treeAfter = tree;
|
||||||
this.index++;
|
this.index++;
|
||||||
this.currentComment = this.comments[this.index];
|
this.currentComment = this.comments[this.index];
|
||||||
if (this.currentComment) log.silly('comment: ' + this.currentComment.range.start.line + ' - ' + this.currentComment.range.end.line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ParseTreeVisitor.prototype.visitAny.call(this, tree);
|
return ParseTreeVisitor.prototype.visitAny.call(this, tree);
|
||||||
|
@ -17,8 +17,10 @@ module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
|
|||||||
ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
|
ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
|
||||||
log.silly('exit', this.currentExport);
|
log.silly('exit', this.currentExport);
|
||||||
|
|
||||||
// We are exiting the export declaration - store the export object
|
if(this.currentExport) {
|
||||||
this.exports.push(this.currentExport);
|
// We are exiting the export declaration - store the export object
|
||||||
|
this.exports.push(this.currentExport);
|
||||||
|
}
|
||||||
this.currentExport = null;
|
this.currentExport = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -78,14 +80,15 @@ module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
visitNamedExport: function(tree) {
|
visitNamedExport: function(tree) {
|
||||||
if ( this.currentExport ) {
|
this.currentExport = null;
|
||||||
this.updateExport(tree);
|
// if ( this.currentExport ) {
|
||||||
|
// this.updateExport(tree);
|
||||||
|
|
||||||
this.currentExport.namedExport = tree;
|
// this.currentExport.namedExport = tree;
|
||||||
this.currentExport.name = 'NAMED_EXPORT';
|
// this.currentExport.name = 'NAMED_EXPORT';
|
||||||
// TODO: work out this bit!!
|
// // TODO: work out this bit!!
|
||||||
// We need to cope with any export specifiers in the named export
|
// // We need to cope with any export specifiers in the named export
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO - if the export is an expression, find the thing that is being
|
// TODO - if the export is an expression, find the thing that is being
|
||||||
|
3
docs/dgeni-package/services/modules.js
Normal file
3
docs/dgeni-package/services/modules.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = function modules() {
|
||||||
|
return {};
|
||||||
|
};
|
20
docs/public-docs-package/index.js
Normal file
20
docs/public-docs-package/index.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
var Package = require('dgeni').Package;
|
||||||
|
var basePackage = require('../dgeni-package');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = new Package('angular-public', [basePackage])
|
||||||
|
|
||||||
|
.processor(require('./processors/filterPublicDocs'))
|
||||||
|
|
||||||
|
.config(function(parseTagsProcessor) {
|
||||||
|
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
|
||||||
|
})
|
||||||
|
|
||||||
|
.config(function(processClassDocs) {
|
||||||
|
processClassDocs.ignorePrivateMembers = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Configure file writing
|
||||||
|
.config(function(writeFilesProcessor) {
|
||||||
|
writeFilesProcessor.outputFolder = 'dist/public_docs';
|
||||||
|
});
|
49
docs/public-docs-package/processors/filterPublicDocs.js
Normal file
49
docs/public-docs-package/processors/filterPublicDocs.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports = function filterPublicDocs(modules) {
|
||||||
|
return {
|
||||||
|
$runAfter: ['tags-parsed'],
|
||||||
|
$runBefore: ['computing-ids'],
|
||||||
|
$process: function(docs) {
|
||||||
|
|
||||||
|
//console.log('filterPublicDocs', Object.keys(modules));
|
||||||
|
|
||||||
|
|
||||||
|
docs = _.filter(docs, function(doc) {
|
||||||
|
if (doc.docType !== 'class') return true;
|
||||||
|
if (!doc.publicModule) return false;
|
||||||
|
|
||||||
|
//console.log('CLASS:', doc.name, doc.moduleDoc.id);
|
||||||
|
updateModule(doc);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
docs = _.filter(docs, function(doc) {
|
||||||
|
return doc.docType !== 'module' || doc.isPublic;
|
||||||
|
});
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function updateModule(classDoc) {
|
||||||
|
|
||||||
|
var originalModule = classDoc.moduleDoc;
|
||||||
|
var publicModule = modules[classDoc.publicModule];
|
||||||
|
|
||||||
|
if (!publicModule) {
|
||||||
|
throw new Error('Missing module definition: "' + classDoc.publicModule + '"\n' +
|
||||||
|
'Referenced in class: "' + classDoc.moduleDoc.id + '/' + classDoc.name + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
publicModule.isPublic = true;
|
||||||
|
|
||||||
|
//console.log('UPDATE CLASS', classDoc.id, originalModule.id, publicModule.id);
|
||||||
|
|
||||||
|
_.remove(classDoc.moduleDoc.exports, function(doc) { return doc === classDoc; });
|
||||||
|
classDoc.moduleDoc = publicModule;
|
||||||
|
publicModule.exports.push(classDoc);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
143
gulpfile.js
143
gulpfile.js
@ -1,6 +1,7 @@
|
|||||||
var gulp = require('gulp');
|
var gulp = require('gulp');
|
||||||
var gulpPlugins = require('gulp-load-plugins')();
|
var gulpPlugins = require('gulp-load-plugins')();
|
||||||
var runSequence = require('run-sequence');
|
var runSequence = require('run-sequence');
|
||||||
|
var madge = require('madge');
|
||||||
var merge = require('merge');
|
var merge = require('merge');
|
||||||
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
|
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ var karma = require('karma').server;
|
|||||||
var minimist = require('minimist');
|
var minimist = require('minimist');
|
||||||
var es5build = require('./tools/build/es5build');
|
var es5build = require('./tools/build/es5build');
|
||||||
var runServerDartTests = require('./tools/build/run_server_dart_tests');
|
var runServerDartTests = require('./tools/build/run_server_dart_tests');
|
||||||
|
var transformCJSTests = require('./tools/build/transformCJSTests');
|
||||||
var util = require('./tools/build/util');
|
var util = require('./tools/build/util');
|
||||||
|
|
||||||
var DART_SDK = require('./tools/build/dartdetect')(gulp);
|
var DART_SDK = require('./tools/build/dartdetect')(gulp);
|
||||||
@ -34,7 +36,7 @@ var _COMPILER_CONFIG_JS_DEFAULT = {
|
|||||||
modules: 'instantiate'
|
modules: 'instantiate'
|
||||||
};
|
};
|
||||||
|
|
||||||
var _HTLM_DEFAULT_SCRIPTS_JS = [
|
var _HTML_DEFAULT_SCRIPTS_JS = [
|
||||||
{src: gulpTraceur.RUNTIME_PATH, mimeType: 'text/javascript', copy: true},
|
{src: gulpTraceur.RUNTIME_PATH, mimeType: 'text/javascript', copy: true},
|
||||||
{src: 'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
{src: 'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
||||||
mimeType: 'text/javascript', copy: true},
|
mimeType: 'text/javascript', copy: true},
|
||||||
@ -205,16 +207,21 @@ var CONFIG = {
|
|||||||
},
|
},
|
||||||
scriptsPerFolder: {
|
scriptsPerFolder: {
|
||||||
js: {
|
js: {
|
||||||
'**': _HTLM_DEFAULT_SCRIPTS_JS,
|
'**': _HTML_DEFAULT_SCRIPTS_JS,
|
||||||
'benchmarks/**':
|
'benchmarks/**':
|
||||||
[
|
[
|
||||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||||
].concat(_HTLM_DEFAULT_SCRIPTS_JS),
|
].concat(_HTML_DEFAULT_SCRIPTS_JS),
|
||||||
'benchmarks_external/**':
|
'benchmarks_external/**':
|
||||||
[
|
[
|
||||||
{ src: 'node_modules/angular/angular.js', mimeType: 'text/javascript', copy: true },
|
{ src: 'node_modules/angular/angular.js', mimeType: 'text/javascript', copy: true },
|
||||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||||
].concat(_HTLM_DEFAULT_SCRIPTS_JS)
|
].concat(_HTML_DEFAULT_SCRIPTS_JS),
|
||||||
|
'benchmarks_external/**/*polymer*/**':
|
||||||
|
[
|
||||||
|
{ src: 'bower_components/polymer/lib/polymer.html', copyOnly: true },
|
||||||
|
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
dart: {
|
dart: {
|
||||||
'**': _HTML_DEFAULT_SCRIPTS_DART,
|
'**': _HTML_DEFAULT_SCRIPTS_DART,
|
||||||
@ -228,8 +235,26 @@ var CONFIG = {
|
|||||||
formatDart: {
|
formatDart: {
|
||||||
packageName: 'dart_style',
|
packageName: 'dart_style',
|
||||||
args: ['dart_style:format', '-w', 'dist/dart']
|
args: ['dart_style:format', '-w', 'dist/dart']
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
js: {
|
||||||
|
cjs: [
|
||||||
|
'/angular2/test/change_detection/**/*_spec.js',
|
||||||
|
'/angular2/test/core/annotations/**/*_spec.js',
|
||||||
|
'/angular2/test/core/compiler/**/*_spec.js',
|
||||||
|
'/angular2/test/di/**/*_spec.js',
|
||||||
|
'/angular2/test/directives/**/*_spec.js',
|
||||||
|
'/angular2/test/facade/**/*_spec.js',
|
||||||
|
'/angular2/test/forms/**/*_spec.js',
|
||||||
|
'/angular2/test/mock/**/*_spec.js',
|
||||||
|
'/angular2/test/reflection/**/*_spec.js',
|
||||||
|
'/angular2/test/services/**/*_spec.js',
|
||||||
|
'/angular2/test/test_lib/**/*_spec.js'
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
CONFIG.test.js.cjs = CONFIG.test.js.cjs.map(function(s) {return CONFIG.dest.js.cjs + s});
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
// clean
|
// clean
|
||||||
@ -299,12 +324,15 @@ gulp.task('build/transpile.js.prod', function(done) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build/transpile.js.cjs', transpile(gulp, gulpPlugins, {
|
gulp.task('build/transpile.js.cjs', transpile(gulp, gulpPlugins, {
|
||||||
src: CONFIG.transpile.src.js,
|
src: CONFIG.transpile.src.js.concat(['modules/**/*.cjs']),
|
||||||
dest: CONFIG.dest.js.cjs,
|
dest: CONFIG.dest.js.cjs,
|
||||||
outputExt: 'js',
|
outputExt: 'js',
|
||||||
options: CONFIG.transpile.options.js.cjs,
|
options: CONFIG.transpile.options.js.cjs,
|
||||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
||||||
}));
|
}));
|
||||||
|
gulp.task('build/transformCJSTests', function() {
|
||||||
|
return gulp.src(CONFIG.dest.js.cjs + '/angular2/test/**/*_spec.js').pipe(transformCJSTests()).pipe(gulp.dest(CONFIG.dest.js.cjs + '/angular2/test/'));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task('build/transpile.dart', transpile(gulp, gulpPlugins, {
|
gulp.task('build/transpile.dart', transpile(gulp, gulpPlugins, {
|
||||||
src: CONFIG.transpile.src.dart,
|
src: CONFIG.transpile.src.dart,
|
||||||
@ -446,6 +474,25 @@ gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
|
|||||||
args: CONFIG.formatDart.args
|
args: CONFIG.formatDart.args
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// ------------
|
||||||
|
// check circular dependencies in Node.js context
|
||||||
|
gulp.task('build/checkCircularDependencies', function (done) {
|
||||||
|
var dependencyObject = madge(CONFIG.dest.js.dev.es6, {
|
||||||
|
format: 'es6',
|
||||||
|
paths: [CONFIG.dest.js.dev.es6],
|
||||||
|
extensions: ['.js', '.es6'],
|
||||||
|
onParseFile: function(data) {
|
||||||
|
data.src = data.src.replace(/import \* as/g, "//import * as");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var circularDependencies = dependencyObject.circular().getArray();
|
||||||
|
if (circularDependencies.length > 0) {
|
||||||
|
console.log(circularDependencies);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
// web servers
|
// web servers
|
||||||
gulp.task('serve.js.dev', jsserve(gulp, gulpPlugins, {
|
gulp.task('serve.js.dev', jsserve(gulp, gulpPlugins, {
|
||||||
@ -481,17 +528,10 @@ gulp.task('serve/benchmarks_external.dart', pubserve(gulp, gulpPlugins, {
|
|||||||
// --------------
|
// --------------
|
||||||
// doc generation
|
// doc generation
|
||||||
var Dgeni = require('dgeni');
|
var Dgeni = require('dgeni');
|
||||||
gulp.task('docs/dgeni', function() {
|
|
||||||
try {
|
|
||||||
var dgeni = new Dgeni([require('./docs/dgeni-package')]);
|
|
||||||
return dgeni.generate();
|
|
||||||
} catch(x) {
|
|
||||||
console.log(x.stack);
|
|
||||||
throw x;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var bower = require('bower');
|
var bower = require('bower');
|
||||||
|
var jasmine = require('gulp-jasmine');
|
||||||
|
var webserver = require('gulp-webserver');
|
||||||
|
|
||||||
gulp.task('docs/bower', function() {
|
gulp.task('docs/bower', function() {
|
||||||
var bowerTask = bower.commands.install(undefined, undefined, { cwd: 'docs' });
|
var bowerTask = bower.commands.install(undefined, undefined, { cwd: 'docs' });
|
||||||
bowerTask.on('log', function (result) {
|
bowerTask.on('log', function (result) {
|
||||||
@ -503,36 +543,54 @@ gulp.task('docs/bower', function() {
|
|||||||
return bowerTask;
|
return bowerTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('docs/assets', ['docs/bower'], function() {
|
|
||||||
return gulp.src('docs/bower_components/**/*')
|
|
||||||
.pipe(gulp.dest('dist/docs/lib'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('docs/app', function() {
|
function createDocsTasks(public) {
|
||||||
return gulp.src('docs/app/**/*')
|
var dgeniPackage = public ? './docs/public-docs-package' : './docs/dgeni-package';
|
||||||
.pipe(gulp.dest('dist/docs'));
|
var distDocsPath = public ? 'dist/public_docs' : 'dist/docs';
|
||||||
});
|
var taskPrefix = public ? 'public_docs' : 'docs';
|
||||||
|
|
||||||
gulp.task('docs', ['docs/assets', 'docs/app', 'docs/dgeni']);
|
gulp.task(taskPrefix + '/dgeni', function() {
|
||||||
gulp.task('docs/watch', function() {
|
try {
|
||||||
return gulp.watch('docs/app/**/*', ['docs/app']);
|
var dgeni = new Dgeni([require(dgeniPackage)]);
|
||||||
});
|
return dgeni.generate();
|
||||||
|
} catch(x) {
|
||||||
|
console.log(x.stack);
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var jasmine = require('gulp-jasmine');
|
gulp.task(taskPrefix + '/assets', ['docs/bower'], function() {
|
||||||
gulp.task('docs/test', function () {
|
return gulp.src('docs/bower_components/**/*')
|
||||||
return gulp.src('docs/**/*.spec.js')
|
.pipe(gulp.dest(distDocsPath + '/lib'));
|
||||||
.pipe(jasmine({
|
});
|
||||||
includeStackTrace: true
|
|
||||||
|
gulp.task(taskPrefix + '/app', function() {
|
||||||
|
return gulp.src('docs/app/**/*')
|
||||||
|
.pipe(gulp.dest(distDocsPath));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(taskPrefix, [taskPrefix + '/assets', taskPrefix + '/app', taskPrefix + '/dgeni']);
|
||||||
|
gulp.task(taskPrefix + '/watch', function() {
|
||||||
|
return gulp.watch('docs/app/**/*', [taskPrefix + '/app']);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(taskPrefix + '/test', function () {
|
||||||
|
return gulp.src('docs/**/*.spec.js')
|
||||||
|
.pipe(jasmine({
|
||||||
|
includeStackTrace: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(taskPrefix + '/serve', function() {
|
||||||
|
gulp.src(distDocsPath + '/')
|
||||||
|
.pipe(webserver({
|
||||||
|
fallback: 'index.html'
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var webserver = require('gulp-webserver');
|
createDocsTasks(true);
|
||||||
gulp.task('docs/serve', function() {
|
createDocsTasks(false);
|
||||||
gulp.src('dist/docs/')
|
|
||||||
.pipe(webserver({
|
|
||||||
fallback: 'index.html'
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
// karma tests
|
// karma tests
|
||||||
@ -556,6 +614,9 @@ gulp.task('test.unit.dart/ci', function (done) {
|
|||||||
karma.start({configFile: __dirname + '/karma-dart.conf.js',
|
karma.start({configFile: __dirname + '/karma-dart.conf.js',
|
||||||
singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done);
|
singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done);
|
||||||
});
|
});
|
||||||
|
gulp.task('test.unit.cjs', function (done) {
|
||||||
|
return gulp.src(CONFIG.test.js.cjs).pipe(jasmine(/*{verbose: true, includeStackTrace: true}*/));
|
||||||
|
});
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
// server tests
|
// server tests
|
||||||
@ -609,6 +670,7 @@ gulp.task('build.dart', function(done) {
|
|||||||
gulp.task('build.js.dev', function(done) {
|
gulp.task('build.js.dev', function(done) {
|
||||||
runSequence(
|
runSequence(
|
||||||
['build/transpile.js.dev', 'build/html.js.dev', 'build/copy.js.dev', 'build/multicopy.js.dev.es6'],
|
['build/transpile.js.dev', 'build/html.js.dev', 'build/copy.js.dev', 'build/multicopy.js.dev.es6'],
|
||||||
|
'build/checkCircularDependencies',
|
||||||
done
|
done
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -624,6 +686,7 @@ gulp.task('build.js.cjs', function(done) {
|
|||||||
runSequence(
|
runSequence(
|
||||||
['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'],
|
['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'],
|
||||||
['build/linknodemodules.js.cjs'],
|
['build/linknodemodules.js.cjs'],
|
||||||
|
'build/transformCJSTests',
|
||||||
done
|
done
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
8
modules/angular2/change_detection.js
vendored
8
modules/angular2/change_detection.js
vendored
@ -1,20 +1,20 @@
|
|||||||
export {AST} from './src/change_detection/parser/ast';
|
export {AST} from './src/change_detection/parser/ast';
|
||||||
export {Lexer} from './src/change_detection/parser/lexer';
|
export {Lexer} from './src/change_detection/parser/lexer';
|
||||||
export {Parser} from './src/change_detection/parser/parser';
|
export {Parser} from './src/change_detection/parser/parser';
|
||||||
export {ContextWithVariableBindings}
|
export {Locals}
|
||||||
from './src/change_detection/parser/context_with_variable_bindings';
|
from './src/change_detection/parser/locals';
|
||||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
|
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
|
||||||
from './src/change_detection/exceptions';
|
from './src/change_detection/exceptions';
|
||||||
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
|
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
|
||||||
CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/change_detection/interfaces';
|
CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/change_detection/interfaces';
|
||||||
export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector, BindingRecord}
|
||||||
from './src/change_detection/proto_change_detector';
|
from './src/change_detection/proto_change_detector';
|
||||||
export {DynamicChangeDetector}
|
export {DynamicChangeDetector}
|
||||||
from './src/change_detection/dynamic_change_detector';
|
from './src/change_detection/dynamic_change_detector';
|
||||||
export * from './src/change_detection/pipes/pipe_registry';
|
export * from './src/change_detection/pipes/pipe_registry';
|
||||||
|
export {uninitialized} from './src/change_detection/change_detection_util';
|
||||||
export * from './src/change_detection/pipes/pipe';
|
export * from './src/change_detection/pipes/pipe';
|
||||||
|
|
||||||
|
|
||||||
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
||||||
from './src/change_detection/proto_change_detector';
|
from './src/change_detection/proto_change_detector';
|
||||||
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
||||||
|
2
modules/angular2/di.js
vendored
2
modules/angular2/di.js
vendored
@ -1,4 +1,4 @@
|
|||||||
export {Inject, InjectPromise, InjectLazy, Optional, DependencyAnnotation} from './src/di/annotations';
|
export {Inject, InjectPromise, InjectLazy, Injectable, Optional, DependencyAnnotation} from './src/di/annotations';
|
||||||
export {Injector} from './src/di/injector';
|
export {Injector} from './src/di/injector';
|
||||||
export {Binding, Dependency, bind} from './src/di/binding';
|
export {Binding, Dependency, bind} from './src/di/binding';
|
||||||
export {Key, KeyRegistry} from './src/di/key';
|
export {Key, KeyRegistry} from './src/di/key';
|
||||||
|
@ -220,7 +220,7 @@ To better understand the kinds of injections which are supported in Angular we h
|
|||||||
|
|
||||||
### Injecting Services
|
### Injecting Services
|
||||||
|
|
||||||
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `componentServices` and then letting the directive ask for the configured service.
|
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `services` and then letting the directive ask for the configured service.
|
||||||
|
|
||||||
This example illustrates how to inject `MyService` into `House` directive.
|
This example illustrates how to inject `MyService` into `House` directive.
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ class MyService {} | Assume a service which needs to be inject
|
|||||||
|
|
|
|
||||||
@Component({ | Assume a top level application component which
|
@Component({ | Assume a top level application component which
|
||||||
selector: 'my-app', | configures the services to be injected.
|
selector: 'my-app', | configures the services to be injected.
|
||||||
componentServices: [MyService] |
|
services: [MyService] |
|
||||||
}) |
|
}) |
|
||||||
@Template({ | Assume we have a template that needs to be
|
@Template({ | Assume we have a template that needs to be
|
||||||
url: 'my_app.html', | configured with directives to be injected.
|
url: 'my_app.html', | configured with directives to be injected.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Zones
|
# Zones
|
||||||
|
|
||||||
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for
|
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for
|
||||||
JavaScript. Zones are used to intercept all async operation callbacks in the browser. By intercepting async
|
JavaScript. Zones are used to intercept all async operation callbacks in the browser. By intercepting async
|
||||||
callbacks angular can automatically execute the change detection at the end of the VM turn to update the application
|
callbacks Angular can automatically execute the change detection at the end of the VM turn to update the application
|
||||||
UI bindings. Zones means that in Angular v2 you don't have to remember to call `rootScope.$apply()` in your async call.
|
UI bindings. Zones means that in Angular v2 you don't have to remember to call `rootScope.$apply()` in your async call.
|
||||||
|
|
||||||
## Execution Context
|
## Execution Context
|
||||||
@ -55,8 +55,8 @@ execution which was registered in the `run` block.
|
|||||||
|
|
||||||
## Putting it all together in Angular
|
## Putting it all together in Angular
|
||||||
|
|
||||||
In Angular2 it is not necessary to notify Angular of changes manually after async callback, because angular relevant
|
In Angular2 it is not necessary to notify Angular of changes manually after async callback, because a relevant
|
||||||
async callbacks are intercepted. The question is how do we know which callbacks are angular relevant?
|
async callbacks are intercepted. The question is how do we know which callbacks are Angular relevant?
|
||||||
|
|
||||||
```
|
```
|
||||||
/// Some other code running on page can do async operation
|
/// Some other code running on page can do async operation
|
||||||
@ -97,9 +97,9 @@ Mouse clicked.
|
|||||||
ANGULAR AUTO-DIGEST!
|
ANGULAR AUTO-DIGEST!
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice how the place where the listener was register will effect weather or not angular will be notified of the
|
Notice how the place where the listener was registered will effect whether or not Angular will be notified of the
|
||||||
async call and cause a change detection to run to update the UI.
|
async call and cause a change detection to run to update the UI.
|
||||||
|
|
||||||
Being able to globally intercept the async operation is important to have a seamless integration with all existing
|
Being able to globally intercept the async operation is important to have a seamless integration with all existing
|
||||||
libraries. But it is equally important to be able to differentiate between Angular and none Angular code running
|
libraries. But it is equally important to be able to differentiate between Angular and non-Angular code running
|
||||||
on the same page concurrently.
|
on the same page concurrently.
|
||||||
|
3
modules/angular2/forms.js
vendored
3
modules/angular2/forms.js
vendored
@ -1,4 +1,5 @@
|
|||||||
export * from './src/forms/model';
|
export * from './src/forms/model';
|
||||||
export * from './src/forms/directives';
|
export * from './src/forms/directives';
|
||||||
export * from './src/forms/validators';
|
export * from './src/forms/validators';
|
||||||
export * from './src/forms/validator_directives';
|
export * from './src/forms/validator_directives';
|
||||||
|
export * from './src/forms/form_builder';
|
@ -16,4 +16,4 @@ dependencies:
|
|||||||
html5lib: '^0.12.0'
|
html5lib: '^0.12.0'
|
||||||
stack_trace: '^1.1.1'
|
stack_trace: '^1.1.1'
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
guinness: "^0.1.16"
|
guinness: "^0.1.17"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
import {ChangeDetectionUtil} from './change_detection_util';
|
||||||
|
|
||||||
@ -9,6 +8,7 @@ import {
|
|||||||
ProtoRecord,
|
ProtoRecord,
|
||||||
RECORD_TYPE_SELF,
|
RECORD_TYPE_SELF,
|
||||||
RECORD_TYPE_PROPERTY,
|
RECORD_TYPE_PROPERTY,
|
||||||
|
RECORD_TYPE_LOCAL,
|
||||||
RECORD_TYPE_INVOKE_METHOD,
|
RECORD_TYPE_INVOKE_METHOD,
|
||||||
RECORD_TYPE_CONST,
|
RECORD_TYPE_CONST,
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
@ -44,13 +44,7 @@ import {
|
|||||||
* var temp;
|
* var temp;
|
||||||
* var context = this.context;
|
* var context = this.context;
|
||||||
*
|
*
|
||||||
* temp = ChangeDetectionUtil.findContext("address", context);
|
* address0 = context.address;
|
||||||
* if (temp instanceof ContextWithVariableBindings) {
|
|
||||||
* address0 = temp.get('address');
|
|
||||||
* } else {
|
|
||||||
* address0 = temp.address;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* if (address0 !== this.address0) {
|
* if (address0 !== this.address0) {
|
||||||
* this.address0 = address0;
|
* this.address0 = address0;
|
||||||
* }
|
* }
|
||||||
@ -70,14 +64,16 @@ import {
|
|||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ChangeDetector0.prototype.hydrate = function(context) {
|
* ChangeDetector0.prototype.hydrate = function(context, locals) {
|
||||||
* this.context = context;
|
* this.context = context;
|
||||||
|
* this.locals = locals;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* ChangeDetector0.prototype.dehydrate = function(context) {
|
* ChangeDetector0.prototype.dehydrate = function(context) {
|
||||||
* this.context = ChangeDetectionUtil.unitialized();
|
* this.context = ChangeDetectionUtil.unitialized();
|
||||||
* this.address0 = ChangeDetectionUtil.unitialized();
|
* this.address0 = ChangeDetectionUtil.unitialized();
|
||||||
* this.city1 = ChangeDetectionUtil.unitialized();
|
* this.city1 = ChangeDetectionUtil.unitialized();
|
||||||
|
* this.locals = null;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* ChangeDetector0.prototype.hydrated = function() {
|
* ChangeDetector0.prototype.hydrated = function() {
|
||||||
@ -99,8 +95,10 @@ var UTIL = "ChangeDetectionUtil";
|
|||||||
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
||||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||||
var PROTOS_ACCESSOR = "this.protos";
|
var PROTOS_ACCESSOR = "this.protos";
|
||||||
|
var CONTEXT_ACCESSOR = "this.context";
|
||||||
var CHANGE_LOCAL = "change";
|
var CHANGE_LOCAL = "change";
|
||||||
var CHANGES_LOCAL = "changes";
|
var CHANGES_LOCAL = "changes";
|
||||||
|
var LOCALS_ACCESSOR = "this.locals";
|
||||||
var TEMP_LOCAL = "temp";
|
var TEMP_LOCAL = "temp";
|
||||||
|
|
||||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
||||||
@ -135,15 +133,17 @@ function pipeOnDestroyTemplate(pipeNames:List) {
|
|||||||
|
|
||||||
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
|
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
|
||||||
return `
|
return `
|
||||||
${type}.prototype.hydrate = function(context) {
|
${type}.prototype.hydrate = function(context, locals) {
|
||||||
this.context = context;
|
${CONTEXT_ACCESSOR} = context;
|
||||||
|
${LOCALS_ACCESSOR} = locals;
|
||||||
}
|
}
|
||||||
${type}.prototype.dehydrate = function() {
|
${type}.prototype.dehydrate = function() {
|
||||||
${pipeOnDestroy}
|
${pipeOnDestroy}
|
||||||
${fieldsDefinitions}
|
${fieldsDefinitions}
|
||||||
|
${LOCALS_ACCESSOR} = null;
|
||||||
}
|
}
|
||||||
${type}.prototype.hydrated = function() {
|
${type}.prototype.hydrated = function() {
|
||||||
return this.context !== ${UTIL}.unitialized();
|
return ${CONTEXT_ACCESSOR} !== ${UTIL}.unitialized();
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ var ${TEMP_LOCAL};
|
|||||||
var ${CHANGE_LOCAL};
|
var ${CHANGE_LOCAL};
|
||||||
var ${CHANGES_LOCAL} = null;
|
var ${CHANGES_LOCAL} = null;
|
||||||
|
|
||||||
context = this.context;
|
context = ${CONTEXT_ACCESSOR};
|
||||||
${records}
|
${records}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -216,28 +216,6 @@ function assignmentTemplate(field:string, value:string) {
|
|||||||
return `${field} = ${value};`;
|
return `${field} = ${value};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function propertyReadTemplate(name:string, context:string, newValue:string) {
|
|
||||||
return `
|
|
||||||
${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context});
|
|
||||||
if (${TEMP_LOCAL} instanceof ContextWithVariableBindings) {
|
|
||||||
${newValue} = ${TEMP_LOCAL}.get('${name}');
|
|
||||||
} else {
|
|
||||||
${newValue} = ${TEMP_LOCAL}.${name};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function invokeMethodTemplate(name:string, args:string, context:string, newValue:string) {
|
|
||||||
return `
|
|
||||||
${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context});
|
|
||||||
if (${TEMP_LOCAL} instanceof ContextWithVariableBindings) {
|
|
||||||
${newValue} = ${TEMP_LOCAL}.get('${name}').apply(null, [${args}]);
|
|
||||||
} else {
|
|
||||||
${newValue} = ${context}.${name}(${args});
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function localDefinitionsTemplate(names:List):string {
|
function localDefinitionsTemplate(names:List):string {
|
||||||
return names.map((n) => `var ${n};`).join("\n");
|
return names.map((n) => `var ${n};`).join("\n");
|
||||||
}
|
}
|
||||||
@ -306,7 +284,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
|
|
||||||
generate():Function {
|
generate():Function {
|
||||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
|
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
|
||||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'ContextWithVariableBindings', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, ContextWithVariableBindings, this.records);
|
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records);
|
||||||
}
|
}
|
||||||
|
|
||||||
genConstructor():string {
|
genConstructor():string {
|
||||||
@ -403,18 +381,13 @@ export class ChangeDetectorJITGenerator {
|
|||||||
return `${newValue} = ${this.genLiteral(r.funcOrValue)}`;
|
return `${newValue} = ${this.genLiteral(r.funcOrValue)}`;
|
||||||
|
|
||||||
case RECORD_TYPE_PROPERTY:
|
case RECORD_TYPE_PROPERTY:
|
||||||
if (r.contextIndex == 0) { // only the first property read can be a local
|
return assignmentTemplate(newValue, `${context}.${r.name}`);
|
||||||
return propertyReadTemplate(r.name, context, newValue);
|
|
||||||
} else {
|
case RECORD_TYPE_LOCAL:
|
||||||
return assignmentTemplate(newValue, `${context}.${r.name}`);
|
return assignmentTemplate(newValue, `${LOCALS_ACCESSOR}.get('${r.name}')`);
|
||||||
}
|
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_METHOD:
|
case RECORD_TYPE_INVOKE_METHOD:
|
||||||
if (r.contextIndex == 0) { // only the first property read can be a local
|
return assignmentTemplate(newValue, `${context}.${r.name}(${args})`);
|
||||||
return invokeMethodTemplate(r.name, args, context, newValue);
|
|
||||||
} else {
|
|
||||||
return assignmentTemplate(newValue, `${context}.${r.name}(${args})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_CLOSURE:
|
case RECORD_TYPE_INVOKE_CLOSURE:
|
||||||
return assignmentTemplate(newValue, `${context}(${args})`);
|
return assignmentTemplate(newValue, `${context}(${args})`);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
|
||||||
import {ProtoRecord} from './proto_record';
|
import {ProtoRecord} from './proto_record';
|
||||||
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
||||||
import {NO_CHANGE} from './pipes/pipe';
|
import {NO_CHANGE} from './pipes/pipe';
|
||||||
@ -144,16 +143,6 @@ export class ChangeDetectionUtil {
|
|||||||
return obj[args[0]];
|
return obj[args[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
static findContext(name:string, c){
|
|
||||||
while (c instanceof ContextWithVariableBindings) {
|
|
||||||
if (c.hasBinding(name)) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
c = c.parent;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static noChangeMarker(value):boolean {
|
static noChangeMarker(value):boolean {
|
||||||
return value === NO_CHANGE;
|
return value === NO_CHANGE;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
|
||||||
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
@ -11,6 +10,7 @@ import {
|
|||||||
ProtoRecord,
|
ProtoRecord,
|
||||||
RECORD_TYPE_SELF,
|
RECORD_TYPE_SELF,
|
||||||
RECORD_TYPE_PROPERTY,
|
RECORD_TYPE_PROPERTY,
|
||||||
|
RECORD_TYPE_LOCAL,
|
||||||
RECORD_TYPE_INVOKE_METHOD,
|
RECORD_TYPE_INVOKE_METHOD,
|
||||||
RECORD_TYPE_CONST,
|
RECORD_TYPE_CONST,
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
@ -26,6 +26,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
dispatcher:any;
|
dispatcher:any;
|
||||||
pipeRegistry;
|
pipeRegistry;
|
||||||
|
|
||||||
|
locals:any;
|
||||||
values:List;
|
values:List;
|
||||||
changes:List;
|
changes:List;
|
||||||
pipes:List;
|
pipes:List;
|
||||||
@ -47,12 +48,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
ListWrapper.fill(this.pipes, null);
|
ListWrapper.fill(this.pipes, null);
|
||||||
ListWrapper.fill(this.prevContexts, uninitialized);
|
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||||
ListWrapper.fill(this.changes, false);
|
ListWrapper.fill(this.changes, false);
|
||||||
|
this.locals = null;
|
||||||
|
|
||||||
this.protos = protoRecords;
|
this.protos = protoRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(context:any) {
|
hydrate(context:any, locals:any) {
|
||||||
this.values[0] = context;
|
this.values[0] = context;
|
||||||
|
this.locals = locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
dehydrate() {
|
dehydrate() {
|
||||||
@ -61,6 +64,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
ListWrapper.fill(this.changes, false);
|
ListWrapper.fill(this.changes, false);
|
||||||
ListWrapper.fill(this.pipes, null);
|
ListWrapper.fill(this.pipes, null);
|
||||||
ListWrapper.fill(this.prevContexts, uninitialized);
|
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||||
|
this.locals = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyPipes() {
|
_destroyPipes() {
|
||||||
@ -143,26 +147,15 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
|
|
||||||
case RECORD_TYPE_PROPERTY:
|
case RECORD_TYPE_PROPERTY:
|
||||||
var context = this._readContext(proto);
|
var context = this._readContext(proto);
|
||||||
var c = ChangeDetectionUtil.findContext(proto.name, context);
|
return proto.funcOrValue(context);
|
||||||
if (c instanceof ContextWithVariableBindings) {
|
|
||||||
return c.get(proto.name);
|
case RECORD_TYPE_LOCAL:
|
||||||
} else {
|
return this.locals.get(proto.name);
|
||||||
var propertyGetter:Function = proto.funcOrValue;
|
|
||||||
return propertyGetter(c);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_METHOD:
|
case RECORD_TYPE_INVOKE_METHOD:
|
||||||
var context = this._readContext(proto);
|
var context = this._readContext(proto);
|
||||||
var args = this._readArgs(proto);
|
var args = this._readArgs(proto);
|
||||||
var c = ChangeDetectionUtil.findContext(proto.name, context);
|
return proto.funcOrValue(context, args);
|
||||||
if (c instanceof ContextWithVariableBindings) {
|
|
||||||
return FunctionWrapper.apply(c.get(proto.name), args);
|
|
||||||
} else {
|
|
||||||
var methodInvoker:Function = proto.funcOrValue;
|
|
||||||
return methodInvoker(c, args);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECORD_TYPE_KEYED_ACCESS:
|
case RECORD_TYPE_KEYED_ACCESS:
|
||||||
var arg = this._readArgs(proto)[0];
|
var arg = this._readArgs(proto)[0];
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {List} from 'angular2/src/facade/collection';
|
import {List} from 'angular2/src/facade/collection';
|
||||||
|
import {Locals} from './parser/locals';
|
||||||
|
|
||||||
export class ChangeRecord {
|
export class ChangeRecord {
|
||||||
bindingMemento:any;
|
bindingMemento:any;
|
||||||
@ -55,7 +56,7 @@ export class ChangeDetector {
|
|||||||
addChild(cd:ChangeDetector) {}
|
addChild(cd:ChangeDetector) {}
|
||||||
removeChild(cd:ChangeDetector) {}
|
removeChild(cd:ChangeDetector) {}
|
||||||
remove() {}
|
remove() {}
|
||||||
hydrate(context:any) {}
|
hydrate(context:any, locals:Locals) {}
|
||||||
dehydrate() {}
|
dehydrate() {}
|
||||||
markPathToRootAsCheckOnce() {}
|
markPathToRootAsCheckOnce() {}
|
||||||
|
|
||||||
|
146
modules/angular2/src/change_detection/parser/ast.js
vendored
146
modules/angular2/src/change_detection/parser/ast.js
vendored
@ -1,9 +1,8 @@
|
|||||||
import {FIELD, autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
|
import {autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
|
||||||
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
|
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
|
||||||
import {ContextWithVariableBindings} from "./context_with_variable_bindings";
|
|
||||||
|
|
||||||
export class AST {
|
export class AST {
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
throw new BaseException("Not supported");
|
throw new BaseException("Not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,7 +10,7 @@ export class AST {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
assign(context, value) {
|
assign(context, locals, value) {
|
||||||
throw new BaseException("Not supported");
|
throw new BaseException("Not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ export class AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class EmptyExpr extends AST {
|
export class EmptyExpr extends AST {
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ export class EmptyExpr extends AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ImplicitReceiver extends AST {
|
export class ImplicitReceiver extends AST {
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,10 +52,10 @@ export class Chain extends AST {
|
|||||||
this.expressions = expressions;
|
this.expressions = expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var result;
|
var result;
|
||||||
for (var i = 0; i < this.expressions.length; i++) {
|
for (var i = 0; i < this.expressions.length; i++) {
|
||||||
var last = this.expressions[i].eval(context);
|
var last = this.expressions[i].eval(context, locals);
|
||||||
if (isPresent(last)) result = last;
|
if (isPresent(last)) result = last;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -78,11 +77,11 @@ export class Conditional extends AST {
|
|||||||
this.falseExp = falseExp;
|
this.falseExp = falseExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
if(this.condition.eval(context)) {
|
if(this.condition.eval(context, locals)) {
|
||||||
return this.trueExp.eval(context);
|
return this.trueExp.eval(context, locals);
|
||||||
} else {
|
} else {
|
||||||
return this.falseExp.eval(context);
|
return this.falseExp.eval(context, locals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,34 +103,29 @@ export class AccessMember extends AST {
|
|||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var evaluatedContext = this.receiver.eval(context);
|
if (this.receiver instanceof ImplicitReceiver &&
|
||||||
|
isPresent(locals) && locals.contains(this.name)) {
|
||||||
while (evaluatedContext instanceof ContextWithVariableBindings) {
|
return locals.get(this.name);
|
||||||
if (evaluatedContext.hasBinding(this.name)) {
|
} else {
|
||||||
return evaluatedContext.get(this.name);
|
var evaluatedReceiver = this.receiver.eval(context, locals);
|
||||||
}
|
return this.getter(evaluatedReceiver);
|
||||||
evaluatedContext = evaluatedContext.parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getter(evaluatedContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAssignable() {
|
get isAssignable() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assign(context, value) {
|
assign(context, locals, value) {
|
||||||
var evaluatedContext = this.receiver.eval(context);
|
var evaluatedContext = this.receiver.eval(context, locals);
|
||||||
|
|
||||||
while (evaluatedContext instanceof ContextWithVariableBindings) {
|
if (this.receiver instanceof ImplicitReceiver &&
|
||||||
if (evaluatedContext.hasBinding(this.name)) {
|
isPresent(locals) && locals.contains(this.name)) {
|
||||||
throw new BaseException(`Cannot reassign a variable binding ${this.name}`)
|
throw new BaseException(`Cannot reassign a variable binding ${this.name}`);
|
||||||
}
|
} else {
|
||||||
evaluatedContext = evaluatedContext.parent;
|
return this.setter(evaluatedContext, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.setter(evaluatedContext, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -148,9 +142,9 @@ export class KeyedAccess extends AST {
|
|||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var obj = this.obj.eval(context);
|
var obj = this.obj.eval(context, locals);
|
||||||
var key = this.key.eval(context);
|
var key = this.key.eval(context, locals);
|
||||||
return obj[key];
|
return obj[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,9 +152,9 @@ export class KeyedAccess extends AST {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assign(context, value) {
|
assign(context, locals, value) {
|
||||||
var obj = this.obj.eval(context);
|
var obj = this.obj.eval(context, locals);
|
||||||
var key = this.key.eval(context);
|
var key = this.key.eval(context, locals);
|
||||||
obj[key] = value;
|
obj[key] = value;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -193,7 +187,7 @@ export class LiteralPrimitive extends AST {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,8 +203,8 @@ export class LiteralArray extends AST {
|
|||||||
this.expressions = expressions;
|
this.expressions = expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return ListWrapper.map(this.expressions, (e) => e.eval(context));
|
return ListWrapper.map(this.expressions, (e) => e.eval(context, locals));
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -227,10 +221,10 @@ export class LiteralMap extends AST {
|
|||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var res = StringMapWrapper.create();
|
var res = StringMapWrapper.create();
|
||||||
for(var i = 0; i < this.keys.length; ++i) {
|
for(var i = 0; i < this.keys.length; ++i) {
|
||||||
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context));
|
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -249,7 +243,7 @@ export class Interpolation extends AST {
|
|||||||
this.expressions = expressions;
|
this.expressions = expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
throw new BaseException("evaluating an Interpolation is not supported");
|
throw new BaseException("evaluating an Interpolation is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,13 +263,13 @@ export class Binary extends AST {
|
|||||||
this.right = right;
|
this.right = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var left = this.left.eval(context);
|
var left = this.left.eval(context, locals);
|
||||||
switch (this.operation) {
|
switch (this.operation) {
|
||||||
case '&&': return left && this.right.eval(context);
|
case '&&': return left && this.right.eval(context, locals);
|
||||||
case '||': return left || this.right.eval(context);
|
case '||': return left || this.right.eval(context, locals);
|
||||||
}
|
}
|
||||||
var right = this.right.eval(context);
|
var right = this.right.eval(context, locals);
|
||||||
|
|
||||||
switch (this.operation) {
|
switch (this.operation) {
|
||||||
case '+' : return left + right;
|
case '+' : return left + right;
|
||||||
@ -307,8 +301,8 @@ export class PrefixNot extends AST {
|
|||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return !this.expression.eval(context);
|
return !this.expression.eval(context, locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -325,8 +319,8 @@ export class Assignment extends AST {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return this.target.assign(context, this.value.eval(context));
|
return this.target.assign(context, locals, this.value.eval(context, locals));
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -347,19 +341,16 @@ export class MethodCall extends AST {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var evaluatedContext = this.receiver.eval(context);
|
var evaluatedArgs = evalList(context, locals, this.args);
|
||||||
var evaluatedArgs = evalList(context, this.args);
|
if (this.receiver instanceof ImplicitReceiver &&
|
||||||
|
isPresent(locals) && locals.contains(this.name)) {
|
||||||
while (evaluatedContext instanceof ContextWithVariableBindings) {
|
var fn = locals.get(this.name);
|
||||||
if (evaluatedContext.hasBinding(this.name)) {
|
return FunctionWrapper.apply(fn, evaluatedArgs);
|
||||||
var fn = evaluatedContext.get(this.name);
|
} else {
|
||||||
return FunctionWrapper.apply(fn, evaluatedArgs);
|
var evaluatedReceiver = this.receiver.eval(context, locals);
|
||||||
}
|
return this.fn(evaluatedReceiver, evaluatedArgs);
|
||||||
evaluatedContext = evaluatedContext.parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.fn(evaluatedContext, evaluatedArgs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -376,12 +367,12 @@ export class FunctionCall extends AST {
|
|||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
var obj = this.target.eval(context);
|
var obj = this.target.eval(context, locals);
|
||||||
if (! (obj instanceof Function)) {
|
if (! (obj instanceof Function)) {
|
||||||
throw new BaseException(`${obj} is not a function`);
|
throw new BaseException(`${obj} is not a function`);
|
||||||
}
|
}
|
||||||
return FunctionWrapper.apply(obj, evalList(context, this.args));
|
return FunctionWrapper.apply(obj, evalList(context, locals, this.args));
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -400,16 +391,16 @@ export class ASTWithSource extends AST {
|
|||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context, locals) {
|
||||||
return this.ast.eval(context);
|
return this.ast.eval(context, locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAssignable() {
|
get isAssignable() {
|
||||||
return this.ast.isAssignable;
|
return this.ast.isAssignable;
|
||||||
}
|
}
|
||||||
|
|
||||||
assign(context, value) {
|
assign(context, locals, value) {
|
||||||
return this.ast.assign(context, value);
|
return this.ast.assign(context, locals, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
@ -454,12 +445,19 @@ export class AstVisitor {
|
|||||||
visitPrefixNot(ast:PrefixNot) {}
|
visitPrefixNot(ast:PrefixNot) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0]];
|
var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0],
|
||||||
function evalList(context, exps:List){
|
[0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0,0,0,0]];
|
||||||
|
|
||||||
|
function evalList(context, locals, exps:List){
|
||||||
var length = exps.length;
|
var length = exps.length;
|
||||||
|
if (length > 10) {
|
||||||
|
throw new BaseException("Cannot have more than 10 argument");
|
||||||
|
}
|
||||||
|
|
||||||
var result = _evalListCache[length];
|
var result = _evalListCache[length];
|
||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
result[i] = exps[i].eval(context);
|
result[i] = exps[i].eval(context, locals);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {BaseException} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
export class ContextWithVariableBindings {
|
|
||||||
parent:any;
|
|
||||||
/// varBindings' keys are read-only. adding/removing keys is not supported.
|
|
||||||
varBindings:Map;
|
|
||||||
|
|
||||||
constructor(parent:any, varBindings:Map) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.varBindings = varBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasBinding(name:string):boolean {
|
|
||||||
return MapWrapper.contains(this.varBindings, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(name:string) {
|
|
||||||
return MapWrapper.get(this.varBindings, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(name:string, value) {
|
|
||||||
// TODO(rado): consider removing this check if we can guarantee this is not
|
|
||||||
// exposed to the public API.
|
|
||||||
if (this.hasBinding(name)) {
|
|
||||||
MapWrapper.set(this.varBindings, name, value);
|
|
||||||
} else {
|
|
||||||
throw new BaseException(
|
|
||||||
'VariableBindings do not support setting of new keys post-construction.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearValues() {
|
|
||||||
MapWrapper.clearValues(this.varBindings);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
|
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
|
||||||
import {int, FIELD, NumberWrapper, StringJoiner, StringWrapper} from "angular2/src/facade/lang";
|
import {int, NumberWrapper, StringJoiner, StringWrapper} from "angular2/src/facade/lang";
|
||||||
|
|
||||||
export const TOKEN_TYPE_CHARACTER = 1;
|
export const TOKEN_TYPE_CHARACTER = 1;
|
||||||
export const TOKEN_TYPE_IDENTIFIER = 2;
|
export const TOKEN_TYPE_IDENTIFIER = 2;
|
||||||
@ -8,6 +9,7 @@ export const TOKEN_TYPE_STRING = 4;
|
|||||||
export const TOKEN_TYPE_OPERATOR = 5;
|
export const TOKEN_TYPE_OPERATOR = 5;
|
||||||
export const TOKEN_TYPE_NUMBER = 6;
|
export const TOKEN_TYPE_NUMBER = 6;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
text:string;
|
text:string;
|
||||||
tokenize(text:string):List {
|
tokenize(text:string):List {
|
||||||
|
51
modules/angular2/src/change_detection/parser/locals.js
vendored
Normal file
51
modules/angular2/src/change_detection/parser/locals.js
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
export class Locals {
|
||||||
|
parent:Locals;
|
||||||
|
current:Map;
|
||||||
|
|
||||||
|
constructor(parent:Locals, current:Map) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.current = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(name:string):boolean {
|
||||||
|
if (MapWrapper.contains(this.current, name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(this.parent)) {
|
||||||
|
return this.parent.contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name:string) {
|
||||||
|
if (MapWrapper.contains(this.current, name)) {
|
||||||
|
return MapWrapper.get(this.current, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(this.parent)) {
|
||||||
|
return this.parent.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BaseException(`Cannot find '${name}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name:string, value) {
|
||||||
|
// TODO(rado): consider removing this check if we can guarantee this is not
|
||||||
|
// exposed to the public API.
|
||||||
|
// TODO: vsavkin maybe it should check only the local map
|
||||||
|
if (MapWrapper.contains(this.current, name)) {
|
||||||
|
MapWrapper.set(this.current, name, value);
|
||||||
|
} else {
|
||||||
|
throw new BaseException('Setting of new keys post-construction is not supported.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearValues() {
|
||||||
|
MapWrapper.clearValues(this.current);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import {FIELD, int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
|
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
|
||||||
$COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
|
$COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
|
||||||
@ -32,6 +33,7 @@ var _implicitReceiver = new ImplicitReceiver();
|
|||||||
var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
||||||
var QUOTE_REGEXP = RegExpWrapper.create("'");
|
var QUOTE_REGEXP = RegExpWrapper.create("'");
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class Parser {
|
export class Parser {
|
||||||
_lexer:Lexer;
|
_lexer:Lexer;
|
||||||
_reflector:Reflector;
|
_reflector:Reflector;
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
ProtoRecord,
|
ProtoRecord,
|
||||||
RECORD_TYPE_SELF,
|
RECORD_TYPE_SELF,
|
||||||
RECORD_TYPE_PROPERTY,
|
RECORD_TYPE_PROPERTY,
|
||||||
|
RECORD_TYPE_LOCAL,
|
||||||
RECORD_TYPE_INVOKE_METHOD,
|
RECORD_TYPE_INVOKE_METHOD,
|
||||||
RECORD_TYPE_CONST,
|
RECORD_TYPE_CONST,
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
@ -45,36 +46,44 @@ import {
|
|||||||
|
|
||||||
export class ProtoChangeDetector {
|
export class ProtoChangeDetector {
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
||||||
instantiate(dispatcher:any):ChangeDetector{
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BindingRecord {
|
||||||
|
ast:AST;
|
||||||
|
bindingMemento:any;
|
||||||
|
directiveMemento:any;
|
||||||
|
|
||||||
|
constructor(ast:AST, bindingMemento:any, directiveMemento:any) {
|
||||||
|
this.ast = ast;
|
||||||
|
this.bindingMemento = bindingMemento;
|
||||||
|
this.directiveMemento = directiveMemento;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
_records:List<ProtoRecord>;
|
|
||||||
_recordBuilder:ProtoRecordBuilder;
|
|
||||||
_pipeRegistry:PipeRegistry;
|
_pipeRegistry:PipeRegistry;
|
||||||
|
_records:List<ProtoRecord>;
|
||||||
|
|
||||||
constructor(pipeRegistry:PipeRegistry) {
|
constructor(pipeRegistry:PipeRegistry) {
|
||||||
super();
|
super();
|
||||||
this._pipeRegistry = pipeRegistry;
|
this._pipeRegistry = pipeRegistry;
|
||||||
this._records = null;
|
|
||||||
this._recordBuilder = new ProtoRecordBuilder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
this._createRecordsIfNecessary(bindingRecords, variableBindings);
|
||||||
}
|
|
||||||
|
|
||||||
instantiate(dispatcher:any) {
|
|
||||||
this._createRecordsIfNecessary();
|
|
||||||
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createRecordsIfNecessary() {
|
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||||
if (isBlank(this._records)) {
|
if (isBlank(this._records)) {
|
||||||
var records = this._recordBuilder.records;
|
var recordBuilder = new ProtoRecordBuilder();
|
||||||
this._records = coalesce(records);
|
ListWrapper.forEach(bindingRecords, (r) => {
|
||||||
|
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||||
|
});
|
||||||
|
this._records = coalesce(recordBuilder.records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,29 +91,27 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
|||||||
var _jitProtoChangeDetectorClassCounter:number = 0;
|
var _jitProtoChangeDetectorClassCounter:number = 0;
|
||||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
_factory:Function;
|
_factory:Function;
|
||||||
_recordBuilder:ProtoRecordBuilder;
|
|
||||||
_pipeRegistry;
|
_pipeRegistry;
|
||||||
|
|
||||||
constructor(pipeRegistry) {
|
constructor(pipeRegistry) {
|
||||||
super();
|
super();
|
||||||
this._pipeRegistry = pipeRegistry;
|
this._pipeRegistry = pipeRegistry;
|
||||||
this._factory = null;
|
this._factory = null;
|
||||||
this._recordBuilder = new ProtoRecordBuilder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
this._createFactoryIfNecessary(bindingRecords, variableBindings);
|
||||||
}
|
|
||||||
|
|
||||||
instantiate(dispatcher:any) {
|
|
||||||
this._createFactoryIfNecessary();
|
|
||||||
return this._factory(dispatcher, this._pipeRegistry);
|
return this._factory(dispatcher, this._pipeRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createFactoryIfNecessary() {
|
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||||
if (isBlank(this._factory)) {
|
if (isBlank(this._factory)) {
|
||||||
|
var recordBuilder = new ProtoRecordBuilder();
|
||||||
|
ListWrapper.forEach(bindingRecords, (r) => {
|
||||||
|
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||||
|
});
|
||||||
var c = _jitProtoChangeDetectorClassCounter++;
|
var c = _jitProtoChangeDetectorClassCounter++;
|
||||||
var records = coalesce(this._recordBuilder.records);
|
var records = coalesce(recordBuilder.records);
|
||||||
var typeName = `ChangeDetector${c}`;
|
var typeName = `ChangeDetector${c}`;
|
||||||
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
|
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
|
||||||
}
|
}
|
||||||
@ -118,13 +125,13 @@ class ProtoRecordBuilder {
|
|||||||
this.records = [];
|
this.records = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
|
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, variableBindings:List = null) {
|
||||||
var last = ListWrapper.last(this.records);
|
var last = ListWrapper.last(this.records);
|
||||||
if (isPresent(last) && last.directiveMemento == directiveMemento) {
|
if (isPresent(last) && last.directiveMemento == directiveMemento) {
|
||||||
last.lastInDirective = false;
|
last.lastInDirective = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length);
|
var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length, variableBindings);
|
||||||
if (! ListWrapper.isEmpty(pr)) {
|
if (! ListWrapper.isEmpty(pr)) {
|
||||||
var last = ListWrapper.last(pr);
|
var last = ListWrapper.last(pr);
|
||||||
last.lastInBinding = true;
|
last.lastInBinding = true;
|
||||||
@ -139,19 +146,21 @@ class _ConvertAstIntoProtoRecords {
|
|||||||
protoRecords:List;
|
protoRecords:List;
|
||||||
bindingMemento:any;
|
bindingMemento:any;
|
||||||
directiveMemento:any;
|
directiveMemento:any;
|
||||||
|
variableBindings:List;
|
||||||
contextIndex:number;
|
contextIndex:number;
|
||||||
expressionAsString:string;
|
expressionAsString:string;
|
||||||
|
|
||||||
constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string) {
|
constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string, variableBindings:List) {
|
||||||
this.protoRecords = [];
|
this.protoRecords = [];
|
||||||
this.bindingMemento = bindingMemento;
|
this.bindingMemento = bindingMemento;
|
||||||
this.directiveMemento = directiveMemento;
|
this.directiveMemento = directiveMemento;
|
||||||
this.contextIndex = contextIndex;
|
this.contextIndex = contextIndex;
|
||||||
this.expressionAsString = expressionAsString;
|
this.expressionAsString = expressionAsString;
|
||||||
|
this.variableBindings = variableBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number) {
|
static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number, variableBindings:List) {
|
||||||
var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString());
|
var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString(), variableBindings);
|
||||||
ast.visit(c);
|
ast.visit(c);
|
||||||
return c.protoRecords;
|
return c.protoRecords;
|
||||||
}
|
}
|
||||||
@ -172,13 +181,22 @@ class _ConvertAstIntoProtoRecords {
|
|||||||
|
|
||||||
visitAccessMember(ast:AccessMember) {
|
visitAccessMember(ast:AccessMember) {
|
||||||
var receiver = ast.receiver.visit(this);
|
var receiver = ast.receiver.visit(this);
|
||||||
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
|
||||||
|
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
||||||
|
} else {
|
||||||
|
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitMethodCall(ast:MethodCall) {
|
visitMethodCall(ast:MethodCall) {;
|
||||||
var receiver = ast.receiver.visit(this);
|
var receiver = ast.receiver.visit(this);
|
||||||
var args = this._visitAll(ast.args);
|
var args = this._visitAll(ast.args);
|
||||||
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
|
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
|
||||||
|
var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
||||||
|
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
|
||||||
|
} else {
|
||||||
|
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFunctionCall(ast:FunctionCall) {
|
visitFunctionCall(ast:FunctionCall) {
|
||||||
|
@ -4,9 +4,10 @@ export const RECORD_TYPE_SELF = 0;
|
|||||||
export const RECORD_TYPE_CONST = 1;
|
export const RECORD_TYPE_CONST = 1;
|
||||||
export const RECORD_TYPE_PRIMITIVE_OP = 2;
|
export const RECORD_TYPE_PRIMITIVE_OP = 2;
|
||||||
export const RECORD_TYPE_PROPERTY = 3;
|
export const RECORD_TYPE_PROPERTY = 3;
|
||||||
export const RECORD_TYPE_INVOKE_METHOD = 4;
|
export const RECORD_TYPE_LOCAL = 4;
|
||||||
export const RECORD_TYPE_INVOKE_CLOSURE = 5;
|
export const RECORD_TYPE_INVOKE_METHOD = 5;
|
||||||
export const RECORD_TYPE_KEYED_ACCESS = 6;
|
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
|
||||||
|
export const RECORD_TYPE_KEYED_ACCESS = 7;
|
||||||
export const RECORD_TYPE_PIPE = 8;
|
export const RECORD_TYPE_PIPE = 8;
|
||||||
export const RECORD_TYPE_INTERPOLATE = 9;
|
export const RECORD_TYPE_INTERPOLATE = 9;
|
||||||
|
|
||||||
|
324
modules/angular2/src/core/annotations/annotations.js
vendored
324
modules/angular2/src/core/annotations/annotations.js
vendored
@ -1,32 +1,199 @@
|
|||||||
import {ABSTRACT, CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang';
|
import {ABSTRACT, CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
|
||||||
|
// type StringMap = {[idx: string]: string};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directives allow you to attach behavior to the DOM elements.
|
||||||
|
*
|
||||||
|
* Directive is an abstract concept, instead use concrete directives such as: [Component], [Decorator] or [Viewport].
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
@ABSTRACT()
|
@ABSTRACT()
|
||||||
export class Directive {
|
export class Directive extends Injectable {
|
||||||
selector:any; //string;
|
/**
|
||||||
bind:any;
|
* The CSS selector that triggers the instantiation of a directive.
|
||||||
lightDomServices:any; //List;
|
*
|
||||||
implementsTypes:any; //List;
|
* Angular only allows directives to trigger on CSS selectors that do not cross element boundaries.
|
||||||
lifecycle:any; //List
|
* The supported selectors are:
|
||||||
|
*
|
||||||
|
* - `element-name` select by element name.
|
||||||
|
* - `.class` select by class name.
|
||||||
|
* - `[attribute]` select by attribute name.
|
||||||
|
* - `[attribute=value]` select by attribute name and value.
|
||||||
|
* - `:not(sub_selector)` select only if the element does not match the `sub_selector`.
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* Suppose we have a directive with an `input[type=text]` selector.
|
||||||
|
*
|
||||||
|
* And the following HTML:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <form>
|
||||||
|
* <input type="text">
|
||||||
|
* <input type="radio">
|
||||||
|
* <form>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The directive would only be instantiated on the `<input type="text">` element.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
selector:string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates the set of properties that accept data binding for a directive.
|
||||||
|
*
|
||||||
|
* The `bind` property defines a set of `directiveProperty` to `bindingProperty` key-value pairs:
|
||||||
|
*
|
||||||
|
* - `directiveProperty` specifies the component property where the value is written.
|
||||||
|
* - `bindingProperty` specifies the DOM property where the value is read from.
|
||||||
|
*
|
||||||
|
* You can include [Pipes] when specifying a `bindingProperty` to allow for data transformation and structural
|
||||||
|
* change detection of the value.
|
||||||
|
*
|
||||||
|
* ## Syntax
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Directive({
|
||||||
|
* bind: {
|
||||||
|
* 'directiveProperty1': 'bindingProperty1',
|
||||||
|
* 'directiveProperty2': 'bindingProperty2 | pipe1 | ...',
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Basic Property Binding:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* selector: '[tooltip]',
|
||||||
|
* bind: {
|
||||||
|
* 'tooltipText': 'tooltip'
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* class Tooltip {
|
||||||
|
* set tooltipText(text) {
|
||||||
|
* // This will get called every time the 'tooltip' binding changes with the new value.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* As used in this example:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <div [tooltip]="someExpression">
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Whenever the `someExpression` expression changes, the `bind` declaration instructs Angular to update the
|
||||||
|
* `Tooltip`'s `tooltipText` property.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Similarly in this example:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <div tooltip="Some Text">
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The `Tooltip`'s `tooltipText` property gets initialized to the `Some Text` literal.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Bindings With Pipes:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* selector: '[class-set]',
|
||||||
|
* bind: {
|
||||||
|
* 'classChanges': 'classSet | keyValDiff'
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* class ClassSet {
|
||||||
|
* set classChanges(changes:KeyValueChanges) {
|
||||||
|
* // This will get called every time the `class-set` expressions changes its structure.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* As used in this example:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <div [class-set]="someExpression">
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* In the above example, the `ClassSet` uses the `keyValDiff` [Pipe] for watching structural changes. This means that
|
||||||
|
* the `classChanges` setter gets invoked if the expression changes to a different reference, or if the
|
||||||
|
* structure of the expression changes. (Shallow property watching of the object)
|
||||||
|
*
|
||||||
|
* NOTE: The `someExpression` can also contain its own [Pipe]s. In this case, the two pipes compose as if they were
|
||||||
|
* inlined.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bind:any; // StringMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies which DOM events the directive listens to and what the action should be.
|
||||||
|
*
|
||||||
|
* The `events` property defines a set of `event` to `method` key-value pairs:
|
||||||
|
*
|
||||||
|
* - `event1` specifies the DOM event that the directive listens to.
|
||||||
|
* - `onMethod1` specifies the method to execute when the event occurs.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Syntax
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Directive({
|
||||||
|
* events: {
|
||||||
|
* 'event1': 'onMethod1',
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ## Basic Event Binding:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* selector: 'input',
|
||||||
|
* events: {
|
||||||
|
* 'change': 'onChange'
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* class InputDecorator {
|
||||||
|
* onChange(event:Event) {
|
||||||
|
* // invoked whenever the DOM element fires the 'change' event.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
events:any; // StringMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a set of lifecycle events in which the directive participates.
|
||||||
|
*/
|
||||||
|
lifecycle:any; //List<LifecycleEvent>
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
bind,
|
bind,
|
||||||
lightDomServices,
|
events,
|
||||||
implementsTypes,
|
|
||||||
lifecycle
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
bind:any,
|
bind:any,
|
||||||
lightDomServices:List,
|
events: any,
|
||||||
implementsTypes:List,
|
|
||||||
lifecycle:List
|
lifecycle:List
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.lightDomServices = lightDomServices;
|
|
||||||
this.implementsTypes = implementsTypes;
|
|
||||||
this.bind = bind;
|
this.bind = bind;
|
||||||
|
this.events = events;
|
||||||
this.lifecycle = lifecycle;
|
this.lifecycle = lifecycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,62 +202,86 @@ export class Directive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class Component extends Directive {
|
export class Component extends Directive {
|
||||||
//TODO: vsavkin: uncomment it once the issue with defining fields in a sublass works
|
//TODO: vsavkin: uncomment it once the issue with defining fields in a sublass works
|
||||||
lightDomServices:any; //List;
|
services:any; //List;
|
||||||
shadowDomServices:any; //List;
|
|
||||||
componentServices:any; //List;
|
|
||||||
lifecycle:any; //List
|
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
bind,
|
bind,
|
||||||
lightDomServices,
|
events,
|
||||||
shadowDomServices,
|
services,
|
||||||
componentServices,
|
|
||||||
implementsTypes,
|
|
||||||
lifecycle
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:String,
|
selector:String,
|
||||||
bind:Object,
|
bind:Object,
|
||||||
lightDomServices:List,
|
events:Object,
|
||||||
shadowDomServices:List,
|
services:List,
|
||||||
componentServices:List,
|
|
||||||
implementsTypes:List,
|
|
||||||
lifecycle:List
|
lifecycle:List
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super({
|
super({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
bind: bind,
|
bind: bind,
|
||||||
lightDomServices: lightDomServices,
|
events: events,
|
||||||
implementsTypes: implementsTypes,
|
|
||||||
lifecycle: lifecycle
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lightDomServices = lightDomServices;
|
this.services = services;
|
||||||
this.shadowDomServices = shadowDomServices;
|
|
||||||
this.componentServices = componentServices;
|
|
||||||
this.lifecycle = lifecycle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
|
export class DynamicComponent extends Directive {
|
||||||
|
services:any; //List;
|
||||||
|
|
||||||
|
@CONST()
|
||||||
|
constructor({
|
||||||
|
selector,
|
||||||
|
bind,
|
||||||
|
events,
|
||||||
|
services,
|
||||||
|
lifecycle
|
||||||
|
}:{
|
||||||
|
selector:string,
|
||||||
|
bind:Object,
|
||||||
|
events:Object,
|
||||||
|
services:List,
|
||||||
|
lifecycle:List
|
||||||
|
}={}) {
|
||||||
|
super({
|
||||||
|
selector: selector,
|
||||||
|
bind: bind,
|
||||||
|
events: events,
|
||||||
|
lifecycle: lifecycle
|
||||||
|
});
|
||||||
|
|
||||||
|
this.services = services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class Decorator extends Directive {
|
export class Decorator extends Directive {
|
||||||
compileChildren: boolean;
|
compileChildren: boolean;
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
bind,
|
bind,
|
||||||
lightDomServices,
|
events,
|
||||||
implementsTypes,
|
|
||||||
lifecycle,
|
lifecycle,
|
||||||
compileChildren = true,
|
compileChildren = true,
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
bind:any,
|
bind:any,
|
||||||
lightDomServices:List,
|
events:any,
|
||||||
implementsTypes:List,
|
|
||||||
lifecycle:List,
|
lifecycle:List,
|
||||||
compileChildren:boolean
|
compileChildren:boolean
|
||||||
}={})
|
}={})
|
||||||
@ -99,38 +290,87 @@ export class Decorator extends Directive {
|
|||||||
super({
|
super({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
bind: bind,
|
bind: bind,
|
||||||
lightDomServices: lightDomServices,
|
events: events,
|
||||||
implementsTypes: implementsTypes,
|
|
||||||
lifecycle: lifecycle
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class Viewport extends Directive {
|
export class Viewport extends Directive {
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
bind,
|
bind,
|
||||||
lightDomServices,
|
events,
|
||||||
implementsTypes,
|
|
||||||
lifecycle
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
bind:any,
|
bind:any,
|
||||||
lightDomServices:List,
|
|
||||||
implementsTypes:List,
|
|
||||||
lifecycle:List
|
lifecycle:List
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super({
|
super({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
bind: bind,
|
bind: bind,
|
||||||
lightDomServices: lightDomServices,
|
events: events,
|
||||||
implementsTypes: implementsTypes,
|
|
||||||
lifecycle: lifecycle
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO(misko): turn into LifecycleEvent class once we switch to TypeScript;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that a directive should be notified whenever a [View] that contains it is destroyed.
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* ...,
|
||||||
|
* lifecycle: [ onDestroy ]
|
||||||
|
* })
|
||||||
|
* class ClassSet implements OnDestroy {
|
||||||
|
* onDestroy() {
|
||||||
|
* // invoked to notify directive of the containing view destruction.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export const onDestroy = "onDestroy";
|
export const onDestroy = "onDestroy";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that a directive should be notified when any of its bindings have changed.
|
||||||
|
*
|
||||||
|
* ## Example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* selector: '[class-set]',
|
||||||
|
* bind: {
|
||||||
|
* 'propA': 'propA'
|
||||||
|
* 'propB': 'propB'
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* class ClassSet {
|
||||||
|
* propA;
|
||||||
|
* propB;
|
||||||
|
* onChange(changes:{[idx: string, PropertyUpdate]}) {
|
||||||
|
* // This will get called after any of the properties have been updated.
|
||||||
|
* if (changes['propA']) {
|
||||||
|
* // if propA was updated
|
||||||
|
* }
|
||||||
|
* if (changes['propA']) {
|
||||||
|
* // if propB was updated
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export const onChange = "onChange";
|
export const onChange = "onChange";
|
||||||
|
@ -13,3 +13,16 @@ export class EventEmitter extends DependencyAnnotation {
|
|||||||
this.eventName = eventName;
|
this.eventName = eventName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directive can inject a property setter that would allow setting this property on the
|
||||||
|
* host element
|
||||||
|
*/
|
||||||
|
export class PropertySetter extends DependencyAnnotation {
|
||||||
|
propName: string;
|
||||||
|
@CONST()
|
||||||
|
constructor(propName) {
|
||||||
|
super();
|
||||||
|
this.propName = propName;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class Template {
|
export class Template {
|
||||||
url:any; //string;
|
url:any; //string;
|
||||||
inline:any; //string;
|
inline:any; //string;
|
||||||
|
@ -4,6 +4,7 @@ import {DependencyAnnotation} from 'angular2/di';
|
|||||||
/**
|
/**
|
||||||
* The directive can only be injected from the current element
|
* The directive can only be injected from the current element
|
||||||
* or from its parent.
|
* or from its parent.
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
export class Parent extends DependencyAnnotation {
|
export class Parent extends DependencyAnnotation {
|
||||||
@CONST()
|
@CONST()
|
||||||
@ -15,6 +16,7 @@ export class Parent extends DependencyAnnotation {
|
|||||||
/**
|
/**
|
||||||
* The directive can only be injected from the current element
|
* The directive can only be injected from the current element
|
||||||
* or from its ancestor.
|
* or from its ancestor.
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
export class Ancestor extends DependencyAnnotation {
|
export class Ancestor extends DependencyAnnotation {
|
||||||
@CONST()
|
@CONST()
|
||||||
|
128
modules/angular2/src/core/application.js
vendored
128
modules/angular2/src/core/application.js
vendored
@ -1,5 +1,5 @@
|
|||||||
import {Injector, bind, OpaqueToken} from 'angular2/di';
|
import {Injector, bind, OpaqueToken} from 'angular2/di';
|
||||||
import {Type, FIELD, isBlank, isPresent, BaseException, assertionsEnabled, print} from 'angular2/src/facade/lang';
|
import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print} from 'angular2/src/facade/lang';
|
||||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {Compiler, CompilerCache} from './compiler/compiler';
|
import {Compiler, CompilerCache} from './compiler/compiler';
|
||||||
@ -14,7 +14,7 @@ import {List, ListWrapper} from 'angular2/src/facade/collection';
|
|||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||||
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
||||||
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
|
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
|
||||||
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
|
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
|
||||||
@ -43,9 +43,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
|||||||
return [
|
return [
|
||||||
bind(appDocumentToken).toValue(DOM.defaultDoc()),
|
bind(appDocumentToken).toValue(DOM.defaultDoc()),
|
||||||
bind(appComponentAnnotatedTypeToken).toFactory((reader) => {
|
bind(appComponentAnnotatedTypeToken).toFactory((reader) => {
|
||||||
// TODO(rado): inspect annotation here and warn if there are bindings,
|
// TODO(rado): investigate whether to support bindings on root component.
|
||||||
// lightDomServices, and other component annotations that are skipped
|
|
||||||
// for bootstrapping components.
|
|
||||||
return reader.read(appComponentType);
|
return reader.read(appComponentType);
|
||||||
}, [DirectiveMetadataReader]),
|
}, [DirectiveMetadataReader]),
|
||||||
|
|
||||||
@ -69,7 +67,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
|||||||
// the angular application. Thus the context and lightDomInjector are
|
// the angular application. Thus the context and lightDomInjector are
|
||||||
// empty.
|
// empty.
|
||||||
var view = appProtoView.instantiate(null, eventManager);
|
var view = appProtoView.instantiate(null, eventManager);
|
||||||
view.hydrate(injector, null, new Object());
|
view.hydrate(injector, null, null, new Object(), null);
|
||||||
return view;
|
return view;
|
||||||
});
|
});
|
||||||
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||||
@ -84,7 +82,9 @@ function _injectorBindings(appComponentType): List<Binding> {
|
|||||||
var plugins = [new HammerGesturesPlugin(), new DomEventsPlugin()];
|
var plugins = [new HammerGesturesPlugin(), new DomEventsPlugin()];
|
||||||
return new EventManager(plugins, zone);
|
return new EventManager(plugins, zone);
|
||||||
}, [VmTurnZone]),
|
}, [VmTurnZone]),
|
||||||
bind(ShadowDomStrategy).toClass(NativeShadowDomStrategy),
|
bind(ShadowDomStrategy).toFactory(
|
||||||
|
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||||
|
[StyleUrlResolver, appDocumentToken]),
|
||||||
Compiler,
|
Compiler,
|
||||||
CompilerCache,
|
CompilerCache,
|
||||||
TemplateResolver,
|
TemplateResolver,
|
||||||
@ -117,18 +117,122 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
|||||||
return zone;
|
return zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple calls to this method are allowed. Each application would only share
|
/**
|
||||||
// _rootInjector, which is not user-configurable by design, thus safe to share.
|
* Bootstrapping for Angular applications.
|
||||||
export function bootstrap(appComponentType: Type, bindings: List<Binding>=null, givenBootstrapErrorReporter: Function=null): Promise {
|
*
|
||||||
|
* You instantiate an Angular application by explicitly specifying a component to use as the root component for your
|
||||||
|
* application via the `bootstrap()` method.
|
||||||
|
*
|
||||||
|
* ## Simple Example
|
||||||
|
*
|
||||||
|
* Assuming this `index.html`:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <html>
|
||||||
|
* <!-- load Angular script tags here. -->
|
||||||
|
* <body>
|
||||||
|
* <my-app>loading...</my-app>
|
||||||
|
* </body>
|
||||||
|
* </html>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* An application is bootstrapped inside an existing browser DOM, typically `index.html`. Unlike Angular 1, Angular 2
|
||||||
|
* does not compile/process bindings in `index.html`. This is mainly for security reasons, as well as architectural
|
||||||
|
* changes in Angular 2. This means that `index.html` can safely be processed using server-side technologies such as
|
||||||
|
* bindings. (which may use double-curly `{{ syntax }}` without collision from Angular 2 component double-curly
|
||||||
|
* `{{ syntax }}`.)
|
||||||
|
*
|
||||||
|
* We can use this script code:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({
|
||||||
|
* selector: 'my-app'
|
||||||
|
* })
|
||||||
|
* @Template({
|
||||||
|
* inline: 'Hello {{ name }}!'
|
||||||
|
* })
|
||||||
|
* class MyApp {
|
||||||
|
* name:string;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* this.name = 'World';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* main() {
|
||||||
|
* return bootstrap(MyApp);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* When the app developer invokes `bootstrap()` with the root component `MyApp` as its argument, Angular performs the
|
||||||
|
* following tasks:
|
||||||
|
*
|
||||||
|
* 1. It uses the component's `selector` property to locate the DOM element which needs to be upgraded into
|
||||||
|
* the angular component.
|
||||||
|
* 2. It creates a new child injector (from the primordial injector) and configures the injector with the component's
|
||||||
|
* `services`. Optionally, you can also override the injector configuration for an app by invoking
|
||||||
|
* `bootstrap` with the `componentServiceBindings` argument.
|
||||||
|
* 3. It creates a new [Zone] and connects it to the angular application's change detection domain instance.
|
||||||
|
* 4. It creates a shadow DOM on the selected component's host element and loads the template into it.
|
||||||
|
* 5. It instantiates the specified component.
|
||||||
|
* 6. Finally, Angular performs change detection to apply the initial data bindings for the application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Instantiating Multiple Applications on a Single Page
|
||||||
|
*
|
||||||
|
* There are two ways to do this.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ### Isolated Applications
|
||||||
|
*
|
||||||
|
* Angular creates a new application each time that the `bootstrap()` method is invoked. When multiple applications
|
||||||
|
* are created for a page, Angular treats each application as independent within an isolated change detection and
|
||||||
|
* [Zone] domain. If you need to share data between applications, use the strategy described in the next
|
||||||
|
* section, "Applications That Share Change Detection."
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ### Applications That Share Change Detection
|
||||||
|
*
|
||||||
|
* If you need to bootstrap multiple applications that share common data, the applications must share a common
|
||||||
|
* change detection and zone. To do that, create a meta-component that lists the application components in its template.
|
||||||
|
* By only invoking the `bootstrap()` method once, with the meta-component as its argument, you ensure that only a single
|
||||||
|
* change detection zone is created and therefore data can be shared across the applications.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Primordial Injector
|
||||||
|
*
|
||||||
|
* When working within a browser window, there are many singleton resources: cookies, title, location, and others.
|
||||||
|
* Angular services that represent these resources must likewise be shared across all Angular applications that
|
||||||
|
* occupy the same browser window. For this reason, Angular creates exactly one global primordial injector which stores
|
||||||
|
* all shared services, and each angular application injector has the primordial injector as its parent.
|
||||||
|
*
|
||||||
|
* Each application has its own private injector as well. When there are multiple applications on a page, Angular treats
|
||||||
|
* each application injector's services as private to that application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # API
|
||||||
|
* - [appComponentType]: The root component which should act as the application. This is a reference to a [Type]
|
||||||
|
* which is annotated with `@Component(...)`.
|
||||||
|
* - [componentServiceBindings]: An additional set of bindings that can be added to the [Component.services] to
|
||||||
|
* override default injection behavior.
|
||||||
|
* - [errorReporter]: `function(exception:any, stackTrace:string)` a default error reporter for unhandled exceptions.
|
||||||
|
*
|
||||||
|
* Returns a [Promise] with the application`s private [Injector].
|
||||||
|
*
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
|
export function bootstrap(appComponentType: Type,
|
||||||
|
componentServiceBindings: List<Binding>=null,
|
||||||
|
errorReporter: Function=null): Promise<Injector> {
|
||||||
BrowserDomAdapter.makeCurrent();
|
BrowserDomAdapter.makeCurrent();
|
||||||
var bootstrapProcess = PromiseWrapper.completer();
|
var bootstrapProcess = PromiseWrapper.completer();
|
||||||
|
|
||||||
var zone = _createVmZone(givenBootstrapErrorReporter);
|
var zone = _createVmZone(errorReporter);
|
||||||
zone.run(() => {
|
zone.run(() => {
|
||||||
// TODO(rado): prepopulate template cache, so applications with only
|
// TODO(rado): prepopulate template cache, so applications with only
|
||||||
// index.html and main.js are possible.
|
// index.html and main.js are possible.
|
||||||
|
|
||||||
var appInjector = _createAppInjector(appComponentType, bindings, zone);
|
var appInjector = _createAppInjector(appComponentType, componentServiceBindings, zone);
|
||||||
|
|
||||||
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
|
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
|
||||||
(rootView) => {
|
(rootView) => {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'angular2/change_detection';
|
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'angular2/change_detection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class BindingPropagationConfig {
|
export class BindingPropagationConfig {
|
||||||
_cd:ChangeDetector;
|
_cd:ChangeDetector;
|
||||||
|
|
||||||
|
28
modules/angular2/src/core/compiler/compiler.js
vendored
28
modules/angular2/src/core/compiler/compiler.js
vendored
@ -1,3 +1,4 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from 'angular2/src/facade/lang';
|
import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
@ -11,7 +12,6 @@ import {CompileElement} from './pipeline/compile_element';
|
|||||||
import {createDefaultSteps} from './pipeline/default_steps';
|
import {createDefaultSteps} from './pipeline/default_steps';
|
||||||
import {TemplateLoader} from './template_loader';
|
import {TemplateLoader} from './template_loader';
|
||||||
import {TemplateResolver} from './template_resolver';
|
import {TemplateResolver} from './template_resolver';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
|
||||||
import {Template} from '../annotations/template';
|
import {Template} from '../annotations/template';
|
||||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
import {CompileStep} from './pipeline/compile_step';
|
import {CompileStep} from './pipeline/compile_step';
|
||||||
@ -22,7 +22,9 @@ import {CssProcessor} from './css_processor';
|
|||||||
/**
|
/**
|
||||||
* Cache that stores the ProtoView of the template of a component.
|
* Cache that stores the ProtoView of the template of a component.
|
||||||
* Used to prevent duplicate work and resolve cyclic dependencies.
|
* Used to prevent duplicate work and resolve cyclic dependencies.
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class CompilerCache {
|
export class CompilerCache {
|
||||||
_cache:Map;
|
_cache:Map;
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -47,7 +49,9 @@ export class CompilerCache {
|
|||||||
* The compiler loads and translates the html templates of components into
|
* The compiler loads and translates the html templates of components into
|
||||||
* nested ProtoViews. To decompose its functionality it uses
|
* nested ProtoViews. To decompose its functionality it uses
|
||||||
* the CompilePipeline and the CompileSteps.
|
* the CompilePipeline and the CompileSteps.
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class Compiler {
|
export class Compiler {
|
||||||
_reader: DirectiveMetadataReader;
|
_reader: DirectiveMetadataReader;
|
||||||
_parser:Parser;
|
_parser:Parser;
|
||||||
@ -56,7 +60,6 @@ export class Compiler {
|
|||||||
_templateLoader:TemplateLoader;
|
_templateLoader:TemplateLoader;
|
||||||
_compiling:Map<Type, Promise>;
|
_compiling:Map<Type, Promise>;
|
||||||
_shadowDomStrategy: ShadowDomStrategy;
|
_shadowDomStrategy: ShadowDomStrategy;
|
||||||
_shadowDomDirectives: List<DirectiveMetadata>;
|
|
||||||
_templateResolver: TemplateResolver;
|
_templateResolver: TemplateResolver;
|
||||||
_componentUrlMapper: ComponentUrlMapper;
|
_componentUrlMapper: ComponentUrlMapper;
|
||||||
_urlResolver: UrlResolver;
|
_urlResolver: UrlResolver;
|
||||||
@ -80,11 +83,6 @@ export class Compiler {
|
|||||||
this._templateLoader = templateLoader;
|
this._templateLoader = templateLoader;
|
||||||
this._compiling = MapWrapper.create();
|
this._compiling = MapWrapper.create();
|
||||||
this._shadowDomStrategy = shadowDomStrategy;
|
this._shadowDomStrategy = shadowDomStrategy;
|
||||||
this._shadowDomDirectives = [];
|
|
||||||
var types = shadowDomStrategy.polyfillDirectives();
|
|
||||||
for (var i = 0; i < types.length; i++) {
|
|
||||||
ListWrapper.push(this._shadowDomDirectives, reader.read(types[i]));
|
|
||||||
}
|
|
||||||
this._templateResolver = templateResolver;
|
this._templateResolver = templateResolver;
|
||||||
this._componentUrlMapper = componentUrlMapper;
|
this._componentUrlMapper = componentUrlMapper;
|
||||||
this._urlResolver = urlResolver;
|
this._urlResolver = urlResolver;
|
||||||
@ -93,12 +91,8 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createSteps(component:Type, template: Template):List<CompileStep> {
|
createSteps(component:Type, template: Template):List<CompileStep> {
|
||||||
// Merge directive metadata (from the template and from the shadow dom strategy)
|
var dirMetadata = ListWrapper.map(this._flattenDirectives(template),
|
||||||
var dirMetadata = [];
|
|
||||||
var tplMetadata = ListWrapper.map(this._flattenDirectives(template),
|
|
||||||
(d) => this._reader.read(d));
|
(d) => this._reader.read(d));
|
||||||
dirMetadata = ListWrapper.concat(dirMetadata, tplMetadata);
|
|
||||||
dirMetadata = ListWrapper.concat(dirMetadata, this._shadowDomDirectives);
|
|
||||||
|
|
||||||
var cmpMetadata = this._reader.read(component);
|
var cmpMetadata = this._reader.read(component);
|
||||||
|
|
||||||
@ -172,7 +166,7 @@ export class Compiler {
|
|||||||
var nestedPVPromises = [];
|
var nestedPVPromises = [];
|
||||||
for (var i = 0; i < compileElements.length; i++) {
|
for (var i = 0; i < compileElements.length; i++) {
|
||||||
var ce = compileElements[i];
|
var ce = compileElements[i];
|
||||||
if (isPresent(ce.componentDirective)) {
|
if (ce.hasNestedView) {
|
||||||
this._compileNestedProtoView(ce, nestedPVPromises);
|
this._compileNestedProtoView(ce, nestedPVPromises);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,10 +193,10 @@ export class Compiler {
|
|||||||
var protoView = this._compile(ce.componentDirective.type);
|
var protoView = this._compile(ce.componentDirective.type);
|
||||||
|
|
||||||
if (PromiseWrapper.isPromise(protoView)) {
|
if (PromiseWrapper.isPromise(protoView)) {
|
||||||
ListWrapper.push(promises, protoView);
|
ListWrapper.push(
|
||||||
protoView.then(function (protoView) {
|
promises,
|
||||||
ce.inheritedElementBinder.nestedProtoView = protoView;
|
protoView.then(function(pv) { ce.inheritedElementBinder.nestedProtoView = pv;})
|
||||||
});
|
);
|
||||||
} else {
|
} else {
|
||||||
ce.inheritedElementBinder.nestedProtoView = protoView;
|
ce.inheritedElementBinder.nestedProtoView = protoView;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class ComponentUrlMapper {
|
export class ComponentUrlMapper {
|
||||||
// Returns the base URL to the component source file.
|
// Returns the base URL to the component source file.
|
||||||
// The returned URL could be:
|
// The returned URL could be:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
@ -15,6 +16,7 @@ import {DirectiveMetadata} from './directive_metadata';
|
|||||||
* - Apply any given transformers,
|
* - Apply any given transformers,
|
||||||
* - Apply the shadow DOM strategy style step.
|
* - Apply the shadow DOM strategy style step.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class CssProcessor {
|
export class CssProcessor {
|
||||||
_transformers: List<CssTransformer>;
|
_transformers: List<CssTransformer>;
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Directive} from '../annotations/annotations';
|
import {Directive} from '../annotations/annotations';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class DirectiveMetadataReader {
|
export class DirectiveMetadataReader {
|
||||||
read(type:Type):DirectiveMetadata {
|
read(type:Type):DirectiveMetadata {
|
||||||
var annotations = reflector.annotations(type);
|
var annotations = reflector.annotations(type);
|
||||||
|
@ -1,22 +1,35 @@
|
|||||||
import {ProtoElementInjector} from './element_injector';
|
import {int, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
import * as eiModule from './element_injector';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {List, Map} from 'angular2/src/facade/collection';
|
import {List, StringMap} from 'angular2/src/facade/collection';
|
||||||
import {ProtoView} from './view';
|
import * as viewModule from './view';
|
||||||
|
|
||||||
export class ElementBinder {
|
export class ElementBinder {
|
||||||
protoElementInjector:ProtoElementInjector;
|
protoElementInjector:eiModule.ProtoElementInjector;
|
||||||
componentDirective:DirectiveMetadata;
|
componentDirective:DirectiveMetadata;
|
||||||
viewportDirective:DirectiveMetadata;
|
viewportDirective:DirectiveMetadata;
|
||||||
textNodeIndices:List<int>;
|
textNodeIndices:List<int>;
|
||||||
hasElementPropertyBindings:boolean;
|
hasElementPropertyBindings:boolean;
|
||||||
nestedProtoView: ProtoView;
|
nestedProtoView: viewModule.ProtoView;
|
||||||
events:Map;
|
events:StringMap;
|
||||||
|
contentTagSelector:string;
|
||||||
|
parent:ElementBinder;
|
||||||
|
index:int;
|
||||||
|
distanceToParent:int;
|
||||||
constructor(
|
constructor(
|
||||||
protoElementInjector: ProtoElementInjector, componentDirective:DirectiveMetadata,
|
index:int, parent:ElementBinder, distanceToParent: int,
|
||||||
|
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveMetadata,
|
||||||
viewportDirective:DirectiveMetadata) {
|
viewportDirective:DirectiveMetadata) {
|
||||||
|
if (isBlank(index)) {
|
||||||
|
throw new BaseException('null index not allowed.');
|
||||||
|
}
|
||||||
|
|
||||||
this.protoElementInjector = protoElementInjector;
|
this.protoElementInjector = protoElementInjector;
|
||||||
this.componentDirective = componentDirective;
|
this.componentDirective = componentDirective;
|
||||||
this.viewportDirective = viewportDirective;
|
this.viewportDirective = viewportDirective;
|
||||||
|
this.parent = parent;
|
||||||
|
this.index = index;
|
||||||
|
this.distanceToParent = distanceToParent;
|
||||||
// updated later when events are bound
|
// updated later when events are bound
|
||||||
this.events = null;
|
this.events = null;
|
||||||
// updated later when text nodes are bound
|
// updated later when text nodes are bound
|
||||||
@ -25,5 +38,7 @@ export class ElementBinder {
|
|||||||
this.hasElementPropertyBindings = false;
|
this.hasElementPropertyBindings = false;
|
||||||
// updated later, so we are able to resolve cycles
|
// updated later, so we are able to resolve cycles
|
||||||
this.nestedProtoView = null;
|
this.nestedProtoView = null;
|
||||||
|
// updated later in the compilation pipeline
|
||||||
|
this.contentTagSelector = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import {FIELD, isPresent, isBlank, Type, int, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, Type, int, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {Math} from 'angular2/src/facade/math';
|
import {Math} from 'angular2/src/facade/math';
|
||||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
|
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
|
||||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||||
import {EventEmitter} from 'angular2/src/core/annotations/events';
|
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
|
||||||
import {View, ProtoView} from 'angular2/src/core/compiler/view';
|
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||||
import {LightDom, SourceLightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
|
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {NgElement} from 'angular2/src/core/dom/element';
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'
|
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
|
||||||
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'
|
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config';
|
||||||
|
import * as pclModule from 'angular2/src/core/compiler/private_component_location';
|
||||||
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||||
|
|
||||||
@ -23,18 +24,16 @@ class StaticKeys {
|
|||||||
viewId:number;
|
viewId:number;
|
||||||
ngElementId:number;
|
ngElementId:number;
|
||||||
viewContainerId:number;
|
viewContainerId:number;
|
||||||
destinationLightDomId:number;
|
|
||||||
sourceLightDomId:number;
|
|
||||||
bindingPropagationConfigId:number;
|
bindingPropagationConfigId:number;
|
||||||
|
privateComponentLocationId:number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||||
this.viewId = Key.get(View).id;
|
this.viewId = Key.get(viewModule.View).id;
|
||||||
this.ngElementId = Key.get(NgElement).id;
|
this.ngElementId = Key.get(NgElement).id;
|
||||||
this.viewContainerId = Key.get(ViewContainer).id;
|
this.viewContainerId = Key.get(ViewContainer).id;
|
||||||
this.destinationLightDomId = Key.get(DestinationLightDom).id;
|
|
||||||
this.sourceLightDomId = Key.get(SourceLightDom).id;
|
|
||||||
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
||||||
|
this.privateComponentLocationId = Key.get(pclModule.PrivateComponentLocation).id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance() {
|
static instance() {
|
||||||
@ -90,34 +89,37 @@ class TreeNode {
|
|||||||
export class DirectiveDependency extends Dependency {
|
export class DirectiveDependency extends Dependency {
|
||||||
depth:int;
|
depth:int;
|
||||||
eventEmitterName:string;
|
eventEmitterName:string;
|
||||||
|
propSetterName:string;
|
||||||
|
|
||||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
|
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
|
||||||
properties:List, depth:int, eventEmitterName: string) {
|
properties:List, depth:int, eventEmitterName: string, propSetterName: string) {
|
||||||
super(key, asPromise, lazy, optional, properties);
|
super(key, asPromise, lazy, optional, properties);
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
this.eventEmitterName = eventEmitterName;
|
this.eventEmitterName = eventEmitterName;
|
||||||
|
this.propSetterName = propSetterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createFrom(d:Dependency):Dependency {
|
static createFrom(d:Dependency):Dependency {
|
||||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
var depth = 0;
|
||||||
d.properties, DirectiveDependency._depth(d.properties),
|
var eventName = null;
|
||||||
DirectiveDependency._eventEmitterName(d.properties));
|
var propName = null;
|
||||||
}
|
var properties = d.properties;
|
||||||
|
|
||||||
static _depth(properties):int {
|
|
||||||
if (properties.length == 0) return 0;
|
|
||||||
if (ListWrapper.any(properties, p => p instanceof Parent)) return 1;
|
|
||||||
if (ListWrapper.any(properties, p => p instanceof Ancestor)) return MAX_DEPTH;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _eventEmitterName(properties):string {
|
|
||||||
for (var i = 0; i < properties.length; i++) {
|
for (var i = 0; i < properties.length; i++) {
|
||||||
if (properties[i] instanceof EventEmitter) {
|
var property = properties[i];
|
||||||
return properties[i].eventName;
|
if (property instanceof Parent) {
|
||||||
|
depth = 1;
|
||||||
|
} else if (property instanceof Ancestor) {
|
||||||
|
depth = MAX_DEPTH;
|
||||||
|
} else if (property instanceof EventEmitter) {
|
||||||
|
eventName = property.eventName;
|
||||||
|
} else if (property instanceof PropertySetter) {
|
||||||
|
propName = property.propName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties, depth,
|
||||||
|
eventName, propName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,20 +148,17 @@ export class DirectiveBinding extends Binding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||||
export class PreBuiltObjects {
|
export class PreBuiltObjects {
|
||||||
view:View;
|
view:viewModule.View;
|
||||||
element:NgElement;
|
element:NgElement;
|
||||||
viewContainer:ViewContainer;
|
viewContainer:ViewContainer;
|
||||||
lightDom:LightDom;
|
|
||||||
bindingPropagationConfig:BindingPropagationConfig;
|
bindingPropagationConfig:BindingPropagationConfig;
|
||||||
constructor(view, element:NgElement, viewContainer:ViewContainer, lightDom:LightDom,
|
constructor(view, element:NgElement, viewContainer:ViewContainer,
|
||||||
bindingPropagationConfig:BindingPropagationConfig) {
|
bindingPropagationConfig:BindingPropagationConfig) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.viewContainer = viewContainer;
|
this.viewContainer = viewContainer;
|
||||||
this.lightDom = lightDom;
|
|
||||||
this.bindingPropagationConfig = bindingPropagationConfig;
|
this.bindingPropagationConfig = bindingPropagationConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,7 +207,7 @@ export class ProtoElementInjector {
|
|||||||
_keyId9:int;
|
_keyId9:int;
|
||||||
parent:ProtoElementInjector;
|
parent:ProtoElementInjector;
|
||||||
index:int;
|
index:int;
|
||||||
view:View;
|
view:viewModule.View;
|
||||||
distanceToParent:number;
|
distanceToParent:number;
|
||||||
|
|
||||||
/** Whether the element is exported as $implicit. */
|
/** Whether the element is exported as $implicit. */
|
||||||
@ -256,8 +255,8 @@ export class ProtoElementInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(parent:ElementInjector, host:ElementInjector, eventCallbacks):ElementInjector {
|
instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector {
|
||||||
return new ElementInjector(this, parent, host, eventCallbacks);
|
return new ElementInjector(this, parent, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
directParent(): ProtoElementInjector {
|
directParent(): ProtoElementInjector {
|
||||||
@ -310,8 +309,10 @@ export class ElementInjector extends TreeNode {
|
|||||||
_obj9:any;
|
_obj9:any;
|
||||||
_preBuiltObjects;
|
_preBuiltObjects;
|
||||||
_constructionCounter;
|
_constructionCounter;
|
||||||
_eventCallbacks;
|
_privateComponent;
|
||||||
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector, eventCallbacks: Map) {
|
_privateComponentBinding:DirectiveBinding;
|
||||||
|
|
||||||
|
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) {
|
||||||
super(parent);
|
super(parent);
|
||||||
if (isPresent(parent) && isPresent(host)) {
|
if (isPresent(parent) && isPresent(host)) {
|
||||||
throw new BaseException('Only either parent or host is allowed');
|
throw new BaseException('Only either parent or host is allowed');
|
||||||
@ -329,7 +330,6 @@ export class ElementInjector extends TreeNode {
|
|||||||
this._preBuiltObjects = null;
|
this._preBuiltObjects = null;
|
||||||
this._lightDomAppInjector = null;
|
this._lightDomAppInjector = null;
|
||||||
this._shadowDomAppInjector = null;
|
this._shadowDomAppInjector = null;
|
||||||
this._eventCallbacks = eventCallbacks;
|
|
||||||
this._obj0 = null;
|
this._obj0 = null;
|
||||||
this._obj1 = null;
|
this._obj1 = null;
|
||||||
this._obj2 = null;
|
this._obj2 = null;
|
||||||
@ -360,6 +360,9 @@ export class ElementInjector extends TreeNode {
|
|||||||
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
|
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
|
||||||
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
|
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
|
||||||
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
|
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
|
||||||
|
if (isPresent(this._privateComponentBinding) && this._privateComponentBinding.callOnDestroy) {
|
||||||
|
this._privateComponent.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
this._obj0 = null;
|
this._obj0 = null;
|
||||||
this._obj1 = null;
|
this._obj1 = null;
|
||||||
@ -371,6 +374,7 @@ export class ElementInjector extends TreeNode {
|
|||||||
this._obj7 = null;
|
this._obj7 = null;
|
||||||
this._obj8 = null;
|
this._obj8 = null;
|
||||||
this._obj9 = null;
|
this._obj9 = null;
|
||||||
|
this._privateComponent = null;
|
||||||
|
|
||||||
this._constructionCounter = 0;
|
this._constructionCounter = 0;
|
||||||
}
|
}
|
||||||
@ -393,6 +397,15 @@ export class ElementInjector extends TreeNode {
|
|||||||
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
|
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
|
||||||
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
|
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
|
||||||
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
|
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
|
||||||
|
if (isPresent(this._privateComponentBinding)) {
|
||||||
|
this._privateComponent = this._new(this._privateComponentBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPrivateComponent(componentType:Type, annotation:Directive) {
|
||||||
|
this._privateComponentBinding = DirectiveBinding.createFromType(componentType, annotation);
|
||||||
|
this._privateComponent = this._new(this._privateComponentBinding);
|
||||||
|
return this._privateComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
|
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
|
||||||
@ -433,6 +446,14 @@ export class ElementInjector extends TreeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPrivateComponent() {
|
||||||
|
return this._privateComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShadowDomAppInjector() {
|
||||||
|
return this._shadowDomAppInjector;
|
||||||
|
}
|
||||||
|
|
||||||
directParent(): ElementInjector {
|
directParent(): ElementInjector {
|
||||||
return this._proto.distanceToParent < 2 ? this.parent : null;
|
return this._proto.distanceToParent < 2 ? this.parent : null;
|
||||||
}
|
}
|
||||||
@ -441,6 +462,10 @@ export class ElementInjector extends TreeNode {
|
|||||||
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isPrivateComponentKey(key:Key) {
|
||||||
|
return isPresent(this._privateComponentBinding) && key.id === this._privateComponentBinding.key.id;
|
||||||
|
}
|
||||||
|
|
||||||
_new(binding:Binding) {
|
_new(binding:Binding) {
|
||||||
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
|
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
|
||||||
throw new CyclicDependencyError(binding.key);
|
throw new CyclicDependencyError(binding.key);
|
||||||
@ -488,18 +513,22 @@ export class ElementInjector extends TreeNode {
|
|||||||
|
|
||||||
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||||
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
|
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
|
||||||
|
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
|
||||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildEventEmitter(dep) {
|
_buildEventEmitter(dep) {
|
||||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||||
if (isPresent(this._eventCallbacks)) {
|
return (event) => {
|
||||||
var callback = MapWrapper.get(this._eventCallbacks, dep.eventEmitterName);
|
view.triggerEventHandlers(dep.eventEmitterName, event, this._proto.index);
|
||||||
if (isPresent(callback)) {
|
};
|
||||||
return ProtoView.buildInnerCallback(callback, view);
|
}
|
||||||
}
|
|
||||||
}
|
_buildPropSetter(dep) {
|
||||||
return (_) => {};
|
var ngElement = this._getPreBuiltObjectByKeyId(StaticKeys.instance().ngElementId);
|
||||||
|
var domElement = ngElement.domElement;
|
||||||
|
var setter = reflector.setter(dep.propSetterName);
|
||||||
|
return function(v) { setter(domElement, v) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -535,6 +564,8 @@ export class ElementInjector extends TreeNode {
|
|||||||
|
|
||||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||||
return this._host.getComponent();
|
return this._host.getComponent();
|
||||||
|
} else if (isPresent(this._host) && this._host._isPrivateComponentKey(key)) {
|
||||||
|
return this._host.getPrivateComponent();
|
||||||
} else if (optional) {
|
} else if (optional) {
|
||||||
return this._appInjector(requestor).getOptional(key);
|
return this._appInjector(requestor).getOptional(key);
|
||||||
} else {
|
} else {
|
||||||
@ -560,12 +591,9 @@ export class ElementInjector extends TreeNode {
|
|||||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||||
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
||||||
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
||||||
if (keyId === staticKeys.destinationLightDomId) {
|
|
||||||
var p:ElementInjector = this.directParent();
|
if (keyId === staticKeys.privateComponentLocationId) {
|
||||||
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
|
return new pclModule.PrivateComponentLocation(this, this._preBuiltObjects.element, this._preBuiltObjects.view);
|
||||||
}
|
|
||||||
if (keyId === staticKeys.sourceLightDomId) {
|
|
||||||
return this._host._preBuiltObjects.lightDom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add other objects as needed
|
//TODO add other objects as needed
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class OnChange {
|
export class OnChange {
|
||||||
onChange(changes) {
|
onChange(changes) {
|
||||||
throw "OnChange.onChange is not implemented";
|
throw "OnChange.onChange is not implemented";
|
||||||
|
@ -2,10 +2,11 @@ import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection
|
|||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
|
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
|
||||||
import {DirectiveMetadata} from '../directive_metadata';
|
import {DirectiveMetadata} from '../directive_metadata';
|
||||||
import {Decorator, Component, Viewport} from '../../annotations/annotations';
|
import {Decorator, Component, Viewport, DynamicComponent} from '../../annotations/annotations';
|
||||||
import {ElementBinder} from '../element_binder';
|
import {ElementBinder} from '../element_binder';
|
||||||
import {ProtoElementInjector} from '../element_injector';
|
import {ProtoElementInjector} from '../element_injector';
|
||||||
import {ProtoView} from '../view';
|
import * as viewModule from '../view';
|
||||||
|
import {dashCaseToCamelCase} from './util';
|
||||||
|
|
||||||
import {AST} from 'angular2/change_detection';
|
import {AST} from 'angular2/change_detection';
|
||||||
|
|
||||||
@ -29,16 +30,19 @@ export class CompileElement {
|
|||||||
decoratorDirectives:List<DirectiveMetadata>;
|
decoratorDirectives:List<DirectiveMetadata>;
|
||||||
viewportDirective:DirectiveMetadata;
|
viewportDirective:DirectiveMetadata;
|
||||||
componentDirective:DirectiveMetadata;
|
componentDirective:DirectiveMetadata;
|
||||||
|
hasNestedView:boolean;
|
||||||
_allDirectives:List<DirectiveMetadata>;
|
_allDirectives:List<DirectiveMetadata>;
|
||||||
isViewRoot:boolean;
|
isViewRoot:boolean;
|
||||||
hasBindings:boolean;
|
hasBindings:boolean;
|
||||||
inheritedProtoView:ProtoView;
|
inheritedProtoView:viewModule.ProtoView;
|
||||||
inheritedProtoElementInjector:ProtoElementInjector;
|
inheritedProtoElementInjector:ProtoElementInjector;
|
||||||
inheritedElementBinder:ElementBinder;
|
inheritedElementBinder:ElementBinder;
|
||||||
distanceToParentInjector:number;
|
distanceToParentInjector:int;
|
||||||
|
distanceToParentBinder:int;
|
||||||
compileChildren: boolean;
|
compileChildren: boolean;
|
||||||
ignoreBindings: boolean;
|
ignoreBindings: boolean;
|
||||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
||||||
|
contentTagSelector: string;
|
||||||
|
|
||||||
constructor(element, compilationUnit = '') {
|
constructor(element, compilationUnit = '') {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
@ -51,6 +55,7 @@ export class CompileElement {
|
|||||||
this.decoratorDirectives = null;
|
this.decoratorDirectives = null;
|
||||||
this.viewportDirective = null;
|
this.viewportDirective = null;
|
||||||
this.componentDirective = null;
|
this.componentDirective = null;
|
||||||
|
this.hasNestedView = false;
|
||||||
this._allDirectives = null;
|
this._allDirectives = null;
|
||||||
this.isViewRoot = false;
|
this.isViewRoot = false;
|
||||||
this.hasBindings = false;
|
this.hasBindings = false;
|
||||||
@ -64,9 +69,11 @@ export class CompileElement {
|
|||||||
// an own elementBinder
|
// an own elementBinder
|
||||||
this.inheritedElementBinder = null;
|
this.inheritedElementBinder = null;
|
||||||
this.distanceToParentInjector = 0;
|
this.distanceToParentInjector = 0;
|
||||||
|
this.distanceToParentBinder = 0;
|
||||||
this.compileChildren = true;
|
this.compileChildren = true;
|
||||||
// set to true to ignore all the bindings on the element
|
// set to true to ignore all the bindings on the element
|
||||||
this.ignoreBindings = false;
|
this.ignoreBindings = false;
|
||||||
|
this.contentTagSelector = null;
|
||||||
// description is calculated here as compilation steps may change the element
|
// description is calculated here as compilation steps may change the element
|
||||||
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
||||||
if (compilationUnit !== '') {
|
if (compilationUnit !== '') {
|
||||||
@ -114,7 +121,7 @@ export class CompileElement {
|
|||||||
if (isBlank(this.propertyBindings)) {
|
if (isBlank(this.propertyBindings)) {
|
||||||
this.propertyBindings = MapWrapper.create();
|
this.propertyBindings = MapWrapper.create();
|
||||||
}
|
}
|
||||||
MapWrapper.set(this.propertyBindings, property, expression);
|
MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
addVariableBinding(variableName:string, variableValue:string) {
|
addVariableBinding(variableName:string, variableValue:string) {
|
||||||
@ -127,7 +134,7 @@ export class CompileElement {
|
|||||||
// by the "value", or exported identifier. For example, ng-repeat sets a view local of "index".
|
// by the "value", or exported identifier. For example, ng-repeat sets a view local of "index".
|
||||||
// When this occurs, a lookup keyed by "index" must occur to find if there is a var referencing
|
// When this occurs, a lookup keyed by "index" must occur to find if there is a var referencing
|
||||||
// it.
|
// it.
|
||||||
MapWrapper.set(this.variableBindings, variableValue, variableName);
|
MapWrapper.set(this.variableBindings, variableValue, dashCaseToCamelCase(variableName));
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventBinding(eventName:string, expression:AST) {
|
addEventBinding(eventName:string, expression:AST) {
|
||||||
@ -152,6 +159,9 @@ export class CompileElement {
|
|||||||
this.viewportDirective = directive;
|
this.viewportDirective = directive;
|
||||||
} else if (annotation instanceof Component) {
|
} else if (annotation instanceof Component) {
|
||||||
this.componentDirective = directive;
|
this.componentDirective = directive;
|
||||||
|
this.hasNestedView = true;
|
||||||
|
} else if (annotation instanceof DynamicComponent) {
|
||||||
|
this.componentDirective = directive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +193,7 @@ function getElementDescription(domElement):string {
|
|||||||
|
|
||||||
buf.add("<");
|
buf.add("<");
|
||||||
buf.add(DOM.tagName(domElement).toLowerCase());
|
buf.add(DOM.tagName(domElement).toLowerCase());
|
||||||
|
|
||||||
// show id and class first to ease element identification
|
// show id and class first to ease element identification
|
||||||
addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
|
addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
|
||||||
addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
|
addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import * as ccModule from './compile_control';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One part of the compile process.
|
* One part of the compile process.
|
||||||
* Is guaranteed to be called in depth first order
|
* Is guaranteed to be called in depth first order
|
||||||
*/
|
*/
|
||||||
export class CompileStep {
|
export class CompileStep {
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {}
|
process(parent:CompileElement, current:CompileElement, control:ccModule.CompileControl) {}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {ChangeDetection, Parser} from 'angular2/change_detection';
|
import {ChangeDetection, Parser} from 'angular2/change_detection';
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
import {PropertyBindingParser} from './property_binding_parser';
|
import {PropertyBindingParser} from './property_binding_parser';
|
||||||
import {TextInterpolationParser} from './text_interpolation_parser';
|
import {TextInterpolationParser} from './text_interpolation_parser';
|
||||||
@ -32,6 +31,7 @@ export function createDefaultSteps(
|
|||||||
var steps = [
|
var steps = [
|
||||||
new ViewSplitter(parser),
|
new ViewSplitter(parser),
|
||||||
cssProcessor.getCompileStep(compiledComponent, shadowDomStrategy, templateUrl),
|
cssProcessor.getCompileStep(compiledComponent, shadowDomStrategy, templateUrl),
|
||||||
|
shadowDomStrategy.getTemplateCompileStep(compiledComponent),
|
||||||
new PropertyBindingParser(parser),
|
new PropertyBindingParser(parser),
|
||||||
new DirectiveParser(directives),
|
new DirectiveParser(directives),
|
||||||
new TextInterpolationParser(parser),
|
new TextInterpolationParser(parser),
|
||||||
@ -41,10 +41,5 @@ export function createDefaultSteps(
|
|||||||
new ElementBinderBuilder(parser),
|
new ElementBinderBuilder(parser),
|
||||||
];
|
];
|
||||||
|
|
||||||
var shadowDomStep = shadowDomStrategy.getTemplateCompileStep(compiledComponent);
|
|
||||||
if (isPresent(shadowDomStep)) {
|
|
||||||
ListWrapper.push(steps, shadowDomStep);
|
|
||||||
}
|
|
||||||
|
|
||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@ import {SelectorMatcher} from '../selector';
|
|||||||
import {CssSelector} from '../selector';
|
import {CssSelector} from '../selector';
|
||||||
|
|
||||||
import {DirectiveMetadata} from '../directive_metadata';
|
import {DirectiveMetadata} from '../directive_metadata';
|
||||||
import {Component, Viewport} from '../../annotations/annotations';
|
import {DynamicComponent, Component, Viewport} from '../../annotations/annotations';
|
||||||
import {CompileStep} from './compile_step';
|
import {CompileStep} from './compile_step';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import {CompileControl} from './compile_control';
|
||||||
|
|
||||||
import {isSpecialProperty} from './element_binder_builder';;
|
import {isSpecialProperty} from './element_binder_builder';
|
||||||
|
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||||
|
|
||||||
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
|
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
|
||||||
|
|
||||||
@ -48,31 +49,21 @@ export class DirectiveParser extends CompileStep {
|
|||||||
var classList = current.classList();
|
var classList = current.classList();
|
||||||
|
|
||||||
var cssSelector = new CssSelector();
|
var cssSelector = new CssSelector();
|
||||||
cssSelector.setElement(DOM.nodeName(current.element));
|
var nodeName = DOM.nodeName(current.element);
|
||||||
|
cssSelector.setElement(nodeName);
|
||||||
for (var i=0; i < classList.length; i++) {
|
for (var i=0; i < classList.length; i++) {
|
||||||
cssSelector.addClassName(classList[i]);
|
cssSelector.addClassName(classList[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||||
if (isBlank(current.propertyBindings) ||
|
cssSelector.addAttribute(attrName, attrValue);
|
||||||
isPresent(current.propertyBindings) && !MapWrapper.contains(current.propertyBindings, attrName)) {
|
|
||||||
cssSelector.addAttribute(attrName, attrValue);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (isPresent(current.propertyBindings)) {
|
|
||||||
MapWrapper.forEach(current.propertyBindings, (expression, prop) => {
|
|
||||||
cssSelector.addAttribute(prop, expression.source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isPresent(current.variableBindings)) {
|
|
||||||
MapWrapper.forEach(current.variableBindings, (value, name) => {
|
|
||||||
cssSelector.addAttribute(name, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
||||||
// only be present on <template> elements any more!
|
// only be present on <template> elements any more!
|
||||||
var isTemplateElement = DOM.isTemplateElement(current.element);
|
var isTemplateElement = DOM.isTemplateElement(current.element);
|
||||||
var matchedProperties; // StringMap - used in dev mode to store all properties that have been matched
|
var matchedProperties; // StringMap - used in dev mode to store all properties that have been matched
|
||||||
|
|
||||||
this._selectorMatcher.match(cssSelector, (selector, directive) => {
|
this._selectorMatcher.match(cssSelector, (selector, directive) => {
|
||||||
matchedProperties = updateMatchedProperties(matchedProperties, selector, directive);
|
matchedProperties = updateMatchedProperties(matchedProperties, selector, directive);
|
||||||
checkDirectiveValidity(directive, current, isTemplateElement);
|
checkDirectiveValidity(directive, current, isTemplateElement);
|
||||||
@ -95,20 +86,20 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
|||||||
if (isPresent(attrs)) {
|
if (isPresent(attrs)) {
|
||||||
for (var idx = 0; idx<attrs.length; idx+=2) {
|
for (var idx = 0; idx<attrs.length; idx+=2) {
|
||||||
// attribute name is stored on even indexes
|
// attribute name is stored on even indexes
|
||||||
StringMapWrapper.set(matchedProperties, attrs[idx], true);
|
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(attrs[idx]), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// some properties can be used by the directive, so we need to register them
|
// some properties can be used by the directive, so we need to register them
|
||||||
if (isPresent(directive.annotation) && isPresent(directive.annotation.bind)) {
|
if (isPresent(directive.annotation) && isPresent(directive.annotation.bind)) {
|
||||||
var bindMap = directive.annotation.bind;
|
var bindMap = directive.annotation.bind;
|
||||||
StringMapWrapper.forEach(bindMap, (value, key) => {
|
StringMapWrapper.forEach(bindMap, (value, key) => {
|
||||||
// value is the name of the property that is intepreted
|
// value is the name of the property that is interpreted
|
||||||
// e.g. 'myprop' or 'myprop | double' when a pipe is used to transform the property
|
// e.g. 'myprop' or 'myprop | double' when a pipe is used to transform the property
|
||||||
|
|
||||||
// keep the property name and remove the pipe
|
// keep the property name and remove the pipe
|
||||||
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
||||||
if (isPresent(bindProp) && isPresent(bindProp[1])) {
|
if (isPresent(bindProp) && isPresent(bindProp[1])) {
|
||||||
StringMapWrapper.set(matchedProperties, bindProp[1], true);
|
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(bindProp[1]), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -118,6 +109,9 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
|||||||
|
|
||||||
// check if the directive is compatible with the current element
|
// check if the directive is compatible with the current element
|
||||||
function checkDirectiveValidity(directive, current, isTemplateElement) {
|
function checkDirectiveValidity(directive, current, isTemplateElement) {
|
||||||
|
var isComponent = directive.annotation instanceof Component || directive.annotation instanceof DynamicComponent;
|
||||||
|
var alreadyHasComponent = isPresent(current.componentDirective);
|
||||||
|
|
||||||
if (directive.annotation instanceof Viewport) {
|
if (directive.annotation instanceof Viewport) {
|
||||||
if (!isTemplateElement) {
|
if (!isTemplateElement) {
|
||||||
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
|
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
|
||||||
@ -127,7 +121,8 @@ function checkDirectiveValidity(directive, current, isTemplateElement) {
|
|||||||
}
|
}
|
||||||
} else if (isTemplateElement) {
|
} else if (isTemplateElement) {
|
||||||
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
|
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
|
||||||
} else if ((directive.annotation instanceof Component) && isPresent(current.componentDirective)) {
|
|
||||||
|
} else if (isComponent && alreadyHasComponent) {
|
||||||
throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`);
|
throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,8 +136,8 @@ function checkMissingDirectives(current, matchedProperties, isTemplateElement) {
|
|||||||
MapWrapper.forEach(ppBindings, (expression, prop) => {
|
MapWrapper.forEach(ppBindings, (expression, prop) => {
|
||||||
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
|
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
|
||||||
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
|
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
|
||||||
throw new BaseException(`Missing directive to handle '${prop}' in ${current.elementDescription}`);
|
throw new BaseException(`Missing directive to handle '${camelCaseToDashCase(prop)}' in ${current.elementDescription}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,24 @@ import {DirectiveMetadata} from '../directive_metadata';
|
|||||||
import {CompileStep} from './compile_step';
|
import {CompileStep} from './compile_step';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import {CompileControl} from './compile_control';
|
||||||
|
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||||
|
|
||||||
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
||||||
|
|
||||||
const ARIA_PREFIX = 'aria-';
|
const ARIA_PREFIX = 'aria';
|
||||||
var ariaSettersCache = StringMapWrapper.create();
|
var ariaSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
function ariaSetterFactory(attrName:string) {
|
function ariaSetterFactory(attrName:string) {
|
||||||
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
||||||
|
var ariaAttrName;
|
||||||
|
|
||||||
if (isBlank(setterFn)) {
|
if (isBlank(setterFn)) {
|
||||||
|
ariaAttrName = camelCaseToDashCase(attrName);
|
||||||
setterFn = function(element, value) {
|
setterFn = function(element, value) {
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
DOM.setAttribute(element, attrName, stringify(value));
|
DOM.setAttribute(element, ariaAttrName, stringify(value));
|
||||||
} else {
|
} else {
|
||||||
DOM.removeAttribute(element, attrName);
|
DOM.removeAttribute(element, ariaAttrName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
||||||
@ -34,7 +37,6 @@ function ariaSetterFactory(attrName:string) {
|
|||||||
return setterFn;
|
return setterFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLASS_ATTR = 'class';
|
|
||||||
const CLASS_PREFIX = 'class.';
|
const CLASS_PREFIX = 'class.';
|
||||||
var classSettersCache = StringMapWrapper.create();
|
var classSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
@ -55,22 +57,23 @@ function classSetterFactory(className:string) {
|
|||||||
return setterFn;
|
return setterFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const STYLE_ATTR = 'style';
|
|
||||||
const STYLE_PREFIX = 'style.';
|
const STYLE_PREFIX = 'style.';
|
||||||
var styleSettersCache = StringMapWrapper.create();
|
var styleSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
||||||
var cacheKey = styleName + stylesuffix;
|
var cacheKey = styleName + stylesuffix;
|
||||||
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
||||||
|
var dashCasedStyleName;
|
||||||
|
|
||||||
if (isBlank(setterFn)) {
|
if (isBlank(setterFn)) {
|
||||||
|
dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||||
setterFn = function(element, value) {
|
setterFn = function(element, value) {
|
||||||
var valAsStr;
|
var valAsStr;
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
valAsStr = stringify(value);
|
valAsStr = stringify(value);
|
||||||
DOM.setStyle(element, styleName, valAsStr + stylesuffix);
|
DOM.setStyle(element, dashCasedStyleName, valAsStr + stylesuffix);
|
||||||
} else {
|
} else {
|
||||||
DOM.removeStyle(element, styleName);
|
DOM.removeStyle(element, dashCasedStyleName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
||||||
@ -132,6 +135,11 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
var elementBinder = null;
|
var elementBinder = null;
|
||||||
|
var parentElementBinder = null;
|
||||||
|
var distanceToParentBinder = this._getDistanceToParentBinder(parent, current);
|
||||||
|
if (isPresent(parent)) {
|
||||||
|
parentElementBinder = parent.inheritedElementBinder;
|
||||||
|
}
|
||||||
if (current.hasBindings) {
|
if (current.hasBindings) {
|
||||||
var protoView = current.inheritedProtoView;
|
var protoView = current.inheritedProtoView;
|
||||||
var protoInjectorWasBuilt = isBlank(parent) ? true :
|
var protoInjectorWasBuilt = isBlank(parent) ? true :
|
||||||
@ -140,8 +148,9 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
var currentProtoElementInjector = protoInjectorWasBuilt ?
|
var currentProtoElementInjector = protoInjectorWasBuilt ?
|
||||||
current.inheritedProtoElementInjector : null;
|
current.inheritedProtoElementInjector : null;
|
||||||
|
|
||||||
elementBinder = protoView.bindElement(currentProtoElementInjector,
|
elementBinder = protoView.bindElement(parentElementBinder, distanceToParentBinder,
|
||||||
current.componentDirective, current.viewportDirective);
|
currentProtoElementInjector, current.componentDirective, current.viewportDirective);
|
||||||
|
current.distanceToParentBinder = 0;
|
||||||
|
|
||||||
if (isPresent(current.textNodeBindings)) {
|
if (isPresent(current.textNodeBindings)) {
|
||||||
this._bindTextNodes(protoView, current);
|
this._bindTextNodes(protoView, current);
|
||||||
@ -152,13 +161,23 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
if (isPresent(current.eventBindings)) {
|
if (isPresent(current.eventBindings)) {
|
||||||
this._bindEvents(protoView, current);
|
this._bindEvents(protoView, current);
|
||||||
}
|
}
|
||||||
this._bindDirectiveProperties(current.getAllDirectives(), current);
|
if (isPresent(current.contentTagSelector)) {
|
||||||
|
elementBinder.contentTagSelector = current.contentTagSelector;
|
||||||
|
}
|
||||||
|
var directives = current.getAllDirectives();
|
||||||
|
this._bindDirectiveProperties(directives, current);
|
||||||
|
this._bindDirectiveEvents(directives, current);
|
||||||
} else if (isPresent(parent)) {
|
} else if (isPresent(parent)) {
|
||||||
elementBinder = parent.inheritedElementBinder;
|
elementBinder = parentElementBinder;
|
||||||
|
current.distanceToParentBinder = distanceToParentBinder;
|
||||||
}
|
}
|
||||||
current.inheritedElementBinder = elementBinder;
|
current.inheritedElementBinder = elementBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getDistanceToParentBinder(parent, current) {
|
||||||
|
return isPresent(parent) ? parent.distanceToParentBinder + 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
_bindTextNodes(protoView, compileElement) {
|
_bindTextNodes(protoView, compileElement) {
|
||||||
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
|
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
|
||||||
protoView.bindTextNode(indexInParent, expression);
|
protoView.bindTextNode(indexInParent, expression);
|
||||||
@ -182,7 +201,9 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
} else {
|
} else {
|
||||||
property = this._resolvePropertyName(property);
|
property = this._resolvePropertyName(property);
|
||||||
//TODO(pk): special casing innerHtml, see: https://github.com/angular/angular/issues/789
|
//TODO(pk): special casing innerHtml, see: https://github.com/angular/angular/issues/789
|
||||||
if (DOM.hasProperty(compileElement.element, property) || StringWrapper.equals(property, 'innerHtml')) {
|
if (StringWrapper.equals(property, 'innerHTML')) {
|
||||||
|
setterFn = (element, value) => DOM.setInnerHTML(element, value);
|
||||||
|
} else if (DOM.hasProperty(compileElement.element, property) || StringWrapper.equals(property, 'innerHtml')) {
|
||||||
setterFn = reflector.setter(property);
|
setterFn = reflector.setter(property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,6 +220,19 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_bindDirectiveEvents(directives: List<DirectiveMetadata>, compileElement: CompileElement) {
|
||||||
|
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
|
||||||
|
var directive = directives[directiveIndex];
|
||||||
|
var annotation = directive.annotation;
|
||||||
|
if (isBlank(annotation.events)) continue;
|
||||||
|
var protoView = compileElement.inheritedProtoView;
|
||||||
|
StringMapWrapper.forEach(annotation.events, (action, eventName) => {
|
||||||
|
var expression = this._parser.parseAction(action, compileElement.elementDescription);
|
||||||
|
protoView.bindEvent(eventName, expression, directiveIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_bindDirectiveProperties(directives: List<DirectiveMetadata>,
|
_bindDirectiveProperties(directives: List<DirectiveMetadata>,
|
||||||
compileElement: CompileElement) {
|
compileElement: CompileElement) {
|
||||||
var protoView = compileElement.inheritedProtoView;
|
var protoView = compileElement.inheritedProtoView;
|
||||||
@ -208,12 +242,11 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
var annotation = directive.annotation;
|
var annotation = directive.annotation;
|
||||||
if (isBlank(annotation.bind)) continue;
|
if (isBlank(annotation.bind)) continue;
|
||||||
StringMapWrapper.forEach(annotation.bind, (bindConfig, dirProp) => {
|
StringMapWrapper.forEach(annotation.bind, (bindConfig, dirProp) => {
|
||||||
var bindConfigParts = this._splitBindConfig(bindConfig);
|
var pipes = this._splitBindConfig(bindConfig);
|
||||||
var elProp = bindConfigParts[0];
|
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||||
var pipes = ListWrapper.slice(bindConfigParts, 1, bindConfigParts.length);
|
|
||||||
|
|
||||||
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
||||||
MapWrapper.get(compileElement.propertyBindings, elProp) :
|
MapWrapper.get(compileElement.propertyBindings, dashCaseToCamelCase(elProp)) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (isBlank(bindingAst)) {
|
if (isBlank(bindingAst)) {
|
||||||
@ -230,7 +263,7 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
directiveIndex,
|
directiveIndex,
|
||||||
fullExpAstWithBindPipes,
|
fullExpAstWithBindPipes,
|
||||||
dirProp,
|
dirProp,
|
||||||
reflector.setter(dirProp)
|
reflector.setter(dashCaseToCamelCase(dirProp))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -238,8 +271,7 @@ export class ElementBinderBuilder extends CompileStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_splitBindConfig(bindConfig:string) {
|
_splitBindConfig(bindConfig:string) {
|
||||||
var parts = StringWrapper.split(bindConfig, RegExpWrapper.create("\\|"));
|
return ListWrapper.map(bindConfig.split('|'), (s) => s.trim());
|
||||||
return ListWrapper.map(parts, (s) => s.trim());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_resolvePropertyName(attrName:string) {
|
_resolvePropertyName(attrName:string) {
|
||||||
|
@ -37,7 +37,8 @@ export class ElementBindingMarker extends CompileStep {
|
|||||||
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
|
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
|
||||||
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
|
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
|
||||||
isPresent(current.viewportDirective) ||
|
isPresent(current.viewportDirective) ||
|
||||||
isPresent(current.componentDirective);
|
isPresent(current.componentDirective) ||
|
||||||
|
isPresent(current.contentTagSelector);
|
||||||
|
|
||||||
if (hasBindings) {
|
if (hasBindings) {
|
||||||
var element = current.element;
|
var element = current.element;
|
||||||
|
@ -40,28 +40,33 @@ export class PropertyBindingParser extends CompileStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var attrs = current.attrs();
|
var attrs = current.attrs();
|
||||||
|
var newAttrs = MapWrapper.create();
|
||||||
var desc = current.elementDescription;
|
var desc = current.elementDescription;
|
||||||
|
|
||||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||||
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
||||||
if (isPresent(bindParts)) {
|
if (isPresent(bindParts)) {
|
||||||
if (isPresent(bindParts[1])) {
|
if (isPresent(bindParts[1])) {
|
||||||
// match: bind-prop
|
// match: bind-prop
|
||||||
current.addPropertyBinding(bindParts[4], this._parseBinding(attrValue, desc));
|
current.addPropertyBinding(bindParts[4], this._parseBinding(attrValue, desc));
|
||||||
|
MapWrapper.set(newAttrs, bindParts[4], attrValue);
|
||||||
} else if (isPresent(bindParts[2]) || isPresent(bindParts[7])) {
|
} else if (isPresent(bindParts[2]) || isPresent(bindParts[7])) {
|
||||||
// match: var-name / var-name="iden" / #name / #name="iden"
|
// match: var-name / var-name="iden" / #name / #name="iden"
|
||||||
var identifier = (isPresent(bindParts[4]) && bindParts[4] !== '') ?
|
var identifier = (isPresent(bindParts[4]) && bindParts[4] !== '') ?
|
||||||
bindParts[4] : bindParts[8];
|
bindParts[4] : bindParts[8];
|
||||||
var value = attrValue == '' ? '\$implicit' : attrValue;
|
var value = attrValue == '' ? '\$implicit' : attrValue;
|
||||||
current.addVariableBinding(identifier, value);
|
current.addVariableBinding(identifier, value);
|
||||||
|
MapWrapper.set(newAttrs, identifier, value);
|
||||||
} else if (isPresent(bindParts[3])) {
|
} else if (isPresent(bindParts[3])) {
|
||||||
// match: on-prop
|
// match: on-event
|
||||||
current.addEventBinding(bindParts[4], this._parseAction(attrValue, desc));
|
current.addEventBinding(bindParts[4], this._parseAction(attrValue, desc));
|
||||||
} else if (isPresent(bindParts[5])) {
|
} else if (isPresent(bindParts[5])) {
|
||||||
// match: [prop]
|
// match: [prop]
|
||||||
current.addPropertyBinding(bindParts[5], this._parseBinding(attrValue, desc));
|
current.addPropertyBinding(bindParts[5], this._parseBinding(attrValue, desc));
|
||||||
|
MapWrapper.set(newAttrs, bindParts[5], attrValue);
|
||||||
} else if (isPresent(bindParts[6])) {
|
} else if (isPresent(bindParts[6])) {
|
||||||
// match: (prop)
|
// match: (event)
|
||||||
current.addEventBinding(bindParts[6], this._parseBinding(attrValue, desc));
|
current.addEventBinding(bindParts[6], this._parseAction(attrValue, desc));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var ast = this._parseInterpolation(attrValue, desc);
|
var ast = this._parseInterpolation(attrValue, desc);
|
||||||
@ -70,6 +75,10 @@ export class PropertyBindingParser extends CompileStep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
MapWrapper.forEach(newAttrs, (attrValue, attrName) => {
|
||||||
|
MapWrapper.set(attrs, attrName, attrValue);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseInterpolation(input:string, location:string):AST {
|
_parseInterpolation(input:string, location:string):AST {
|
||||||
|
@ -34,9 +34,6 @@ export class ProtoElementInjectorBuilder extends CompileStep {
|
|||||||
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
|
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
|
||||||
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
||||||
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
|
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
|
||||||
// TODO: add lightDomServices as well,
|
|
||||||
// but after the directives as we rely on that order
|
|
||||||
// in the element_binder_builder.
|
|
||||||
|
|
||||||
// Create a protoElementInjector for any element that either has bindings *or* has one
|
// Create a protoElementInjector for any element that either has bindings *or* has one
|
||||||
// or more var- defined. Elements with a var- defined need a their own element injector
|
// or more var- defined. Elements with a var- defined need a their own element injector
|
||||||
|
@ -35,7 +35,8 @@ export class ProtoViewBuilder extends CompileStep {
|
|||||||
if (current.isViewRoot) {
|
if (current.isViewRoot) {
|
||||||
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
|
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
|
||||||
inheritedProtoView = new ProtoView(current.element, protoChangeDetector,
|
inheritedProtoView = new ProtoView(current.element, protoChangeDetector,
|
||||||
this._shadowDomStrategy);
|
this._shadowDomStrategy, this._getParentProtoView(parent));
|
||||||
|
|
||||||
if (isPresent(parent)) {
|
if (isPresent(parent)) {
|
||||||
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
|
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
|
||||||
throw new BaseException('Only one nested view per element is allowed');
|
throw new BaseException('Only one nested view per element is allowed');
|
||||||
@ -55,16 +56,20 @@ export class ProtoViewBuilder extends CompileStep {
|
|||||||
inheritedProtoView = parent.inheritedProtoView;
|
inheritedProtoView = parent.inheritedProtoView;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The view's contextWithLocals needs to have a full set of variable names at construction time
|
// The view's locals needs to have a full set of variable names at construction time
|
||||||
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
|
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
|
||||||
// to actually create variable bindings for the $implicit bindings, add to the
|
// to actually create variable bindings for the $implicit bindings, add to the
|
||||||
// protoContextLocals manually.
|
// protoLocals manually.
|
||||||
if (isPresent(current.variableBindings)) {
|
if (isPresent(current.variableBindings)) {
|
||||||
MapWrapper.forEach(current.variableBindings, (mappedName, varName) => {
|
MapWrapper.forEach(current.variableBindings, (mappedName, varName) => {
|
||||||
MapWrapper.set(inheritedProtoView.protoContextLocals, mappedName, null);
|
MapWrapper.set(inheritedProtoView.protoLocals, mappedName, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
current.inheritedProtoView = inheritedProtoView;
|
current.inheritedProtoView = inheritedProtoView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getParentProtoView(parent:CompileElement) {
|
||||||
|
return isPresent(parent) ? parent.inheritedProtoView : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
16
modules/angular2/src/core/compiler/pipeline/util.js
vendored
Normal file
16
modules/angular2/src/core/compiler/pipeline/util.js
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
var DASH_CASE_REGEXP = RegExpWrapper.create('-([a-z])');
|
||||||
|
var CAMEL_CASE_REGEXP = RegExpWrapper.create('([A-Z])');
|
||||||
|
|
||||||
|
export function dashCaseToCamelCase(input:string) {
|
||||||
|
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP, (m) => {
|
||||||
|
return m[1].toUpperCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function camelCaseToDashCase(input:string) {
|
||||||
|
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP, (m) => {
|
||||||
|
return '-' + m[1].toLowerCase();
|
||||||
|
});
|
||||||
|
}
|
@ -109,8 +109,10 @@ export class ViewSplitter extends CompileStep {
|
|||||||
var binding = bindings[i];
|
var binding = bindings[i];
|
||||||
if (binding.keyIsVar) {
|
if (binding.keyIsVar) {
|
||||||
compileElement.addVariableBinding(binding.key, binding.name);
|
compileElement.addVariableBinding(binding.key, binding.name);
|
||||||
|
MapWrapper.set(compileElement.attrs(), binding.key, binding.name);
|
||||||
} else if (isPresent(binding.expression)) {
|
} else if (isPresent(binding.expression)) {
|
||||||
compileElement.addPropertyBinding(binding.key, binding.expression);
|
compileElement.addPropertyBinding(binding.key, binding.expression);
|
||||||
|
MapWrapper.set(compileElement.attrs(), binding.key, binding.expression.source);
|
||||||
} else {
|
} else {
|
||||||
DOM.setAttribute(compileElement.element, binding.key, '');
|
DOM.setAttribute(compileElement.element, binding.key, '');
|
||||||
}
|
}
|
||||||
|
34
modules/angular2/src/core/compiler/private_component_loader.js
vendored
Normal file
34
modules/angular2/src/core/compiler/private_component_loader.js
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {Compiler} from './compiler';
|
||||||
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
|
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
|
import {PrivateComponentLocation} from './private_component_location';
|
||||||
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
export class PrivateComponentLoader {
|
||||||
|
compiler:Compiler;
|
||||||
|
shadowDomStrategy:ShadowDomStrategy;
|
||||||
|
eventManager:EventManager;
|
||||||
|
directiveMetadataReader:DirectiveMetadataReader;
|
||||||
|
|
||||||
|
constructor(compiler:Compiler, shadowDomStrategy:ShadowDomStrategy,
|
||||||
|
eventManager:EventManager, directiveMetadataReader:DirectiveMetadataReader) {
|
||||||
|
|
||||||
|
this.compiler = compiler;
|
||||||
|
this.shadowDomStrategy = shadowDomStrategy;
|
||||||
|
this.eventManager = eventManager;
|
||||||
|
this.directiveMetadataReader = directiveMetadataReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
load(type:Type, location:PrivateComponentLocation) {
|
||||||
|
var annotation = this.directiveMetadataReader.read(type).annotation;
|
||||||
|
return this.compiler.compile(type).then((componentProtoView) => {
|
||||||
|
location.createComponent(
|
||||||
|
type, annotation,
|
||||||
|
componentProtoView,
|
||||||
|
this.eventManager,
|
||||||
|
this.shadowDomStrategy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
34
modules/angular2/src/core/compiler/private_component_location.js
vendored
Normal file
34
modules/angular2/src/core/compiler/private_component_location.js
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||||
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
|
import * as viewModule from './view';
|
||||||
|
import * as eiModule from './element_injector';
|
||||||
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
|
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
export class PrivateComponentLocation {
|
||||||
|
_elementInjector:eiModule.ElementInjector;
|
||||||
|
_elt:NgElement;
|
||||||
|
_view:viewModule.View;
|
||||||
|
|
||||||
|
constructor(elementInjector:eiModule.ElementInjector, elt:NgElement, view:viewModule.View){
|
||||||
|
this._elementInjector = elementInjector;
|
||||||
|
this._elt = elt;
|
||||||
|
this._view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
createComponent(type:Type, annotation:Directive, componentProtoView:viewModule.ProtoView,
|
||||||
|
eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) {
|
||||||
|
var context = this._elementInjector.createPrivateComponent(type, annotation);
|
||||||
|
|
||||||
|
var view = componentProtoView.instantiate(this._elementInjector, eventManager);
|
||||||
|
view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, null, context, null);
|
||||||
|
|
||||||
|
shadowDomStrategy.attachTemplate(this._elt.domElement, view);
|
||||||
|
|
||||||
|
ListWrapper.push(this._view.componentChildViews, view);
|
||||||
|
this._view.changeDetector.addChild(view.changeDetector);
|
||||||
|
}
|
||||||
|
}
|
90
modules/angular2/src/core/compiler/selector.js
vendored
90
modules/angular2/src/core/compiler/selector.js
vendored
@ -1,12 +1,13 @@
|
|||||||
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isBlank, RegExpWrapper, RegExpMatcherWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
const _EMPTY_ATTR_VALUE = '';
|
const _EMPTY_ATTR_VALUE = '';
|
||||||
|
|
||||||
// TODO: Can't use `const` here as
|
// TODO: Can't use `const` here as
|
||||||
// in Dart this is not transpiled into `final` yet...
|
// in Dart this is not transpiled into `final` yet...
|
||||||
var _SELECTOR_REGEXP =
|
var _SELECTOR_REGEXP =
|
||||||
RegExpWrapper.create('^([-\\w]+)|' + // "tag"
|
RegExpWrapper.create('(\\:not\\()|' + //":not("
|
||||||
|
'([-\\w]+)|' + // "tag"
|
||||||
'(?:\\.([-\\w]+))|' + // ".class"
|
'(?:\\.([-\\w]+))|' + // ".class"
|
||||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])'); // "[name]", "[name=value]" or "[name*=value]"
|
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])'); // "[name]", "[name=value]" or "[name*=value]"
|
||||||
|
|
||||||
@ -19,21 +20,35 @@ export class CssSelector {
|
|||||||
element:string;
|
element:string;
|
||||||
classNames:List;
|
classNames:List;
|
||||||
attrs:List;
|
attrs:List;
|
||||||
static parse(selector:string):CssSelector {
|
notSelector: CssSelector;
|
||||||
|
static parse(selector:string): CssSelector {
|
||||||
var cssSelector = new CssSelector();
|
var cssSelector = new CssSelector();
|
||||||
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
||||||
var match;
|
var match;
|
||||||
|
var current = cssSelector;
|
||||||
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
||||||
if (isPresent(match[1])) {
|
if (isPresent(match[1])) {
|
||||||
cssSelector.setElement(match[1]);
|
if (isPresent(cssSelector.notSelector)) {
|
||||||
|
throw new BaseException('Nesting :not is not allowed in a selector');
|
||||||
|
}
|
||||||
|
current.notSelector = new CssSelector();
|
||||||
|
current = current.notSelector;
|
||||||
}
|
}
|
||||||
if (isPresent(match[2])) {
|
if (isPresent(match[2])) {
|
||||||
cssSelector.addClassName(match[2]);
|
current.setElement(match[2]);
|
||||||
}
|
}
|
||||||
if (isPresent(match[3])) {
|
if (isPresent(match[3])) {
|
||||||
cssSelector.addAttribute(match[3], match[4]);
|
current.addClassName(match[3]);
|
||||||
|
}
|
||||||
|
if (isPresent(match[4])) {
|
||||||
|
current.addAttribute(match[4], match[5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isPresent(cssSelector.notSelector) && isBlank(cssSelector.element)
|
||||||
|
&& ListWrapper.isEmpty(cssSelector.classNames) && ListWrapper.isEmpty(cssSelector.attrs)) {
|
||||||
|
cssSelector.element = "*";
|
||||||
|
}
|
||||||
|
|
||||||
return cssSelector;
|
return cssSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +56,7 @@ export class CssSelector {
|
|||||||
this.element = null;
|
this.element = null;
|
||||||
this.classNames = ListWrapper.create();
|
this.classNames = ListWrapper.create();
|
||||||
this.attrs = ListWrapper.create();
|
this.attrs = ListWrapper.create();
|
||||||
|
this.notSelector = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setElement(element:string = null) {
|
setElement(element:string = null) {
|
||||||
@ -85,6 +101,9 @@ export class CssSelector {
|
|||||||
res += ']';
|
res += ']';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isPresent(this.notSelector)) {
|
||||||
|
res += ":not(" + this.notSelector.toString() + ")";
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,20 +207,22 @@ export class SelectorMatcher {
|
|||||||
* whose css selector is contained in the given css selector.
|
* whose css selector is contained in the given css selector.
|
||||||
* @param cssSelector A css selector
|
* @param cssSelector A css selector
|
||||||
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
||||||
|
* @return boolean true if a match was found
|
||||||
*/
|
*/
|
||||||
match(cssSelector:CssSelector, matchedCallback:Function) {
|
match(cssSelector:CssSelector, matchedCallback:Function):boolean {
|
||||||
|
var result = false;
|
||||||
var element = cssSelector.element;
|
var element = cssSelector.element;
|
||||||
var classNames = cssSelector.classNames;
|
var classNames = cssSelector.classNames;
|
||||||
var attrs = cssSelector.attrs;
|
var attrs = cssSelector.attrs;
|
||||||
|
|
||||||
this._matchTerminal(this._elementMap, element, matchedCallback);
|
result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
||||||
this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback);
|
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result;
|
||||||
|
|
||||||
if (isPresent(classNames)) {
|
if (isPresent(classNames)) {
|
||||||
for (var index = 0; index<classNames.length; index++) {
|
for (var index = 0; index<classNames.length; index++) {
|
||||||
var className = classNames[index];
|
var className = classNames[index];
|
||||||
this._matchTerminal(this._classMap, className, matchedCallback);
|
result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
||||||
this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback);
|
result = this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,43 +233,51 @@ export class SelectorMatcher {
|
|||||||
|
|
||||||
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
||||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||||
this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, matchedCallback);
|
result = this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
this._matchTerminal(valuesMap, attrValue, matchedCallback);
|
result = this._matchTerminal(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||||
|
|
||||||
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName)
|
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName)
|
||||||
this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback);
|
result = this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchTerminal(map:Map<string,string> = null, name, matchedCallback) {
|
_matchTerminal(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectables = MapWrapper.get(map, name);
|
||||||
|
var starSelectables = MapWrapper.get(map, "*");
|
||||||
|
if (isPresent(starSelectables)) {
|
||||||
|
selectables = ListWrapper.concat(selectables, starSelectables);
|
||||||
}
|
}
|
||||||
var selectables = MapWrapper.get(map, name)
|
|
||||||
if (isBlank(selectables)) {
|
if (isBlank(selectables)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
var selectable;
|
var selectable;
|
||||||
|
var result = false;
|
||||||
for (var index=0; index<selectables.length; index++) {
|
for (var index=0; index<selectables.length; index++) {
|
||||||
selectable = selectables[index];
|
selectable = selectables[index];
|
||||||
matchedCallback(selectable.selector, selectable.cbContext);
|
result = selectable.finalize(cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchPartial(map:Map<string,string> = null, name, cssSelector, matchedCallback) {
|
_matchPartial(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
var nestedSelector = MapWrapper.get(map, name)
|
var nestedSelector = MapWrapper.get(map, name)
|
||||||
if (isBlank(nestedSelector)) {
|
if (isBlank(nestedSelector)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO(perf): get rid of recursion and measure again
|
// TODO(perf): get rid of recursion and measure again
|
||||||
// TODO(perf): don't pass the whole selector into the recursion,
|
// TODO(perf): don't pass the whole selector into the recursion,
|
||||||
// but only the not processed parts
|
// but only the not processed parts
|
||||||
nestedSelector.match(cssSelector, matchedCallback);
|
return nestedSelector.match(cssSelector, matchedCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,10 +285,25 @@ export class SelectorMatcher {
|
|||||||
// Store context to pass back selector and context when a selector is matched
|
// Store context to pass back selector and context when a selector is matched
|
||||||
class SelectorContext {
|
class SelectorContext {
|
||||||
selector:CssSelector;
|
selector:CssSelector;
|
||||||
|
notSelector:CssSelector;
|
||||||
cbContext; // callback context
|
cbContext; // callback context
|
||||||
|
|
||||||
constructor(selector:CssSelector, cbContext) {
|
constructor(selector:CssSelector, cbContext) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
|
this.notSelector = selector.notSelector;
|
||||||
this.cbContext = cbContext;
|
this.cbContext = cbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize(cssSelector: CssSelector, callback) {
|
||||||
|
var result = true;
|
||||||
|
if (isPresent(this.notSelector)) {
|
||||||
|
var notMatcher = new SelectorMatcher();
|
||||||
|
notMatcher.addSelectable(this.notSelector, null);
|
||||||
|
result = !notMatcher.match(cssSelector, null);
|
||||||
|
}
|
||||||
|
if (result && isPresent(callback)) {
|
||||||
|
callback(this.selector, this.cbContext);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import {Decorator} from '../../annotations/annotations';
|
import * as ldModule from './light_dom';
|
||||||
import {SourceLightDom, DestinationLightDom, LightDom} from './light_dom';
|
import {Inject, Injectable} from 'angular2/di';
|
||||||
import {Inject} from 'angular2/di';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {NgElement} from 'angular2/src/core/dom/element';
|
|
||||||
|
|
||||||
class ContentStrategy {
|
class ContentStrategy {
|
||||||
nodes:List;
|
nodes:List;
|
||||||
@ -16,24 +14,18 @@ class ContentStrategy {
|
|||||||
* It is used when the content tag is not a direct child of another component,
|
* It is used when the content tag is not a direct child of another component,
|
||||||
* and thus does not affect redistribution.
|
* and thus does not affect redistribution.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
class RenderedContent extends ContentStrategy {
|
class RenderedContent extends ContentStrategy {
|
||||||
static _lazyScriptTemplate;
|
|
||||||
beginScript;
|
beginScript;
|
||||||
endScript;
|
endScript;
|
||||||
|
|
||||||
constructor(contentEl) {
|
constructor(contentEl) {
|
||||||
super();
|
super();
|
||||||
this._replaceContentElementWithScriptTags(contentEl);
|
this.beginScript = contentEl;
|
||||||
|
this.endScript = DOM.nextSibling(this.beginScript);
|
||||||
this.nodes = [];
|
this.nodes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
_scriptTemplate() {
|
|
||||||
if (!isPresent(RenderedContent._lazyScriptTemplate)) {
|
|
||||||
RenderedContent._lazyScriptTemplate = DOM.createScriptTag('type', 'ng/content');
|
|
||||||
}
|
|
||||||
return RenderedContent._lazyScriptTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserts the nodes in between the start and end scripts.
|
// Inserts the nodes in between the start and end scripts.
|
||||||
// Previous content is removed.
|
// Previous content is removed.
|
||||||
insert(nodes:List) {
|
insert(nodes:List) {
|
||||||
@ -42,16 +34,6 @@ class RenderedContent extends ContentStrategy {
|
|||||||
this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this.endScript : nodes[0]);
|
this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this.endScript : nodes[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replaces the content tag with a pair of script tags
|
|
||||||
_replaceContentElementWithScriptTags(contentEl) {
|
|
||||||
this.beginScript = DOM.clone(this._scriptTemplate());
|
|
||||||
this.endScript = DOM.clone(this._scriptTemplate());
|
|
||||||
|
|
||||||
DOM.insertBefore(contentEl, this.beginScript);
|
|
||||||
DOM.insertBefore(contentEl, this.endScript);
|
|
||||||
DOM.removeChild(DOM.parentElement(contentEl), contentEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeNodesUntil(node) {
|
_removeNodesUntil(node) {
|
||||||
var p = DOM.parentElement(this.beginScript);
|
var p = DOM.parentElement(this.beginScript);
|
||||||
for (var next = DOM.nextSibling(this.beginScript);
|
for (var next = DOM.nextSibling(this.beginScript);
|
||||||
@ -68,9 +50,9 @@ class RenderedContent extends ContentStrategy {
|
|||||||
* and thus does not get rendered but only affect the distribution of its parent component.
|
* and thus does not get rendered but only affect the distribution of its parent component.
|
||||||
*/
|
*/
|
||||||
class IntermediateContent extends ContentStrategy {
|
class IntermediateContent extends ContentStrategy {
|
||||||
destinationLightDom:LightDom;
|
destinationLightDom:ldModule.LightDom;
|
||||||
|
|
||||||
constructor(destinationLightDom:LightDom) {
|
constructor(destinationLightDom:ldModule.LightDom) {
|
||||||
super();
|
super();
|
||||||
this.destinationLightDom = destinationLightDom;
|
this.destinationLightDom = destinationLightDom;
|
||||||
this.nodes = [];
|
this.nodes = [];
|
||||||
@ -83,18 +65,17 @@ class IntermediateContent extends ContentStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Decorator({
|
|
||||||
selector: 'content'
|
|
||||||
})
|
|
||||||
export class Content {
|
export class Content {
|
||||||
select:string;
|
select:string;
|
||||||
_strategy:ContentStrategy;
|
_strategy:ContentStrategy;
|
||||||
|
contentStartElement;
|
||||||
|
|
||||||
constructor(@Inject(DestinationLightDom) destinationLightDom, contentEl:NgElement) {
|
constructor(destinationLightDom:ldModule.LightDom, contentStartEl, selector:string) {
|
||||||
this.select = contentEl.getAttribute('select');
|
this.select = selector;
|
||||||
|
this.contentStartElement = contentStartEl;
|
||||||
this._strategy = isPresent(destinationLightDom) ?
|
this._strategy = isPresent(destinationLightDom) ?
|
||||||
new IntermediateContent(destinationLightDom) :
|
new IntermediateContent(destinationLightDom) :
|
||||||
new RenderedContent(contentEl.domElement);
|
new RenderedContent(contentStartEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes():List {
|
nodes():List {
|
||||||
|
@ -2,40 +2,40 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {View} from '../view';
|
import * as viewModule from '../view';
|
||||||
import {ElementInjector} from '../element_injector';
|
|
||||||
import {ViewContainer} from '../view_container';
|
|
||||||
import {Content} from './content_tag';
|
import {Content} from './content_tag';
|
||||||
|
|
||||||
export class SourceLightDom {}
|
|
||||||
export class DestinationLightDom {}
|
export class DestinationLightDom {}
|
||||||
|
|
||||||
|
|
||||||
class _Root {
|
class _Root {
|
||||||
node;
|
node;
|
||||||
injector:ElementInjector;
|
viewContainer;
|
||||||
|
content;
|
||||||
|
|
||||||
constructor(node, injector) {
|
constructor(node, viewContainer, content) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.injector = injector;
|
this.viewContainer = viewContainer;
|
||||||
|
this.content = content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LightDom should implement SourceLightDom and DestinationLightDom
|
// TODO: LightDom should implement DestinationLightDom
|
||||||
// once interfaces are supported
|
// once interfaces are supported
|
||||||
export class LightDom {
|
export class LightDom {
|
||||||
// The light DOM of the element is enclosed inside the lightDomView
|
// The light DOM of the element is enclosed inside the lightDomView
|
||||||
lightDomView:View;
|
lightDomView:viewModule.View;
|
||||||
// The shadow DOM
|
// The shadow DOM
|
||||||
shadowDomView:View;
|
shadowDomView:viewModule.View;
|
||||||
// The nodes of the light DOM
|
// The nodes of the light DOM
|
||||||
nodes:List;
|
nodes:List;
|
||||||
roots:List<_Root>;
|
roots:List<_Root>;
|
||||||
|
|
||||||
constructor(lightDomView:View, shadowDomView:View, element) {
|
constructor(lightDomView:viewModule.View, shadowDomView:viewModule.View, element) {
|
||||||
this.lightDomView = lightDomView;
|
this.lightDomView = lightDomView;
|
||||||
this.shadowDomView = shadowDomView;
|
this.shadowDomView = shadowDomView;
|
||||||
this.nodes = DOM.childNodesAsList(element);
|
this.nodes = DOM.childNodesAsList(element);
|
||||||
|
|
||||||
this.roots = null;
|
this.roots = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,20 +51,19 @@ export class LightDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collects the Content directives from the view and all its child views
|
// Collects the Content directives from the view and all its child views
|
||||||
_collectAllContentTags(view: View, acc:List<Content>):List<Content> {
|
_collectAllContentTags(view: viewModule.View, acc:List<Content>):List<Content> {
|
||||||
var eis = view.elementInjectors;
|
var contentTags = view.contentTags;
|
||||||
for (var i = 0; i < eis.length; ++i) {
|
var vcs = view.viewContainers;
|
||||||
var ei = eis[i];
|
for (var i=0; i<vcs.length; i++) {
|
||||||
if (isBlank(ei)) continue;
|
var vc = vcs[i];
|
||||||
|
var contentTag = contentTags[i];
|
||||||
if (ei.hasDirective(Content)) {
|
if (isPresent(contentTag)) {
|
||||||
ListWrapper.push(acc, ei.get(Content));
|
ListWrapper.push(acc, contentTag);
|
||||||
|
}
|
||||||
} else if (ei.hasPreBuiltObject(ViewContainer)) {
|
if (isPresent(vc)) {
|
||||||
var vc = ei.get(ViewContainer);
|
|
||||||
ListWrapper.forEach(vc.contentTagContainers(), (view) => {
|
ListWrapper.forEach(vc.contentTagContainers(), (view) => {
|
||||||
this._collectAllContentTags(view, acc);
|
this._collectAllContentTags(view, acc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
@ -76,21 +75,16 @@ export class LightDom {
|
|||||||
// - plain DOM nodes
|
// - plain DOM nodes
|
||||||
expandedDomNodes():List {
|
expandedDomNodes():List {
|
||||||
var res = [];
|
var res = [];
|
||||||
|
|
||||||
var roots = this._roots();
|
var roots = this._roots();
|
||||||
for (var i = 0; i < roots.length; ++i) {
|
for (var i = 0; i < roots.length; ++i) {
|
||||||
|
|
||||||
var root = roots[i];
|
var root = roots[i];
|
||||||
var ei = root.injector;
|
|
||||||
|
|
||||||
if (isPresent(ei) && ei.hasPreBuiltObject(ViewContainer)) {
|
|
||||||
var vc = root.injector.get(ViewContainer);
|
|
||||||
res = ListWrapper.concat(res, vc.nodes());
|
|
||||||
|
|
||||||
} else if (isPresent(ei) && ei.hasDirective(Content)) {
|
|
||||||
var content = root.injector.get(Content);
|
|
||||||
res = ListWrapper.concat(res, content.nodes());
|
|
||||||
|
|
||||||
|
if (isPresent(root.viewContainer)) {
|
||||||
|
res = ListWrapper.concat(res, root.viewContainer.nodes());
|
||||||
|
} else if (isPresent(root.content)) {
|
||||||
|
res = ListWrapper.concat(res, root.content.nodes());
|
||||||
} else {
|
} else {
|
||||||
ListWrapper.push(res, root.node);
|
ListWrapper.push(res, root.node);
|
||||||
}
|
}
|
||||||
@ -103,10 +97,24 @@ export class LightDom {
|
|||||||
_roots() {
|
_roots() {
|
||||||
if (isPresent(this.roots)) return this.roots;
|
if (isPresent(this.roots)) return this.roots;
|
||||||
|
|
||||||
var viewInj = this.lightDomView.elementInjectors;
|
var viewContainers = this.lightDomView.viewContainers;
|
||||||
this.roots = ListWrapper.map(this.nodes, (n) =>
|
var contentTags = this.lightDomView.contentTags;
|
||||||
new _Root(n, ListWrapper.find(viewInj,
|
|
||||||
(inj) => isPresent(inj) ? inj.forElement(n) : false)));
|
this.roots = ListWrapper.map(this.nodes, (n) => {
|
||||||
|
var foundVc = null;
|
||||||
|
var foundContentTag = null;
|
||||||
|
for (var i=0; i<viewContainers.length; i++) {
|
||||||
|
var vc = viewContainers[i];
|
||||||
|
var contentTag = contentTags[i];
|
||||||
|
if (isPresent(vc) && vc.templateElement === n) {
|
||||||
|
foundVc = vc;
|
||||||
|
}
|
||||||
|
if (isPresent(contentTag) && contentTag.contentStartElement === n) {
|
||||||
|
foundContentTag = contentTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new _Root(n, foundVc, foundContentTag);
|
||||||
|
});
|
||||||
|
|
||||||
return this.roots;
|
return this.roots;
|
||||||
}
|
}
|
||||||
@ -119,10 +127,10 @@ function redistributeNodes(contents:List<Content>, nodes:List) {
|
|||||||
var select = content.select;
|
var select = content.select;
|
||||||
var matchSelector = (n) => DOM.elementMatches(n, select);
|
var matchSelector = (n) => DOM.elementMatches(n, select);
|
||||||
|
|
||||||
if (isBlank(select)) {
|
// Empty selector is identical to <content/>
|
||||||
|
if (select.length === 0) {
|
||||||
content.insert(nodes);
|
content.insert(nodes);
|
||||||
ListWrapper.clear(nodes);
|
ListWrapper.clear(nodes);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var matchingNodes = ListWrapper.filter(nodes, matchSelector);
|
var matchingNodes = ListWrapper.filter(nodes, matchSelector);
|
||||||
content.insert(matchingNodes);
|
content.insert(matchingNodes);
|
||||||
|
@ -523,28 +523,8 @@ var _polyfillHostRe = RegExpWrapper.create(_polyfillHost, 'im');
|
|||||||
var _colonHostRe = RegExpWrapper.create(':host', 'im');
|
var _colonHostRe = RegExpWrapper.create(':host', 'im');
|
||||||
var _colonHostContextRe = RegExpWrapper.create(':host-context', 'im');
|
var _colonHostContextRe = RegExpWrapper.create(':host-context', 'im');
|
||||||
|
|
||||||
function _cssTextToStyle(cssText: string) {
|
|
||||||
return DOM.createStyleElement(cssText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _cssToRules(cssText: string) {
|
function _cssToRules(cssText: string) {
|
||||||
var style = _cssTextToStyle(cssText);
|
return DOM.cssToRules(cssText);
|
||||||
DOM.appendChild(DOM.defaultDoc().head, style);
|
|
||||||
var rules = [];
|
|
||||||
if (isPresent(style.sheet)) {
|
|
||||||
// TODO(sorvell): Firefox throws when accessing the rules of a stylesheet
|
|
||||||
// with an @import
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=625013
|
|
||||||
try {
|
|
||||||
rules = style.sheet.cssRules;
|
|
||||||
} catch(e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// console.warn('sheet not found', style);
|
|
||||||
}
|
|
||||||
DOM.remove(style);
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _withCssRules(cssText: string, callback: Function) {
|
function _withCssRules(cssText: string, callback: Function) {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import {Type, isBlank, isPresent, int} from 'angular2/src/facade/lang';
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {Type, isBlank, isPresent, int, StringWrapper, assertionsEnabled} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, Map} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {View} from './view';
|
import * as viewModule from './view';
|
||||||
|
|
||||||
import {Content} from './shadow_dom_emulation/content_tag';
|
|
||||||
import {LightDom} from './shadow_dom_emulation/light_dom';
|
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||||
import {ShadowCss} from './shadow_dom_emulation/shadow_css';
|
import {ShadowCss} from './shadow_dom_emulation/shadow_css';
|
||||||
|
|
||||||
@ -15,24 +15,34 @@ import {StyleUrlResolver} from './style_url_resolver';
|
|||||||
|
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
|
|
||||||
import {CompileStep} from './pipeline/compile_step';
|
import * as NS from './pipeline/compile_step';
|
||||||
import {CompileElement} from './pipeline/compile_element';
|
import {CompileElement} from './pipeline/compile_element';
|
||||||
import {CompileControl} from './pipeline/compile_control';
|
import {CompileControl} from './pipeline/compile_control';
|
||||||
|
|
||||||
|
var _EMPTY_STEP;
|
||||||
|
|
||||||
|
// Note: fill _EMPTY_STEP to prevent
|
||||||
|
// problems from cyclic dependencies
|
||||||
|
function _emptyStep() {
|
||||||
|
if (isBlank(_EMPTY_STEP)) {
|
||||||
|
_EMPTY_STEP = new _EmptyCompileStep();
|
||||||
|
}
|
||||||
|
return _EMPTY_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
export class ShadowDomStrategy {
|
export class ShadowDomStrategy {
|
||||||
attachTemplate(el, view:View) {}
|
attachTemplate(el, view:viewModule.View) {}
|
||||||
constructLightDom(lightDomView:View, shadowDomView:View, el): LightDom { return null; }
|
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { return null; }
|
||||||
polyfillDirectives():List<Type> { return []; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An optional step that can modify the template style elements.
|
* An optional step that can modify the template style elements.
|
||||||
*
|
*
|
||||||
* @param {DirectiveMetadata} cmpMetadata
|
* @param {DirectiveMetadata} cmpMetadata
|
||||||
* @param {string} templateUrl the template base URL
|
* @param {string} templateUrl the template base URL
|
||||||
* @returns {CompileStep} a compile step to append to the compiler pipeline, null if not required.
|
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||||
*/
|
*/
|
||||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): CompileStep {
|
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||||
return null;
|
return _emptyStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,9 +51,9 @@ export class ShadowDomStrategy {
|
|||||||
* This step could be used to modify the template in order to scope the styles.
|
* This step could be used to modify the template in order to scope the styles.
|
||||||
*
|
*
|
||||||
* @param {DirectiveMetadata} cmpMetadata
|
* @param {DirectiveMetadata} cmpMetadata
|
||||||
* @returns {CompileStep} a compile step to append to the compiler pipeline, null if not required.
|
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||||
*/
|
*/
|
||||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): CompileStep { return null; }
|
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep { return _emptyStep(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application element does not go through the compiler pipeline.
|
* The application element does not go through the compiler pipeline.
|
||||||
@ -68,6 +78,7 @@ export class ShadowDomStrategy {
|
|||||||
* - styles are **not** scoped to their component and will apply to the whole document,
|
* - styles are **not** scoped to their component and will apply to the whole document,
|
||||||
* - you can **not** use shadow DOM specific selectors in the styles
|
* - you can **not** use shadow DOM specific selectors in the styles
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||||
_styleUrlResolver: StyleUrlResolver;
|
_styleUrlResolver: StyleUrlResolver;
|
||||||
_styleHost;
|
_styleHost;
|
||||||
@ -78,23 +89,23 @@ export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
|||||||
this._styleHost = styleHost;
|
this._styleHost = styleHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachTemplate(el, view:View){
|
attachTemplate(el, view:viewModule.View) {
|
||||||
DOM.clearNodes(el);
|
DOM.clearNodes(el);
|
||||||
_moveViewNodesIntoParent(el, view);
|
_moveViewNodesIntoParent(el, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructLightDom(lightDomView:View, shadowDomView:View, el): LightDom {
|
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom {
|
||||||
return new LightDom(lightDomView, shadowDomView, el);
|
return new LightDom(lightDomView, shadowDomView, el);
|
||||||
}
|
}
|
||||||
|
|
||||||
polyfillDirectives():List<Type> {
|
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||||
return [Content];
|
|
||||||
}
|
|
||||||
|
|
||||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): CompileStep {
|
|
||||||
return new _EmulatedUnscopedCssStep(cmpMetadata, templateUrl, this._styleUrlResolver,
|
return new _EmulatedUnscopedCssStep(cmpMetadata, templateUrl, this._styleUrlResolver,
|
||||||
this._styleHost);
|
this._styleHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
|
||||||
|
return new _BaseEmulatedShadowDomStep();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +120,7 @@ export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
|||||||
* - a common subset of shadow DOM selectors are supported,
|
* - a common subset of shadow DOM selectors are supported,
|
||||||
* - see `ShadowCss` for more information and limitations.
|
* - see `ShadowCss` for more information and limitations.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
|
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
|
||||||
_styleInliner: StyleInliner;
|
_styleInliner: StyleInliner;
|
||||||
|
|
||||||
@ -117,12 +129,12 @@ export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomSt
|
|||||||
this._styleInliner = styleInliner;
|
this._styleInliner = styleInliner;
|
||||||
}
|
}
|
||||||
|
|
||||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): CompileStep {
|
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||||
return new _EmulatedScopedCssStep(cmpMetadata, templateUrl, this._styleInliner,
|
return new _EmulatedScopedCssStep(cmpMetadata, templateUrl, this._styleInliner,
|
||||||
this._styleUrlResolver, this._styleHost);
|
this._styleUrlResolver, this._styleHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): CompileStep {
|
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
|
||||||
return new _ShimShadowDomStep(cmpMetadata);
|
return new _ShimShadowDomStep(cmpMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +151,7 @@ export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomSt
|
|||||||
* The templates for the component are inserted in a Shadow Root created on the component element.
|
* The templates for the component are inserted in a Shadow Root created on the component element.
|
||||||
* Hence they are strictly isolated.
|
* Hence they are strictly isolated.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||||
_styleUrlResolver: StyleUrlResolver;
|
_styleUrlResolver: StyleUrlResolver;
|
||||||
|
|
||||||
@ -147,16 +160,48 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
|||||||
this._styleUrlResolver = styleUrlResolver;
|
this._styleUrlResolver = styleUrlResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachTemplate(el, view:View){
|
attachTemplate(el, view:viewModule.View){
|
||||||
_moveViewNodesIntoParent(DOM.createShadowRoot(el), view);
|
_moveViewNodesIntoParent(DOM.createShadowRoot(el), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): CompileStep {
|
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||||
return new _NativeCssStep(templateUrl, this._styleUrlResolver);
|
return new _NativeCssStep(templateUrl, this._styleUrlResolver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ShimShadowDomStep extends CompileStep {
|
class _BaseEmulatedShadowDomStep extends NS.CompileStep {
|
||||||
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
|
if (current.ignoreBindings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var nodeName = DOM.nodeName(current.element);
|
||||||
|
if (StringWrapper.equals(nodeName.toUpperCase(), 'CONTENT')) {
|
||||||
|
var attrs = current.attrs();
|
||||||
|
var selector = MapWrapper.get(attrs, 'select');
|
||||||
|
current.contentTagSelector = isPresent(selector) ? selector : '';
|
||||||
|
|
||||||
|
var contentStart = DOM.createScriptTag('type', 'ng/contentStart');
|
||||||
|
if (assertionsEnabled()) {
|
||||||
|
DOM.setAttribute(contentStart, 'select', current.contentTagSelector);
|
||||||
|
}
|
||||||
|
var contentEnd = DOM.createScriptTag('type', 'ng/contentEnd');
|
||||||
|
DOM.insertBefore(current.element, contentStart);
|
||||||
|
DOM.insertBefore(current.element, contentEnd);
|
||||||
|
DOM.remove(current.element);
|
||||||
|
|
||||||
|
current.element = contentStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmptyCompileStep extends NS.CompileStep {
|
||||||
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _ShimShadowDomStep extends _BaseEmulatedShadowDomStep {
|
||||||
_contentAttribute: string;
|
_contentAttribute: string;
|
||||||
|
|
||||||
constructor(cmpMetadata: DirectiveMetadata) {
|
constructor(cmpMetadata: DirectiveMetadata) {
|
||||||
@ -167,6 +212,7 @@ class _ShimShadowDomStep extends CompileStep {
|
|||||||
|
|
||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
|
super.process(parent, current, control);
|
||||||
if (current.ignoreBindings) {
|
if (current.ignoreBindings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -184,7 +230,7 @@ class _ShimShadowDomStep extends CompileStep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EmulatedUnscopedCssStep extends CompileStep {
|
class _EmulatedUnscopedCssStep extends NS.CompileStep {
|
||||||
_templateUrl: string;
|
_templateUrl: string;
|
||||||
_styleUrlResolver: StyleUrlResolver;
|
_styleUrlResolver: StyleUrlResolver;
|
||||||
_styleHost;
|
_styleHost;
|
||||||
@ -213,7 +259,7 @@ class _EmulatedUnscopedCssStep extends CompileStep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EmulatedScopedCssStep extends CompileStep {
|
class _EmulatedScopedCssStep extends NS.CompileStep {
|
||||||
_templateUrl: string;
|
_templateUrl: string;
|
||||||
_component: Type;
|
_component: Type;
|
||||||
_styleInliner: StyleInliner;
|
_styleInliner: StyleInliner;
|
||||||
@ -255,7 +301,7 @@ class _EmulatedScopedCssStep extends CompileStep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NativeCssStep extends CompileStep {
|
class _NativeCssStep extends NS.CompileStep {
|
||||||
_styleUrlResolver: StyleUrlResolver;
|
_styleUrlResolver: StyleUrlResolver;
|
||||||
_templateUrl: string;
|
_templateUrl: string;
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
||||||
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
|
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
|
||||||
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
|
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
|
||||||
@ -21,6 +22,7 @@ import {
|
|||||||
*
|
*
|
||||||
* When an @import rules is inlined, it's url are rewritten.
|
* When an @import rules is inlined, it's url are rewritten.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class StyleInliner {
|
export class StyleInliner {
|
||||||
_xhr: XHR;
|
_xhr: XHR;
|
||||||
_urlResolver: UrlResolver;
|
_urlResolver: UrlResolver;
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// Some of the code comes from WebComponents.JS
|
// Some of the code comes from WebComponents.JS
|
||||||
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
|
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
|
||||||
|
|
||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {RegExp, RegExpWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
import {RegExp, RegExpWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rewrites URLs by resolving '@import' and 'url()' URLs from the given base URL.
|
* Rewrites URLs by resolving '@import' and 'url()' URLs from the given base URL.
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class StyleUrlResolver {
|
export class StyleUrlResolver {
|
||||||
_resolver: UrlResolver;
|
_resolver: UrlResolver;
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Map, MapWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
import {Map, MapWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
@ -10,7 +11,9 @@ import {UrlResolver} from './url_resolver';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Strategy to load component templates.
|
* Strategy to load component templates.
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class TemplateLoader {
|
export class TemplateLoader {
|
||||||
_xhr: XHR;
|
_xhr: XHR;
|
||||||
_htmlCache: StringMap;
|
_htmlCache: StringMap;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {Template} from 'angular2/src/core/annotations/template';
|
import {Template} from 'angular2/src/core/annotations/template';
|
||||||
|
|
||||||
import {Type, stringify, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {Type, stringify, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
@ -6,6 +7,7 @@ import {Map, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection
|
|||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class TemplateResolver {
|
export class TemplateResolver {
|
||||||
_cache: Map;
|
_cache: Map;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {isPresent, isBlank, RegExpWrapper, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, RegExpWrapper, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class UrlResolver {
|
export class UrlResolver {
|
||||||
static a;
|
static a;
|
||||||
|
|
||||||
@ -12,8 +14,8 @@ export class UrlResolver {
|
|||||||
|
|
||||||
resolve(baseUrl: string, url: string): string {
|
resolve(baseUrl: string, url: string): string {
|
||||||
if (isBlank(baseUrl)) {
|
if (isBlank(baseUrl)) {
|
||||||
UrlResolver.a.href = url;
|
DOM.resolveAndSetHref(UrlResolver.a, url, null);
|
||||||
return UrlResolver.a.href;
|
return DOM.getHref(UrlResolver.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBlank(url) || url == '') return baseUrl;
|
if (isBlank(url) || url == '') return baseUrl;
|
||||||
@ -28,8 +30,8 @@ export class UrlResolver {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
UrlResolver.a.href = baseUrl + '/../' + url;
|
DOM.resolveAndSetHref(UrlResolver.a, baseUrl, url);
|
||||||
return UrlResolver.a.href;
|
return DOM.getHref(UrlResolver.a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
304
modules/angular2/src/core/compiler/view.js
vendored
304
modules/angular2/src/core/compiler/view.js
vendored
@ -1,19 +1,20 @@
|
|||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {Promise} from 'angular2/src/facade/async';
|
import {Promise} from 'angular2/src/facade/async';
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, ChangeRecord}
|
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
||||||
from 'angular2/change_detection';
|
ChangeRecord, BindingRecord, uninitialized} from 'angular2/change_detection';
|
||||||
|
|
||||||
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
|
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
|
||||||
import {BindingPropagationConfig} from './binding_propagation_config';
|
import {BindingPropagationConfig} from './binding_propagation_config';
|
||||||
import {ElementBinder} from './element_binder';
|
import {ElementBinder} from './element_binder';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {SetterFn} from 'angular2/src/reflection/types';
|
import {SetterFn} from 'angular2/src/reflection/types';
|
||||||
import {FIELD, IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {Injector} from 'angular2/di';
|
import {Injector} from 'angular2/di';
|
||||||
import {NgElement} from 'angular2/src/core/dom/element';
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
import {ViewContainer} from './view_container';
|
import {ViewContainer} from './view_container';
|
||||||
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||||
|
import {Content} from './shadow_dom_emulation/content_tag';
|
||||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
import {ViewPool} from './view_pool';
|
import {ViewPool} from './view_pool';
|
||||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
@ -27,6 +28,7 @@ var VIEW_POOL_PREFILL = 0;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||||
|
* @publicModule angular2/angular2
|
||||||
*/
|
*/
|
||||||
@IMPLEMENTS(ChangeDispatcher)
|
@IMPLEMENTS(ChangeDispatcher)
|
||||||
export class View {
|
export class View {
|
||||||
@ -41,37 +43,42 @@ export class View {
|
|||||||
nodes:List;
|
nodes:List;
|
||||||
componentChildViews: List<View>;
|
componentChildViews: List<View>;
|
||||||
viewContainers: List<ViewContainer>;
|
viewContainers: List<ViewContainer>;
|
||||||
|
contentTags: List<Content>;
|
||||||
preBuiltObjects: List<PreBuiltObjects>;
|
preBuiltObjects: List<PreBuiltObjects>;
|
||||||
|
lightDoms: List<LightDom>;
|
||||||
proto: ProtoView;
|
proto: ProtoView;
|
||||||
context: any;
|
context: any;
|
||||||
contextWithLocals:ContextWithVariableBindings;
|
locals:Locals;
|
||||||
|
|
||||||
constructor(proto:ProtoView, nodes:List, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) {
|
constructor(proto:ProtoView, nodes:List, protoLocals:Map) {
|
||||||
this.proto = proto;
|
this.proto = proto;
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
this.changeDetector = protoChangeDetector.instantiate(this);
|
this.changeDetector = null;
|
||||||
this.elementInjectors = null;
|
this.elementInjectors = null;
|
||||||
this.rootElementInjectors = null;
|
this.rootElementInjectors = null;
|
||||||
this.textNodes = null;
|
this.textNodes = null;
|
||||||
this.bindElements = null;
|
this.bindElements = null;
|
||||||
this.componentChildViews = null;
|
this.componentChildViews = null;
|
||||||
this.viewContainers = null;
|
this.viewContainers = null;
|
||||||
|
this.contentTags = null;
|
||||||
this.preBuiltObjects = null;
|
this.preBuiltObjects = null;
|
||||||
|
this.lightDoms = null;
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.contextWithLocals = (MapWrapper.size(protoContextLocals) > 0)
|
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||||
? new ContextWithVariableBindings(null, MapWrapper.clone(protoContextLocals))
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List,
|
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List,
|
||||||
viewContainers:List, preBuiltObjects:List, componentChildViews:List) {
|
viewContainers:List, contentTags:List, preBuiltObjects:List, componentChildViews:List, lightDoms:List<LightDom>) {
|
||||||
|
this.changeDetector = changeDetector;
|
||||||
this.elementInjectors = elementInjectors;
|
this.elementInjectors = elementInjectors;
|
||||||
this.rootElementInjectors = rootElementInjectors;
|
this.rootElementInjectors = rootElementInjectors;
|
||||||
this.textNodes = textNodes;
|
this.textNodes = textNodes;
|
||||||
this.bindElements = bindElements;
|
this.bindElements = bindElements;
|
||||||
this.viewContainers = viewContainers;
|
this.viewContainers = viewContainers;
|
||||||
|
this.contentTags = contentTags;
|
||||||
this.preBuiltObjects = preBuiltObjects;
|
this.preBuiltObjects = preBuiltObjects;
|
||||||
this.componentChildViews = componentChildViews;
|
this.componentChildViews = componentChildViews;
|
||||||
|
this.lightDoms = lightDoms;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocal(contextName: string, value) {
|
setLocal(contextName: string, value) {
|
||||||
@ -80,29 +87,22 @@ export class View {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var templateName = MapWrapper.get(this.proto.variableBindings, contextName);
|
var templateName = MapWrapper.get(this.proto.variableBindings, contextName);
|
||||||
this.context.set(templateName, value);
|
this.locals.set(templateName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrated() {
|
hydrated() {
|
||||||
return isPresent(this.context);
|
return isPresent(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hydrateContext(newContext) {
|
_hydrateContext(newContext, locals) {
|
||||||
if (isPresent(this.contextWithLocals)) {
|
this.context = newContext;
|
||||||
this.contextWithLocals.parent = newContext;
|
this.locals.parent = locals;
|
||||||
this.context = this.contextWithLocals;
|
this.changeDetector.hydrate(this.context, this.locals);
|
||||||
} else {
|
|
||||||
this.context = newContext;
|
|
||||||
}
|
|
||||||
// TODO(tbosch): if we have a contextWithLocals we actually only need to
|
|
||||||
// set the contextWithLocals once. Would it be faster to always use a contextWithLocals
|
|
||||||
// even if we don't have locals and not update the recordRange here?
|
|
||||||
this.changeDetector.hydrate(this.context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_dehydrateContext() {
|
_dehydrateContext() {
|
||||||
if (isPresent(this.contextWithLocals)) {
|
if (isPresent(this.locals)) {
|
||||||
this.contextWithLocals.clearValues();
|
this.locals.clearValues();
|
||||||
}
|
}
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.changeDetector.dehydrate();
|
this.changeDetector.dehydrate();
|
||||||
@ -124,14 +124,17 @@ export class View {
|
|||||||
* A call to hydrate/dehydrate does not attach/detach the view from the view
|
* A call to hydrate/dehydrate does not attach/detach the view from the view
|
||||||
* tree.
|
* tree.
|
||||||
*/
|
*/
|
||||||
hydrate(appInjector: Injector, hostElementInjector: ElementInjector,
|
hydrate(appInjector: Injector, hostElementInjector: ElementInjector, hostLightDom: LightDom,
|
||||||
context: Object) {
|
context: Object, locals:Locals) {
|
||||||
if (this.hydrated()) throw new BaseException('The view is already hydrated.');
|
if (this.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||||
this._hydrateContext(context);
|
this._hydrateContext(context, locals);
|
||||||
|
|
||||||
// viewContainers
|
// viewContainers
|
||||||
for (var i = 0; i < this.viewContainers.length; i++) {
|
for (var i = 0; i < this.viewContainers.length; i++) {
|
||||||
this.viewContainers[i].hydrate(appInjector, hostElementInjector);
|
var vc = this.viewContainers[i];
|
||||||
|
if (isPresent(vc)) {
|
||||||
|
vc.hydrate(appInjector, hostElementInjector, hostLightDom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var binders = this.proto.elementBinders;
|
var binders = this.proto.elementBinders;
|
||||||
@ -142,7 +145,7 @@ export class View {
|
|||||||
|
|
||||||
// shadowDomAppInjector
|
// shadowDomAppInjector
|
||||||
if (isPresent(componentDirective)) {
|
if (isPresent(componentDirective)) {
|
||||||
var services = componentDirective.annotation.componentServices;
|
var services = componentDirective.annotation.services;
|
||||||
if (isPresent(services))
|
if (isPresent(services))
|
||||||
shadowDomAppInjector = appInjector.createChild(services);
|
shadowDomAppInjector = appInjector.createChild(services);
|
||||||
else {
|
else {
|
||||||
@ -162,26 +165,22 @@ export class View {
|
|||||||
// name.
|
// name.
|
||||||
var exportImplicitName = elementInjector.getExportImplicitName();
|
var exportImplicitName = elementInjector.getExportImplicitName();
|
||||||
if (elementInjector.isExportingComponent()) {
|
if (elementInjector.isExportingComponent()) {
|
||||||
this.context.set(exportImplicitName, elementInjector.getComponent());
|
this.locals.set(exportImplicitName, elementInjector.getComponent());
|
||||||
} else if (elementInjector.isExportingElement()) {
|
} else if (elementInjector.isExportingElement()) {
|
||||||
this.context.set(exportImplicitName, elementInjector.getNgElement().domElement);
|
this.locals.set(exportImplicitName, elementInjector.getNgElement().domElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(componentDirective)) {
|
if (isPresent(binders[i].nestedProtoView) && isPresent(componentDirective)) {
|
||||||
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
||||||
elementInjector, elementInjector.getComponent());
|
elementInjector, this.lightDoms[i], elementInjector.getComponent(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be moved into DOM write queue
|
for (var i = 0; i < this.lightDoms.length; ++i) {
|
||||||
for (var i = 0; i < binders.length; ++i) {
|
var lightDom = this.lightDoms[i];
|
||||||
var componentDirective = binders[i].componentDirective;
|
if (isPresent(lightDom)) {
|
||||||
if (isPresent(componentDirective)) {
|
lightDom.redistribute();
|
||||||
var lightDom = this.preBuiltObjects[i].lightDom;
|
|
||||||
if (isPresent(lightDom)) {
|
|
||||||
lightDom.redistribute();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,13 +203,33 @@ export class View {
|
|||||||
// viewContainers
|
// viewContainers
|
||||||
if (isPresent(this.viewContainers)) {
|
if (isPresent(this.viewContainers)) {
|
||||||
for (var i = 0; i < this.viewContainers.length; i++) {
|
for (var i = 0; i < this.viewContainers.length; i++) {
|
||||||
this.viewContainers[i].dehydrate();
|
var vc = this.viewContainers[i];
|
||||||
|
if (isPresent(vc)) {
|
||||||
|
vc.dehydrate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dehydrateContext();
|
this._dehydrateContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the event handlers for the element and the directives.
|
||||||
|
*
|
||||||
|
* This method is intended to be called from directive EventEmitters.
|
||||||
|
*
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {*} eventObj
|
||||||
|
* @param {int} binderIndex
|
||||||
|
*/
|
||||||
|
triggerEventHandlers(eventName: string, eventObj, binderIndex: int) {
|
||||||
|
var handlers = this.proto.eventHandlers[binderIndex];
|
||||||
|
if (isBlank(handlers)) return;
|
||||||
|
var handler = StringMapWrapper.get(handlers, eventName);
|
||||||
|
if (isBlank(handler)) return;
|
||||||
|
handler(eventObj, this);
|
||||||
|
}
|
||||||
|
|
||||||
onRecordChange(directiveMemento, records:List) {
|
onRecordChange(directiveMemento, records:List) {
|
||||||
this._invokeMementos(records);
|
this._invokeMementos(records);
|
||||||
if (directiveMemento instanceof DirectiveMemento) {
|
if (directiveMemento instanceof DirectiveMemento) {
|
||||||
@ -262,12 +281,15 @@ export class View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class ProtoView {
|
export class ProtoView {
|
||||||
element;
|
element;
|
||||||
elementBinders:List<ElementBinder>;
|
elementBinders:List<ElementBinder>;
|
||||||
protoChangeDetector:ProtoChangeDetector;
|
protoChangeDetector:ProtoChangeDetector;
|
||||||
variableBindings: Map;
|
variableBindings: Map;
|
||||||
protoContextLocals:Map;
|
protoLocals:Map;
|
||||||
textNodesWithBindingCount:int;
|
textNodesWithBindingCount:int;
|
||||||
elementsWithBindingCount:int;
|
elementsWithBindingCount:int;
|
||||||
instantiateInPlace:boolean;
|
instantiateInPlace:boolean;
|
||||||
@ -276,16 +298,22 @@ export class ProtoView {
|
|||||||
shadowDomStrategy: ShadowDomStrategy;
|
shadowDomStrategy: ShadowDomStrategy;
|
||||||
_viewPool: ViewPool;
|
_viewPool: ViewPool;
|
||||||
stylePromises: List<Promise>;
|
stylePromises: List<Promise>;
|
||||||
|
// List<Map<eventName, handler>>, indexed by binder index
|
||||||
|
eventHandlers:List;
|
||||||
|
bindingRecords:List;
|
||||||
|
parentProtoView:ProtoView;
|
||||||
|
_variableBindings:List;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
template,
|
template,
|
||||||
protoChangeDetector:ProtoChangeDetector,
|
protoChangeDetector:ProtoChangeDetector,
|
||||||
shadowDomStrategy: ShadowDomStrategy) {
|
shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) {
|
||||||
this.element = template;
|
this.element = template;
|
||||||
this.elementBinders = [];
|
this.elementBinders = [];
|
||||||
this.variableBindings = MapWrapper.create();
|
this.variableBindings = MapWrapper.create();
|
||||||
this.protoContextLocals = MapWrapper.create();
|
this.protoLocals = MapWrapper.create();
|
||||||
this.protoChangeDetector = protoChangeDetector;
|
this.protoChangeDetector = protoChangeDetector;
|
||||||
|
this.parentProtoView = parentProtoView;
|
||||||
this.textNodesWithBindingCount = 0;
|
this.textNodesWithBindingCount = 0;
|
||||||
this.elementsWithBindingCount = 0;
|
this.elementsWithBindingCount = 0;
|
||||||
this.instantiateInPlace = false;
|
this.instantiateInPlace = false;
|
||||||
@ -295,6 +323,9 @@ export class ProtoView {
|
|||||||
this.shadowDomStrategy = shadowDomStrategy;
|
this.shadowDomStrategy = shadowDomStrategy;
|
||||||
this._viewPool = new ViewPool(VIEW_POOL_CAPACITY);
|
this._viewPool = new ViewPool(VIEW_POOL_CAPACITY);
|
||||||
this.stylePromises = [];
|
this.stylePromises = [];
|
||||||
|
this.eventHandlers = [];
|
||||||
|
this.bindingRecords = [];
|
||||||
|
this._variableBindings = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||||
@ -310,6 +341,23 @@ export class ProtoView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this work should be done the constructor of ProtoView once we separate
|
||||||
|
// ProtoView and ProtoViewBuilder
|
||||||
|
_getVariableBindings() {
|
||||||
|
if (isPresent(this._variableBindings)) {
|
||||||
|
return this._variableBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._variableBindings = isPresent(this.parentProtoView) ?
|
||||||
|
ListWrapper.clone(this.parentProtoView._getVariableBindings()) : [];
|
||||||
|
|
||||||
|
MapWrapper.forEach(this.protoLocals, (v, local) => {
|
||||||
|
ListWrapper.push(this._variableBindings, local);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._variableBindings;
|
||||||
|
}
|
||||||
|
|
||||||
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
||||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
|
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
|
||||||
var elementsWithBindingsDynamic;
|
var elementsWithBindingsDynamic;
|
||||||
@ -320,8 +368,8 @@ export class ProtoView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var elementsWithBindings = ListWrapper.createFixedSize(elementsWithBindingsDynamic.length);
|
var elementsWithBindings = ListWrapper.createFixedSize(elementsWithBindingsDynamic.length);
|
||||||
for (var i = 0; i < elementsWithBindingsDynamic.length; ++i) {
|
for (var binderIdx = 0; binderIdx < elementsWithBindingsDynamic.length; ++binderIdx) {
|
||||||
elementsWithBindings[i] = elementsWithBindingsDynamic[i];
|
elementsWithBindings[binderIdx] = elementsWithBindingsDynamic[binderIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewNodes;
|
var viewNodes;
|
||||||
@ -337,23 +385,27 @@ export class ProtoView {
|
|||||||
viewNodes = [rootElementClone];
|
viewNodes = [rootElementClone];
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = new View(this, viewNodes, this.protoChangeDetector, this.protoContextLocals);
|
var view = new View(this, viewNodes, this.protoLocals);
|
||||||
|
var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords, this._getVariableBindings());
|
||||||
var binders = this.elementBinders;
|
var binders = this.elementBinders;
|
||||||
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
||||||
|
var eventHandlers = ListWrapper.createFixedSize(binders.length);
|
||||||
var rootElementInjectors = [];
|
var rootElementInjectors = [];
|
||||||
var textNodes = [];
|
var textNodes = [];
|
||||||
var elementsWithPropertyBindings = [];
|
var elementsWithPropertyBindings = [];
|
||||||
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
|
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
|
||||||
var viewContainers = [];
|
var viewContainers = ListWrapper.createFixedSize(binders.length);
|
||||||
|
var contentTags = ListWrapper.createFixedSize(binders.length);
|
||||||
var componentChildViews = [];
|
var componentChildViews = [];
|
||||||
|
var lightDoms = ListWrapper.createFixedSize(binders.length);
|
||||||
|
|
||||||
for (var i = 0; i < binders.length; i++) {
|
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||||
var binder = binders[i];
|
var binder = binders[binderIdx];
|
||||||
var element;
|
var element;
|
||||||
if (i === 0 && this.rootBindingOffset === 1) {
|
if (binderIdx === 0 && this.rootBindingOffset === 1) {
|
||||||
element = rootElementClone;
|
element = rootElementClone;
|
||||||
} else {
|
} else {
|
||||||
element = elementsWithBindings[i - this.rootBindingOffset];
|
element = elementsWithBindings[binderIdx - this.rootBindingOffset];
|
||||||
}
|
}
|
||||||
var elementInjector = null;
|
var elementInjector = null;
|
||||||
|
|
||||||
@ -362,13 +414,13 @@ export class ProtoView {
|
|||||||
if (isPresent(protoElementInjector)) {
|
if (isPresent(protoElementInjector)) {
|
||||||
if (isPresent(protoElementInjector.parent)) {
|
if (isPresent(protoElementInjector.parent)) {
|
||||||
var parentElementInjector = elementInjectors[protoElementInjector.parent.index];
|
var parentElementInjector = elementInjectors[protoElementInjector.parent.index];
|
||||||
elementInjector = protoElementInjector.instantiate(parentElementInjector, null, binder.events);
|
elementInjector = protoElementInjector.instantiate(parentElementInjector, null);
|
||||||
} else {
|
} else {
|
||||||
elementInjector = protoElementInjector.instantiate(null, hostElementInjector, binder.events);
|
elementInjector = protoElementInjector.instantiate(null, hostElementInjector);
|
||||||
ListWrapper.push(rootElementInjectors, elementInjector);
|
ListWrapper.push(rootElementInjectors, elementInjector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elementInjectors[i] = elementInjector;
|
elementInjectors[binderIdx] = elementInjector;
|
||||||
|
|
||||||
if (binder.hasElementPropertyBindings) {
|
if (binder.hasElementPropertyBindings) {
|
||||||
ListWrapper.push(elementsWithPropertyBindings, element);
|
ListWrapper.push(elementsWithPropertyBindings, element);
|
||||||
@ -389,47 +441,64 @@ export class ProtoView {
|
|||||||
// componentChildViews
|
// componentChildViews
|
||||||
var lightDom = null;
|
var lightDom = null;
|
||||||
var bindingPropagationConfig = null;
|
var bindingPropagationConfig = null;
|
||||||
if (isPresent(binder.componentDirective)) {
|
if (isPresent(binder.nestedProtoView) && isPresent(binder.componentDirective)) {
|
||||||
var strategy = this.shadowDomStrategy;
|
var strategy = this.shadowDomStrategy;
|
||||||
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
|
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
|
||||||
view.changeDetector.addChild(childView.changeDetector);
|
changeDetector.addChild(childView.changeDetector);
|
||||||
|
|
||||||
lightDom = strategy.constructLightDom(view, childView, element);
|
lightDom = strategy.constructLightDom(view, childView, element);
|
||||||
strategy.attachTemplate(element, childView);
|
strategy.attachTemplate(element, childView);
|
||||||
|
|
||||||
bindingPropagationConfig = new BindingPropagationConfig(view.changeDetector);
|
bindingPropagationConfig = new BindingPropagationConfig(changeDetector);
|
||||||
|
|
||||||
ListWrapper.push(componentChildViews, childView);
|
ListWrapper.push(componentChildViews, childView);
|
||||||
}
|
}
|
||||||
|
lightDoms[binderIdx] = lightDom;
|
||||||
|
|
||||||
|
var destLightDom = null;
|
||||||
|
if (isPresent(binder.parent) && binder.distanceToParent === 1) {
|
||||||
|
destLightDom = lightDoms[binder.parent.index];
|
||||||
|
}
|
||||||
|
|
||||||
// viewContainers
|
// viewContainers
|
||||||
var viewContainer = null;
|
var viewContainer = null;
|
||||||
if (isPresent(binder.viewportDirective)) {
|
if (isPresent(binder.viewportDirective)) {
|
||||||
var destLightDom = this._directParentElementLightDom(protoElementInjector, preBuiltObjects);
|
|
||||||
viewContainer = new ViewContainer(view, element, binder.nestedProtoView, elementInjector,
|
viewContainer = new ViewContainer(view, element, binder.nestedProtoView, elementInjector,
|
||||||
eventManager, destLightDom);
|
eventManager, destLightDom);
|
||||||
ListWrapper.push(viewContainers, viewContainer);
|
|
||||||
}
|
}
|
||||||
|
viewContainers[binderIdx] = viewContainer;
|
||||||
|
|
||||||
|
// contentTags
|
||||||
|
var contentTag = null;
|
||||||
|
if (isPresent(binder.contentTagSelector)) {
|
||||||
|
contentTag = new Content(destLightDom, element, binder.contentTagSelector);
|
||||||
|
}
|
||||||
|
contentTags[binderIdx] = contentTag;
|
||||||
|
|
||||||
// preBuiltObjects
|
// preBuiltObjects
|
||||||
if (isPresent(elementInjector)) {
|
if (isPresent(elementInjector)) {
|
||||||
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewContainer,
|
preBuiltObjects[binderIdx] = new PreBuiltObjects(view, new NgElement(element), viewContainer,
|
||||||
lightDom, bindingPropagationConfig);
|
bindingPropagationConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// events
|
// events
|
||||||
if (isPresent(binder.events)) {
|
if (isPresent(binder.events)) {
|
||||||
MapWrapper.forEach(binder.events, (expr, eventName) => {
|
eventHandlers[binderIdx] = StringMapWrapper.create();
|
||||||
|
StringMapWrapper.forEach(binder.events, (eventMap, eventName) => {
|
||||||
|
var handler = ProtoView.buildEventHandler(eventMap, binderIdx);
|
||||||
|
StringMapWrapper.set(eventHandlers[binderIdx], eventName, handler);
|
||||||
if (isBlank(elementInjector) || !elementInjector.hasEventEmitter(eventName)) {
|
if (isBlank(elementInjector) || !elementInjector.hasEventEmitter(eventName)) {
|
||||||
var handler = ProtoView.buildInnerCallback(expr, view);
|
eventManager.addEventListener(element, eventName,
|
||||||
eventManager.addEventListener(element, eventName, handler);
|
(event) => { handler(event, view); });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
|
this.eventHandlers = eventHandlers;
|
||||||
viewContainers, preBuiltObjects, componentChildViews);
|
|
||||||
|
view.init(changeDetector, elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
|
||||||
|
viewContainers, contentTags, preBuiltObjects, componentChildViews, lightDoms);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@ -438,34 +507,43 @@ export class ProtoView {
|
|||||||
this._viewPool.push(view);
|
this._viewPool.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildInnerCallback(expr:AST, view:View) {
|
/**
|
||||||
|
* Creates an event handler.
|
||||||
|
*
|
||||||
|
* @param {Map} eventMap Map directiveIndexes to expressions
|
||||||
|
* @param {int} injectorIdx
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
static buildEventHandler(eventMap: Map, injectorIdx: int) {
|
||||||
var locals = MapWrapper.create();
|
var locals = MapWrapper.create();
|
||||||
return (event) => {
|
return (event, view) => {
|
||||||
// Most of the time the event will be fired only when the view is
|
// Most of the time the event will be fired only when the view is in the live document.
|
||||||
// in the live document. However, in a rare circumstance the
|
// However, in a rare circumstance the view might get dehydrated, in between the event
|
||||||
// view might get dehydrated, in between the event queuing up and
|
// queuing up and firing.
|
||||||
// firing.
|
|
||||||
if (view.hydrated()) {
|
if (view.hydrated()) {
|
||||||
MapWrapper.set(locals, '$event', event);
|
MapWrapper.set(locals, '$event', event);
|
||||||
var context = new ContextWithVariableBindings(view.context, locals);
|
MapWrapper.forEach(eventMap, (expr, directiveIndex) => {
|
||||||
expr.eval(context);
|
var context;
|
||||||
|
if (directiveIndex === -1) {
|
||||||
|
context = view.context;
|
||||||
|
} else {
|
||||||
|
context = view.elementInjectors[injectorIdx].getDirectiveAtIndex(directiveIndex);
|
||||||
|
}
|
||||||
|
expr.eval(context, new Locals(view.locals, locals));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_directParentElementLightDom(protoElementInjector:ProtoElementInjector, preBuiltObjects:List):LightDom {
|
|
||||||
var p = protoElementInjector.directParent();
|
|
||||||
return isPresent(p) ? preBuiltObjects[p.index].lightDom : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bindVariable(contextName:string, templateName:string) {
|
bindVariable(contextName:string, templateName:string) {
|
||||||
MapWrapper.set(this.variableBindings, contextName, templateName);
|
MapWrapper.set(this.variableBindings, contextName, templateName);
|
||||||
MapWrapper.set(this.protoContextLocals, templateName, null);
|
MapWrapper.set(this.protoLocals, templateName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindElement(protoElementInjector:ProtoElementInjector,
|
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
|
||||||
componentDirective:DirectiveMetadata = null, viewportDirective:DirectiveMetadata = null):ElementBinder {
|
componentDirective:DirectiveMetadata = null, viewportDirective:DirectiveMetadata = null):ElementBinder {
|
||||||
var elBinder = new ElementBinder(protoElementInjector, componentDirective, viewportDirective);
|
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||||
|
protoElementInjector, componentDirective, viewportDirective);
|
||||||
ListWrapper.push(this.elementBinders, elBinder);
|
ListWrapper.push(this.elementBinders, elBinder);
|
||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
@ -480,7 +558,7 @@ export class ProtoView {
|
|||||||
}
|
}
|
||||||
ListWrapper.push(elBinder.textNodeIndices, indexInParent);
|
ListWrapper.push(elBinder.textNodeIndices, indexInParent);
|
||||||
var memento = this.textNodesWithBindingCount++;
|
var memento = this.textNodesWithBindingCount++;
|
||||||
this.protoChangeDetector.addAst(expression, memento);
|
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -493,18 +571,35 @@ export class ProtoView {
|
|||||||
this.elementsWithBindingCount++;
|
this.elementsWithBindingCount++;
|
||||||
}
|
}
|
||||||
var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setterName, setter);
|
var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setterName, setter);
|
||||||
this.protoChangeDetector.addAst(expression, memento);
|
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an event binding for the last created ElementBinder via bindElement
|
* Adds an event binding for the last created ElementBinder via bindElement.
|
||||||
|
*
|
||||||
|
* If the directive index is a positive integer, the event is evaluated in the context of
|
||||||
|
* the given directive.
|
||||||
|
*
|
||||||
|
* If the directive index is -1, the event is evaluated in the context of the enclosing view.
|
||||||
|
*
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {AST} expression
|
||||||
|
* @param {int} directiveIndex The directive index in the binder or -1 when the event is not bound
|
||||||
|
* to a directive
|
||||||
*/
|
*/
|
||||||
bindEvent(eventName:string, expression:AST) {
|
bindEvent(eventName:string, expression:AST, directiveIndex: int = -1) {
|
||||||
var elBinder = this.elementBinders[this.elementBinders.length-1];
|
var elBinder = this.elementBinders[this.elementBinders.length - 1];
|
||||||
if (isBlank(elBinder.events)) {
|
var events = elBinder.events;
|
||||||
elBinder.events = MapWrapper.create();
|
if (isBlank(events)) {
|
||||||
|
events = StringMapWrapper.create();
|
||||||
|
elBinder.events = events;
|
||||||
}
|
}
|
||||||
MapWrapper.set(elBinder.events, eventName, expression);
|
var event = StringMapWrapper.get(events, eventName);
|
||||||
|
if (isBlank(event)) {
|
||||||
|
event = MapWrapper.create();
|
||||||
|
StringMapWrapper.set(events, eventName, event);
|
||||||
|
}
|
||||||
|
MapWrapper.set(event, directiveIndex, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -523,7 +618,7 @@ export class ProtoView {
|
|||||||
setter
|
setter
|
||||||
);
|
);
|
||||||
var directiveMemento = DirectiveMemento.get(bindingMemento);
|
var directiveMemento = DirectiveMemento.get(bindingMemento);
|
||||||
this.protoChangeDetector.addAst(expression, bindingMemento, directiveMemento);
|
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, bindingMemento, directiveMemento));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
||||||
@ -540,7 +635,7 @@ export class ProtoView {
|
|||||||
var cmpType = rootComponentAnnotatedType.type;
|
var cmpType = rootComponentAnnotatedType.type;
|
||||||
var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy);
|
var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy);
|
||||||
rootProtoView.instantiateInPlace = true;
|
rootProtoView.instantiateInPlace = true;
|
||||||
var binder = rootProtoView.bindElement(
|
var binder = rootProtoView.bindElement(null, 0,
|
||||||
new ProtoElementInjector(null, 0, [cmpType], true));
|
new ProtoElementInjector(null, 0, [cmpType], true));
|
||||||
binder.componentDirective = rootComponentAnnotatedType;
|
binder.componentDirective = rootComponentAnnotatedType;
|
||||||
binder.nestedProtoView = protoView;
|
binder.nestedProtoView = protoView;
|
||||||
@ -549,6 +644,9 @@ export class ProtoView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class ElementBindingMemento {
|
export class ElementBindingMemento {
|
||||||
_elementIndex:int;
|
_elementIndex:int;
|
||||||
_setterName:string;
|
_setterName:string;
|
||||||
@ -565,6 +663,9 @@ export class ElementBindingMemento {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class DirectiveBindingMemento {
|
export class DirectiveBindingMemento {
|
||||||
_elementInjectorIndex:int;
|
_elementInjectorIndex:int;
|
||||||
_directiveIndex:int;
|
_directiveIndex:int;
|
||||||
@ -621,7 +722,10 @@ class DirectiveMemento {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PropertyUpdate {
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
|
export class PropertyUpdate {
|
||||||
currentValue;
|
currentValue;
|
||||||
previousValue;
|
previousValue;
|
||||||
|
|
||||||
@ -629,4 +733,8 @@ class PropertyUpdate {
|
|||||||
this.currentValue = currentValue;
|
this.currentValue = currentValue;
|
||||||
this.previousValue = previousValue;
|
this.previousValue = previousValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createWithoutPrevious(currentValue) {
|
||||||
|
return new PropertyUpdate(currentValue, uninitialized);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,29 @@ import {Injector} from 'angular2/di';
|
|||||||
import * as eiModule from 'angular2/src/core/compiler/element_injector';
|
import * as eiModule from 'angular2/src/core/compiler/element_injector';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
|
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class ViewContainer {
|
export class ViewContainer {
|
||||||
parentView: viewModule.View;
|
parentView: viewModule.View;
|
||||||
templateElement;
|
templateElement;
|
||||||
defaultProtoView: viewModule.ProtoView;
|
defaultProtoView: viewModule.ProtoView;
|
||||||
_views: List<viewModule.View>;
|
_views: List<viewModule.View>;
|
||||||
_lightDom: any;
|
_lightDom: LightDom;
|
||||||
_eventManager: EventManager;
|
_eventManager: EventManager;
|
||||||
elementInjector: eiModule.ElementInjector;
|
elementInjector: eiModule.ElementInjector;
|
||||||
appInjector: Injector;
|
appInjector: Injector;
|
||||||
hostElementInjector: eiModule.ElementInjector;
|
hostElementInjector: eiModule.ElementInjector;
|
||||||
|
hostLightDom: LightDom;
|
||||||
|
|
||||||
constructor(parentView: viewModule.View, templateElement, defaultProtoView: viewModule.ProtoView,
|
constructor(parentView: viewModule.View,
|
||||||
elementInjector: eiModule.ElementInjector, eventManager: EventManager, lightDom = null) {
|
templateElement,
|
||||||
|
defaultProtoView: viewModule.ProtoView,
|
||||||
|
elementInjector: eiModule.ElementInjector,
|
||||||
|
eventManager: EventManager,
|
||||||
|
lightDom = null) {
|
||||||
this.parentView = parentView;
|
this.parentView = parentView;
|
||||||
this.templateElement = templateElement;
|
this.templateElement = templateElement;
|
||||||
this.defaultProtoView = defaultProtoView;
|
this.defaultProtoView = defaultProtoView;
|
||||||
@ -30,17 +39,20 @@ export class ViewContainer {
|
|||||||
this._views = [];
|
this._views = [];
|
||||||
this.appInjector = null;
|
this.appInjector = null;
|
||||||
this.hostElementInjector = null;
|
this.hostElementInjector = null;
|
||||||
|
this.hostLightDom = null;
|
||||||
this._eventManager = eventManager;
|
this._eventManager = eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector) {
|
hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector, hostLightDom: LightDom) {
|
||||||
this.appInjector = appInjector;
|
this.appInjector = appInjector;
|
||||||
this.hostElementInjector = hostElementInjector;
|
this.hostElementInjector = hostElementInjector;
|
||||||
|
this.hostLightDom = hostLightDom;
|
||||||
}
|
}
|
||||||
|
|
||||||
dehydrate() {
|
dehydrate() {
|
||||||
this.appInjector = null;
|
this.appInjector = null;
|
||||||
this.hostElementInjector = null;
|
this.hostElementInjector = null;
|
||||||
|
this.hostLightDom = null;
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +88,13 @@ export class ViewContainer {
|
|||||||
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
|
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
|
||||||
// insertion must come before hydration so that element injector trees are attached.
|
// insertion must come before hydration so that element injector trees are attached.
|
||||||
this.insert(newView, atIndex);
|
this.insert(newView, atIndex);
|
||||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);
|
newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom,
|
||||||
|
this.parentView.context, this.parentView.locals);
|
||||||
|
|
||||||
|
// new content tags might have appeared, we need to redistrubute.
|
||||||
|
if (isPresent(this.hostLightDom)) {
|
||||||
|
this.hostLightDom.redistribute();
|
||||||
|
}
|
||||||
return newView;
|
return newView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +108,7 @@ export class ViewContainer {
|
|||||||
}
|
}
|
||||||
this.parentView.changeDetector.addChild(view.changeDetector);
|
this.parentView.changeDetector.addChild(view.changeDetector);
|
||||||
this._linkElementInjectors(view);
|
this._linkElementInjectors(view);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +134,10 @@ export class ViewContainer {
|
|||||||
} else {
|
} else {
|
||||||
this._lightDom.redistribute();
|
this._lightDom.redistribute();
|
||||||
}
|
}
|
||||||
|
// content tags might have disappeared we need to do redistribution.
|
||||||
|
if (isPresent(this.hostLightDom)) {
|
||||||
|
this.hostLightDom.redistribute();
|
||||||
|
}
|
||||||
detachedView.changeDetector.remove();
|
detachedView.changeDetector.remove();
|
||||||
this._unlinkElementInjectors(detachedView);
|
this._unlinkElementInjectors(detachedView);
|
||||||
return detachedView;
|
return detachedView;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
|
import 'package:angular2/di.dart';
|
||||||
import './xhr.dart' show XHR;
|
import './xhr.dart' show XHR;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
class XHRImpl extends XHR {
|
class XHRImpl extends XHR {
|
||||||
Future<String> get(String url) {
|
Future<String> get(String url) {
|
||||||
return HttpRequest.request(url).then(
|
return HttpRequest.request(url).then(
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {XHR} from './xhr';
|
import {XHR} from './xhr';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class XHRImpl extends XHR {
|
export class XHRImpl extends XHR {
|
||||||
get(url: string): Promise<string> {
|
get(url: string): Promise<string> {
|
||||||
var completer = PromiseWrapper.completer();
|
var completer = PromiseWrapper.completer();
|
||||||
|
3
modules/angular2/src/core/dom/element.js
vendored
3
modules/angular2/src/core/dom/element.js
vendored
@ -1,6 +1,9 @@
|
|||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {normalizeBlank} from 'angular2/src/facade/lang';
|
import {normalizeBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @publicModule angular2/angular2
|
||||||
|
*/
|
||||||
export class NgElement {
|
export class NgElement {
|
||||||
domElement;
|
domElement;
|
||||||
constructor(domElement) {
|
constructor(domElement) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {isPresent, print} from 'angular2/src/facade/lang';
|
import {isPresent, print} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class ExceptionHandler {
|
export class ExceptionHandler {
|
||||||
call(error, stackTrace = null, reason = null) {
|
call(error, stackTrace = null, reason = null) {
|
||||||
var longStackTrace = isListLikeIterable(stackTrace) ? ListWrapper.join(stackTrace, "\n\n") : stackTrace;
|
var longStackTrace = isListLikeIterable(stackTrace) ? ListWrapper.join(stackTrace, "\n\n") : stackTrace;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class LifeCycle {
|
export class LifeCycle {
|
||||||
_errorHandler;
|
_errorHandler;
|
||||||
_changeDetector:ChangeDetector;
|
_changeDetector:ChangeDetector;
|
||||||
|
21
modules/angular2/src/di/annotations.js
vendored
21
modules/angular2/src/di/annotations.js
vendored
@ -107,4 +107,23 @@ export class DependencyAnnotation {
|
|||||||
@CONST()
|
@CONST()
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class annotation that marks a class as available to `Injector`s for
|
||||||
|
* creation.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class NeedsService {
|
||||||
|
* constructor(svc:UsefulService) {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Injectable
|
||||||
|
* class UsefulService {}
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class Injectable {
|
||||||
|
@CONST()
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
modules/angular2/src/di/binding.js
vendored
2
modules/angular2/src/di/binding.js
vendored
@ -1,4 +1,4 @@
|
|||||||
import {FIELD, Type, isBlank, isPresent} from 'angular2/src/facade/lang';
|
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
import {Key} from './key';
|
import {Key} from './key';
|
||||||
|
14
modules/angular2/src/di/injector.js
vendored
14
modules/angular2/src/di/injector.js
vendored
@ -7,6 +7,7 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|||||||
import {Key} from './key';
|
import {Key} from './key';
|
||||||
|
|
||||||
var _constructing = new Object();
|
var _constructing = new Object();
|
||||||
|
var _notFound = new Object();
|
||||||
|
|
||||||
class _Waiting {
|
class _Waiting {
|
||||||
promise:Promise;
|
promise:Promise;
|
||||||
@ -72,10 +73,10 @@ export class Injector {
|
|||||||
var strategy = returnPromise ? this._asyncStrategy : this._syncStrategy;
|
var strategy = returnPromise ? this._asyncStrategy : this._syncStrategy;
|
||||||
|
|
||||||
var instance = strategy.readFromCache(key);
|
var instance = strategy.readFromCache(key);
|
||||||
if (isPresent(instance)) return instance;
|
if (instance !== _notFound) return instance;
|
||||||
|
|
||||||
instance = strategy.instantiate(key);
|
instance = strategy.instantiate(key);
|
||||||
if (isPresent(instance)) return instance;
|
if (instance !== _notFound) return instance;
|
||||||
|
|
||||||
if (isPresent(this._parent)) {
|
if (isPresent(this._parent)) {
|
||||||
return this._parent._getByKey(key, returnPromise, returnLazy, optional);
|
return this._parent._getByKey(key, returnPromise, returnLazy, optional);
|
||||||
@ -148,13 +149,13 @@ class _SyncInjectorStrategy {
|
|||||||
} else if (isPresent(instance) && !_isWaiting(instance)) {
|
} else if (isPresent(instance) && !_isWaiting(instance)) {
|
||||||
return instance;
|
return instance;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return _notFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(key:Key) {
|
instantiate(key:Key) {
|
||||||
var binding = this.injector._getBinding(key);
|
var binding = this.injector._getBinding(key);
|
||||||
if (isBlank(binding)) return null;
|
if (isBlank(binding)) return _notFound;
|
||||||
|
|
||||||
if (binding.providedAsPromise) throw new AsyncBindingError(key);
|
if (binding.providedAsPromise) throw new AsyncBindingError(key);
|
||||||
|
|
||||||
@ -198,13 +199,13 @@ class _AsyncInjectorStrategy {
|
|||||||
} else if (isPresent(instance)) {
|
} else if (isPresent(instance)) {
|
||||||
return PromiseWrapper.resolve(instance);
|
return PromiseWrapper.resolve(instance);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return _notFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(key:Key) {
|
instantiate(key:Key) {
|
||||||
var binding = this.injector._getBinding(key);
|
var binding = this.injector._getBinding(key);
|
||||||
if (isBlank(binding)) return null;
|
if (isBlank(binding)) return _notFound;
|
||||||
|
|
||||||
//add a marker so we can detect cyclic dependencies
|
//add a marker so we can detect cyclic dependencies
|
||||||
this.injector._markAsConstructing(key);
|
this.injector._markAsConstructing(key);
|
||||||
@ -243,7 +244,6 @@ class _AsyncInjectorStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _flattenBindings(bindings:List, res:Map) {
|
function _flattenBindings(bindings:List, res:Map) {
|
||||||
ListWrapper.forEach(bindings, function (b) {
|
ListWrapper.forEach(bindings, function (b) {
|
||||||
if (b instanceof Binding) {
|
if (b instanceof Binding) {
|
||||||
|
2
modules/angular2/src/di/key.js
vendored
2
modules/angular2/src/di/key.js
vendored
@ -1,6 +1,6 @@
|
|||||||
import {KeyMetadataError} from './exceptions';
|
import {KeyMetadataError} from './exceptions';
|
||||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||||
import {FIELD, int, isPresent} from 'angular2/src/facade/lang';
|
import {int, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
export class Key {
|
export class Key {
|
||||||
token;
|
token;
|
||||||
|
@ -2,7 +2,8 @@ library angular.core.facade.dom;
|
|||||||
|
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
import 'dart:js' show JsObject;
|
import 'dart:js' show JsObject;
|
||||||
import 'dom_adapter.dart' show setRootDomAdapter, DomAdapter;
|
import 'dom_adapter.dart' show setRootDomAdapter;
|
||||||
|
import 'generic_browser_adapter.dart' show GenericBrowserDomAdapter;
|
||||||
import '../facade/browser.dart';
|
import '../facade/browser.dart';
|
||||||
|
|
||||||
// WARNING: Do not expose outside this class. Parsing HTML using this
|
// WARNING: Do not expose outside this class. Parsing HTML using this
|
||||||
@ -13,14 +14,14 @@ class _IdentitySanitizer implements NodeTreeSanitizer {
|
|||||||
|
|
||||||
final _identitySanitizer = new _IdentitySanitizer();
|
final _identitySanitizer = new _IdentitySanitizer();
|
||||||
|
|
||||||
class BrowserDomAdapter extends DomAdapter {
|
class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
static void makeCurrent() {
|
static void makeCurrent() {
|
||||||
setRootDomAdapter(new BrowserDomAdapter());
|
setRootDomAdapter(new BrowserDomAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final attrToPropMap = const {
|
final attrToPropMap = const {
|
||||||
'inner-html': 'innerHtml',
|
'innerHtml': 'innerHtml',
|
||||||
'readonly': 'readOnly',
|
'readonly': 'readOnly',
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex',
|
||||||
};
|
};
|
||||||
@ -150,8 +151,9 @@ class BrowserDomAdapter extends DomAdapter {
|
|||||||
|
|
||||||
String tagName(Element element) => element.tagName;
|
String tagName(Element element) => element.tagName;
|
||||||
|
|
||||||
Map<String, String> attributeMap(Element element) =>
|
Map<String, String> attributeMap(Element element) {
|
||||||
element.attributes;
|
return new Map.from(element.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
String getAttribute(Element element, String attribute) =>
|
String getAttribute(Element element, String attribute) =>
|
||||||
element.getAttribute(attribute);
|
element.getAttribute(attribute);
|
||||||
@ -173,6 +175,10 @@ class BrowserDomAdapter extends DomAdapter {
|
|||||||
document.implementation.createHtmlDocument('fakeTitle');
|
document.implementation.createHtmlDocument('fakeTitle');
|
||||||
|
|
||||||
HtmlDocument defaultDoc() => document;
|
HtmlDocument defaultDoc() => document;
|
||||||
|
String getTitle() => document.title;
|
||||||
|
void setTitle(String newTitle) {
|
||||||
|
document.title = newTitle;
|
||||||
|
}
|
||||||
bool elementMatches(n, String selector) =>
|
bool elementMatches(n, String selector) =>
|
||||||
n is Element && n.matches(selector);
|
n is Element && n.matches(selector);
|
||||||
bool isTemplateElement(Element el) =>
|
bool isTemplateElement(Element el) =>
|
||||||
@ -192,4 +198,7 @@ class BrowserDomAdapter extends DomAdapter {
|
|||||||
bool isStyleRule(CssRule rule) => rule is CssStyleRule;
|
bool isStyleRule(CssRule rule) => rule is CssStyleRule;
|
||||||
bool isMediaRule(CssRule rule) => rule is CssMediaRule;
|
bool isMediaRule(CssRule rule) => rule is CssMediaRule;
|
||||||
bool isKeyframesRule(CssRule rule) => rule is CssKeyframesRule;
|
bool isKeyframesRule(CssRule rule) => rule is CssKeyframesRule;
|
||||||
|
String getHref(AnchorElement element) {
|
||||||
|
return element.href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {DomAdapter, setRootDomAdapter} from './dom_adapter';
|
import {setRootDomAdapter} from './dom_adapter';
|
||||||
|
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
||||||
|
|
||||||
var _attrToPropMap = {
|
var _attrToPropMap = {
|
||||||
'inner-html': 'innerHTML',
|
'innerHtml': 'innerHTML',
|
||||||
'readonly': 'readOnly',
|
'readonly': 'readOnly',
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BrowserDomAdapter extends DomAdapter {
|
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
static makeCurrent() {
|
static makeCurrent() {
|
||||||
setRootDomAdapter(new BrowserDomAdapter());
|
setRootDomAdapter(new BrowserDomAdapter());
|
||||||
}
|
}
|
||||||
@ -79,7 +80,9 @@ export class BrowserDomAdapter extends DomAdapter {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
clearNodes(el) {
|
clearNodes(el) {
|
||||||
el.innerHTML = '';
|
for (var i = 0; i < el.childNodes.length; i++) {
|
||||||
|
this.remove(el.childNodes[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appendChild(el, node) {
|
appendChild(el, node) {
|
||||||
el.appendChild(node);
|
el.appendChild(node);
|
||||||
@ -215,6 +218,12 @@ export class BrowserDomAdapter extends DomAdapter {
|
|||||||
defaultDoc() {
|
defaultDoc() {
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
getTitle() {
|
||||||
|
return document.title;
|
||||||
|
}
|
||||||
|
setTitle(newTitle:string) {
|
||||||
|
document.title = newTitle;
|
||||||
|
}
|
||||||
elementMatches(n, selector:string):boolean {
|
elementMatches(n, selector:string):boolean {
|
||||||
return n instanceof HTMLElement && n.matches(selector);
|
return n instanceof HTMLElement && n.matches(selector);
|
||||||
}
|
}
|
||||||
@ -258,4 +267,7 @@ export class BrowserDomAdapter extends DomAdapter {
|
|||||||
isKeyframesRule(rule): boolean {
|
isKeyframesRule(rule): boolean {
|
||||||
return rule.type === CSSRule.KEYFRAMES_RULE;
|
return rule.type === CSSRule.KEYFRAMES_RULE;
|
||||||
}
|
}
|
||||||
|
getHref(el:Element): string {
|
||||||
|
return el.href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
modules/angular2/src/dom/dom_adapter.js
vendored
15
modules/angular2/src/dom/dom_adapter.js
vendored
@ -201,6 +201,12 @@ export class DomAdapter {
|
|||||||
defaultDoc() {
|
defaultDoc() {
|
||||||
throw _abstract();
|
throw _abstract();
|
||||||
}
|
}
|
||||||
|
getTitle() {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
|
setTitle(newTitle:string) {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
elementMatches(n, selector:string):boolean {
|
elementMatches(n, selector:string):boolean {
|
||||||
throw _abstract();
|
throw _abstract();
|
||||||
}
|
}
|
||||||
@ -234,4 +240,13 @@ export class DomAdapter {
|
|||||||
isKeyframesRule(rule): boolean {
|
isKeyframesRule(rule): boolean {
|
||||||
throw _abstract();
|
throw _abstract();
|
||||||
}
|
}
|
||||||
|
getHref(element): string {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
|
resolveAndSetHref(element, baseUrl:string, href:string) {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
|
cssToRules(css:string): List {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
37
modules/angular2/src/dom/generic_browser_adapter.js
vendored
Normal file
37
modules/angular2/src/dom/generic_browser_adapter.js
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {ABSTRACT} from 'angular2/src/facade/lang';
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {DomAdapter} from './dom_adapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides DOM operations in any browser environment.
|
||||||
|
*/
|
||||||
|
@ABSTRACT()
|
||||||
|
export class GenericBrowserDomAdapter extends DomAdapter {
|
||||||
|
resolveAndSetHref(el, baseUrl:string, href:string) {
|
||||||
|
el.href = href == null ? baseUrl : baseUrl + '/../' + href;
|
||||||
|
}
|
||||||
|
cssToRules(css:string): List {
|
||||||
|
var style = this.createStyleElement(css);
|
||||||
|
this.appendChild(this.defaultDoc().head, style);
|
||||||
|
var rules = ListWrapper.create();
|
||||||
|
if (isPresent(style.sheet)) {
|
||||||
|
// TODO(sorvell): Firefox throws when accessing the rules of a stylesheet
|
||||||
|
// with an @import
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=625013
|
||||||
|
try {
|
||||||
|
var rawRules = style.sheet.cssRules;
|
||||||
|
rules = ListWrapper.createFixedSize(rawRules.length);
|
||||||
|
for (var i=0; i<rawRules.length; i++) {
|
||||||
|
rules[i] = rawRules[i];
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// console.warn('sheet not found', style);
|
||||||
|
}
|
||||||
|
this.remove(style);
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
}
|
@ -226,4 +226,13 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||||||
bool isKeyframesRule(rule) {
|
bool isKeyframesRule(rule) {
|
||||||
throw 'not implemented';
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
|
String getHref(element) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
|
void resolveAndSetHref(element, baseUrl, href) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
|
List cssToRules(String css) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
482
modules/angular2/src/dom/parse5_adapter.cjs
Normal file
482
modules/angular2/src/dom/parse5_adapter.cjs
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
var parse5 = require('parse5');
|
||||||
|
var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2);
|
||||||
|
var serializer = new parse5.Serializer(parse5.TreeAdapters.htmlparser2);
|
||||||
|
var treeAdapter = parser.treeAdapter;
|
||||||
|
|
||||||
|
var cssParse = require('css-parse');
|
||||||
|
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
import {List, MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {DomAdapter, setRootDomAdapter} from './dom_adapter';
|
||||||
|
import {BaseException, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {SelectorMatcher, CssSelector} from 'angular2/src/core/compiler/selector';
|
||||||
|
|
||||||
|
var _attrToPropMap = {
|
||||||
|
'innerHtml': 'innerHTML',
|
||||||
|
'readonly': 'readOnly',
|
||||||
|
'tabindex': 'tabIndex',
|
||||||
|
};
|
||||||
|
var defDoc = null;
|
||||||
|
|
||||||
|
function _notImplemented(methodName) {
|
||||||
|
return new BaseException('This method is not implemented in Parse5DomAdapter: ' + methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Parse5DomAdapter extends DomAdapter {
|
||||||
|
static makeCurrent() {
|
||||||
|
setRootDomAdapter(new Parse5DomAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
get attrToPropMap() {
|
||||||
|
return _attrToPropMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
query(selector) {
|
||||||
|
throw _notImplemented('query');
|
||||||
|
}
|
||||||
|
querySelector(el, selector:string) {
|
||||||
|
return this.querySelectorAll(el, selector)[0];
|
||||||
|
}
|
||||||
|
querySelectorAll(el, selector:string) {
|
||||||
|
var res = ListWrapper.create();
|
||||||
|
var _recursive = (result, node, selector, matcher) => {
|
||||||
|
if (this.elementMatches(node, selector, matcher)) {
|
||||||
|
ListWrapper.push(result, node);
|
||||||
|
}
|
||||||
|
var cNodes = node.childNodes;
|
||||||
|
if (cNodes && cNodes.length > 0) {
|
||||||
|
for (var i = 0; i < cNodes.length; i++) {
|
||||||
|
_recursive(result, cNodes[i], selector, matcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var matcher = new SelectorMatcher();
|
||||||
|
matcher.addSelectable(CssSelector.parse(selector));
|
||||||
|
_recursive(res, el, selector, matcher);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
elementMatches(node, selector:string, matcher = null):boolean {
|
||||||
|
var result = false;
|
||||||
|
if (selector && selector.charAt(0) == "#") {
|
||||||
|
result = this.getAttribute(node, 'id') == selector.substring(1);
|
||||||
|
} else if (selector) {
|
||||||
|
var result = false;
|
||||||
|
if (matcher == null) {
|
||||||
|
matcher = new SelectorMatcher();
|
||||||
|
matcher.addSelectable(CssSelector.parse(selector));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cssSelector = new CssSelector();
|
||||||
|
cssSelector.setElement(this.tagName(node));
|
||||||
|
if (node.attribs) {
|
||||||
|
for (var attrName in node.attribs) {
|
||||||
|
cssSelector.addAttribute(attrName, node.attribs[attrName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var classList = this.classList(node);
|
||||||
|
for (var i = 0; i < classList.length; i++) {
|
||||||
|
cssSelector.addClassName(classList[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher.match(cssSelector, function(selector, cb) {result = true;});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
on(el, evt, listener) {
|
||||||
|
//Do nothing, in order to not break forms integration tests
|
||||||
|
}
|
||||||
|
dispatchEvent(el, evt) {
|
||||||
|
throw _notImplemented('dispatchEvent');
|
||||||
|
}
|
||||||
|
createMouseEvent(eventType) {
|
||||||
|
throw _notImplemented('createMouseEvent');
|
||||||
|
}
|
||||||
|
createEvent(eventType) {
|
||||||
|
throw _notImplemented('createEvent');
|
||||||
|
}
|
||||||
|
getInnerHTML(el) {
|
||||||
|
return serializer.serialize(this.templateAwareRoot(el));
|
||||||
|
}
|
||||||
|
getOuterHTML(el) {
|
||||||
|
serializer.html = '';
|
||||||
|
serializer._serializeElement(el);
|
||||||
|
return serializer.html;
|
||||||
|
}
|
||||||
|
nodeName(node):string {
|
||||||
|
return node.tagName;
|
||||||
|
}
|
||||||
|
nodeValue(node):string {
|
||||||
|
return node.nodeValue;
|
||||||
|
}
|
||||||
|
type(node:string) {
|
||||||
|
throw _notImplemented('type');
|
||||||
|
}
|
||||||
|
content(node) {
|
||||||
|
return node.childNodes[0];
|
||||||
|
}
|
||||||
|
firstChild(el) {
|
||||||
|
return el.firstChild;
|
||||||
|
}
|
||||||
|
nextSibling(el) {
|
||||||
|
return el.nextSibling;
|
||||||
|
}
|
||||||
|
parentElement(el) {
|
||||||
|
return el.parent;
|
||||||
|
}
|
||||||
|
childNodes(el) {
|
||||||
|
return el.childNodes;
|
||||||
|
}
|
||||||
|
childNodesAsList(el):List {
|
||||||
|
var childNodes = el.childNodes;
|
||||||
|
var res = ListWrapper.createFixedSize(childNodes.length);
|
||||||
|
for (var i = 0; i < childNodes.length; i++) {
|
||||||
|
res[i] = childNodes[i];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
clearNodes(el) {
|
||||||
|
while (el.childNodes.length > 0) {
|
||||||
|
this.remove(el.childNodes[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendChild(el, node) {
|
||||||
|
this.remove(node);
|
||||||
|
treeAdapter.appendChild(this.templateAwareRoot(el), node);
|
||||||
|
}
|
||||||
|
removeChild(el, node) {
|
||||||
|
if (ListWrapper.contains(el.childNodes, node)) {
|
||||||
|
this.remove(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove(el) {
|
||||||
|
var parent = el.parent;
|
||||||
|
if (parent) {
|
||||||
|
var index = parent.childNodes.indexOf(el);
|
||||||
|
parent.childNodes.splice(index, 1);
|
||||||
|
}
|
||||||
|
var prev = el.previousSibling;
|
||||||
|
var next = el.nextSibling;
|
||||||
|
if (prev) {
|
||||||
|
prev.next = next;
|
||||||
|
}
|
||||||
|
if (next) {
|
||||||
|
next.prev = prev;
|
||||||
|
}
|
||||||
|
el.prev = null;
|
||||||
|
el.next = null;
|
||||||
|
el.parent = null;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
insertBefore(el, node) {
|
||||||
|
this.remove(node);
|
||||||
|
treeAdapter.insertBefore(el.parent, node, el);
|
||||||
|
}
|
||||||
|
insertAllBefore(el, nodes) {
|
||||||
|
ListWrapper.forEach(nodes, (n) => {
|
||||||
|
this.insertBefore(el, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
insertAfter(el, node) {
|
||||||
|
if (el.nextSibling) {
|
||||||
|
this.insertBefore(el.nextSibling, node);
|
||||||
|
} else {
|
||||||
|
this.appendChild(el.parent, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInnerHTML(el, value) {
|
||||||
|
this.clearNodes(el);
|
||||||
|
var content = parser.parseFragment(value);
|
||||||
|
for (var i = 0; i < content.childNodes.length; i++) {
|
||||||
|
treeAdapter.appendChild(el, content.childNodes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getText(el) {
|
||||||
|
if (this.isTextNode(el)) {
|
||||||
|
return el.data;
|
||||||
|
} else if (el.childNodes.length == 0) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
var textContent = "";
|
||||||
|
for (var i = 0; i < el.childNodes.length; i++) {
|
||||||
|
textContent += this.getText(el.childNodes[i]);
|
||||||
|
}
|
||||||
|
return textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setText(el, value:string) {
|
||||||
|
if (this.isTextNode(el)) {
|
||||||
|
el.data = value;
|
||||||
|
} else {
|
||||||
|
this.clearNodes(el);
|
||||||
|
treeAdapter.insertText(el, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getValue(el) {
|
||||||
|
return el.value;
|
||||||
|
}
|
||||||
|
setValue(el, value:string) {
|
||||||
|
el.value = value;
|
||||||
|
}
|
||||||
|
getChecked(el) {
|
||||||
|
return el.checked;
|
||||||
|
}
|
||||||
|
setChecked(el, value:boolean) {
|
||||||
|
el.checked = value;
|
||||||
|
}
|
||||||
|
createTemplate(html) {
|
||||||
|
var template = treeAdapter.createElement("template", 'http://www.w3.org/1999/xhtml', []);
|
||||||
|
var content = parser.parseFragment(html);
|
||||||
|
treeAdapter.appendChild(template, content);
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
createElement(tagName) {
|
||||||
|
return treeAdapter.createElement(tagName, 'http://www.w3.org/1999/xhtml', []);
|
||||||
|
}
|
||||||
|
createTextNode(text: string) {
|
||||||
|
throw _notImplemented('createTextNode');
|
||||||
|
}
|
||||||
|
createScriptTag(attrName:string, attrValue:string) {
|
||||||
|
return treeAdapter.createElement("script", 'http://www.w3.org/1999/xhtml', [{name: attrName, value: attrValue}]);
|
||||||
|
}
|
||||||
|
createStyleElement(css:string) {
|
||||||
|
var style = this.createElement('style');
|
||||||
|
this.setText(style, css);
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
createShadowRoot(el) {
|
||||||
|
el.shadowRoot = treeAdapter.createDocumentFragment();
|
||||||
|
el.shadowRoot.parent = el;
|
||||||
|
return el.shadowRoot;
|
||||||
|
}
|
||||||
|
getShadowRoot(el) {
|
||||||
|
return el.shadowRoot;
|
||||||
|
}
|
||||||
|
clone(node) {
|
||||||
|
var temp = treeAdapter.createElement("template", null, []);
|
||||||
|
treeAdapter.appendChild(temp, node);
|
||||||
|
var serialized = serializer.serialize(temp);
|
||||||
|
var newParser = new parse5.Parser(parse5.TreeAdapters.htmlparser2);
|
||||||
|
return newParser.parseFragment(serialized).childNodes[0];
|
||||||
|
}
|
||||||
|
hasProperty(element, name:string) {
|
||||||
|
return _HTMLElementPropertyList.indexOf(name) > -1;
|
||||||
|
}
|
||||||
|
getElementsByClassName(element, name:string) {
|
||||||
|
return this.querySelectorAll(element, "." + name);
|
||||||
|
}
|
||||||
|
getElementsByTagName(element, name:string) {
|
||||||
|
throw _notImplemented('getElementsByTagName');
|
||||||
|
}
|
||||||
|
classList(element):List {
|
||||||
|
var classAttrValue = null;
|
||||||
|
var attributes = element.attribs;
|
||||||
|
if (attributes && attributes.hasOwnProperty("class")) {
|
||||||
|
classAttrValue = attributes["class"];
|
||||||
|
}
|
||||||
|
return classAttrValue ? classAttrValue.trim().split(/\s+/g) : [];
|
||||||
|
}
|
||||||
|
addClass(element, classname:string) {
|
||||||
|
var classList = this.classList(element);
|
||||||
|
var index = classList.indexOf(classname);
|
||||||
|
if (index == -1) {
|
||||||
|
ListWrapper.push(classList, classname);
|
||||||
|
element.attribs["class"] = element.className = ListWrapper.join(classList, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeClass(element, classname:string) {
|
||||||
|
var classList = this.classList(element);
|
||||||
|
var index = classList.indexOf(classname);
|
||||||
|
if (index > -1) {
|
||||||
|
classList.splice(index, 1);
|
||||||
|
element.attribs["class"] = element.className = ListWrapper.join(classList, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasClass(element, classname:string) {
|
||||||
|
return ListWrapper.contains(this.classList(element), classname);
|
||||||
|
}
|
||||||
|
_readStyleAttribute(element) {
|
||||||
|
var styleMap = {};
|
||||||
|
var attributes = element.attribs;
|
||||||
|
if (attributes && attributes.hasOwnProperty("style")) {
|
||||||
|
var styleAttrValue = attributes["style"];
|
||||||
|
var styleList = styleAttrValue.split(/;+/g);
|
||||||
|
for (var i = 0; i < styleList.length; i++) {
|
||||||
|
if (styleList[i].length > 0) {
|
||||||
|
var elems = styleList[i].split(/:+/g);
|
||||||
|
styleMap[elems[0].trim()] = elems[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return styleMap;
|
||||||
|
}
|
||||||
|
_writeStyleAttribute(element, styleMap) {
|
||||||
|
var styleAttrValue = "";
|
||||||
|
for (var key in styleMap) {
|
||||||
|
var newValue = styleMap[key];
|
||||||
|
if (newValue && newValue.length > 0) {
|
||||||
|
styleAttrValue += key + ":" + styleMap[key] + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.attribs["style"] = styleAttrValue;
|
||||||
|
}
|
||||||
|
setStyle(element, stylename:string, stylevalue:string) {
|
||||||
|
var styleMap = this._readStyleAttribute(element);
|
||||||
|
styleMap[stylename] = stylevalue;
|
||||||
|
this._writeStyleAttribute(element, styleMap);
|
||||||
|
}
|
||||||
|
removeStyle(element, stylename:string) {
|
||||||
|
this.setStyle(element, stylename, null);
|
||||||
|
}
|
||||||
|
getStyle(element, stylename:string) {
|
||||||
|
var styleMap = this._readStyleAttribute(element);
|
||||||
|
return styleMap.hasOwnProperty(stylename) ? styleMap[stylename] : "";
|
||||||
|
}
|
||||||
|
tagName(element):string {
|
||||||
|
return element.tagName == "style" ? "STYLE" : element.tagName;
|
||||||
|
}
|
||||||
|
attributeMap(element) {
|
||||||
|
var res = MapWrapper.create();
|
||||||
|
var elAttrs = treeAdapter.getAttrList(element);
|
||||||
|
for (var i = 0; i < elAttrs.length; i++) {
|
||||||
|
var attrib = elAttrs[i];
|
||||||
|
MapWrapper.set(res, attrib.name, attrib.value);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
getAttribute(element, attribute:string) {
|
||||||
|
return element.attribs && element.attribs.hasOwnProperty(attribute)? element.attribs[attribute]: null;
|
||||||
|
}
|
||||||
|
setAttribute(element, attribute:string, value:string) {
|
||||||
|
if (attribute) {
|
||||||
|
element.attribs[attribute] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeAttribute(element, attribute:string) {
|
||||||
|
if (attribute) {
|
||||||
|
delete element.attribs[attribute];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templateAwareRoot(el) {
|
||||||
|
return this.isTemplateElement(el) ? this.content(el) : el;
|
||||||
|
}
|
||||||
|
createHtmlDocument() {
|
||||||
|
throw _notImplemented('createHtmlDocument');
|
||||||
|
}
|
||||||
|
defaultDoc() {
|
||||||
|
if (defDoc === null) {
|
||||||
|
defDoc = StringMapWrapper.create();
|
||||||
|
defDoc.title = "Default title";
|
||||||
|
StringMapWrapper.set(defDoc, "head", treeAdapter.createElement("head", null, []));
|
||||||
|
}
|
||||||
|
return defDoc;
|
||||||
|
}
|
||||||
|
getTitle() {
|
||||||
|
return this.defaultDoc().title || "";
|
||||||
|
}
|
||||||
|
setTitle(newTitle:string) {
|
||||||
|
this.defaultDoc().title = newTitle;
|
||||||
|
}
|
||||||
|
isTemplateElement(el:any):boolean {
|
||||||
|
return this.isElementNode(el) && this.tagName(el) === "template";
|
||||||
|
}
|
||||||
|
isTextNode(node):boolean {
|
||||||
|
return treeAdapter.isTextNode(node);
|
||||||
|
}
|
||||||
|
isCommentNode(node):boolean {
|
||||||
|
throw treeAdapter.isCommentNode(node);
|
||||||
|
}
|
||||||
|
isElementNode(node):boolean {
|
||||||
|
return node ? treeAdapter.isElementNode(node) : false;
|
||||||
|
}
|
||||||
|
hasShadowRoot(node):boolean {
|
||||||
|
return isPresent(node.shadowRoot);
|
||||||
|
}
|
||||||
|
importIntoDoc(node) {
|
||||||
|
return this.clone(node);
|
||||||
|
}
|
||||||
|
isPageRule(rule): boolean {
|
||||||
|
return rule.type === 6; //CSSRule.PAGE_RULE
|
||||||
|
}
|
||||||
|
isStyleRule(rule): boolean {
|
||||||
|
return rule.type === 1; //CSSRule.MEDIA_RULE
|
||||||
|
}
|
||||||
|
isMediaRule(rule): boolean {
|
||||||
|
return rule.type === 4; //CSSRule.MEDIA_RULE
|
||||||
|
}
|
||||||
|
isKeyframesRule(rule): boolean {
|
||||||
|
return rule.type === 7; //CSSRule.KEYFRAMES_RULE
|
||||||
|
}
|
||||||
|
getHref(el): string {
|
||||||
|
return el.href;
|
||||||
|
}
|
||||||
|
resolveAndSetHref(el, baseUrl:string, href:string) {
|
||||||
|
if (href == null) {
|
||||||
|
el.href = baseUrl;
|
||||||
|
} else {
|
||||||
|
el.href = url.resolve(baseUrl, href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_buildRules(parsedRules, css) {
|
||||||
|
var rules = ListWrapper.create();
|
||||||
|
for (var i = 0; i < parsedRules.length; i++) {
|
||||||
|
var parsedRule = parsedRules[i];
|
||||||
|
var rule = {cssText: css};
|
||||||
|
rule.style = {content: "", cssText: ""};
|
||||||
|
if (parsedRule.type == "rule") {
|
||||||
|
rule.type = 1;
|
||||||
|
rule.selectorText = parsedRule.selectors.join(", ").replace(/\s{2,}/g, " ").replace(/\s*~\s*/g, " ~ ")
|
||||||
|
.replace(/\s*\+\s*/g, " + ").replace(/\s*>\s*/g, " > ").replace(/\[(\w+)=(\w+)\]/g, '[$1="$2"]');
|
||||||
|
if (isBlank(parsedRule.declarations)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (var j = 0; j < parsedRule.declarations.length; j++) {
|
||||||
|
var declaration = parsedRule.declarations[j];
|
||||||
|
rule.style[declaration.property] = declaration.value;
|
||||||
|
rule.style.cssText += declaration.property + ": " + declaration.value + ";";
|
||||||
|
}
|
||||||
|
} else if (parsedRule.type == "media") {
|
||||||
|
rule.type = 4;
|
||||||
|
rule.media = {mediaText: parsedRule.media};
|
||||||
|
if (parsedRule.rules) {
|
||||||
|
rule.cssRules = this._buildRules(parsedRule.rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListWrapper.push(rules, rule);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
cssToRules(css:string): List {
|
||||||
|
css = css.replace(/url\(\'(.+)\'\)/g, 'url($1)');
|
||||||
|
var rules = ListWrapper.create();
|
||||||
|
var parsedCSS = cssParse(css, {silent: true});
|
||||||
|
if (parsedCSS.stylesheet && parsedCSS.stylesheet.rules) {
|
||||||
|
rules = this._buildRules(parsedCSS.stylesheet.rules, css);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: build a proper list, this one is all the keys of a HTMLInputElement
|
||||||
|
var _HTMLElementPropertyList = ["webkitEntries","incremental","webkitdirectory","selectionDirection","selectionEnd",
|
||||||
|
"selectionStart","labels","validationMessage","validity","willValidate","width","valueAsNumber","valueAsDate",
|
||||||
|
"value","useMap","defaultValue","type","step","src","size","required","readOnly","placeholder","pattern","name",
|
||||||
|
"multiple","min","minLength","maxLength","max","list","indeterminate","height","formTarget","formNoValidate",
|
||||||
|
"formMethod","formEnctype","formAction","files","form","disabled","dirName","checked","defaultChecked","autofocus",
|
||||||
|
"autocomplete","alt","align","accept","onautocompleteerror","onautocomplete","onwaiting","onvolumechange",
|
||||||
|
"ontoggle","ontimeupdate","onsuspend","onsubmit","onstalled","onshow","onselect","onseeking","onseeked","onscroll",
|
||||||
|
"onresize","onreset","onratechange","onprogress","onplaying","onplay","onpause","onmousewheel","onmouseup",
|
||||||
|
"onmouseover","onmouseout","onmousemove","onmouseleave","onmouseenter","onmousedown","onloadstart",
|
||||||
|
"onloadedmetadata","onloadeddata","onload","onkeyup","onkeypress","onkeydown","oninvalid","oninput","onfocus",
|
||||||
|
"onerror","onended","onemptied","ondurationchange","ondrop","ondragstart","ondragover","ondragleave","ondragenter",
|
||||||
|
"ondragend","ondrag","ondblclick","oncuechange","oncontextmenu","onclose","onclick","onchange","oncanplaythrough",
|
||||||
|
"oncanplay","oncancel","onblur","onabort","spellcheck","isContentEditable","contentEditable","outerText",
|
||||||
|
"innerText","accessKey","hidden","webkitdropzone","draggable","tabIndex","dir","translate","lang","title",
|
||||||
|
"childElementCount","lastElementChild","firstElementChild","children","onwebkitfullscreenerror",
|
||||||
|
"onwebkitfullscreenchange","nextElementSibling","previousElementSibling","onwheel","onselectstart",
|
||||||
|
"onsearch","onpaste","oncut","oncopy","onbeforepaste","onbeforecut","onbeforecopy","shadowRoot","dataset",
|
||||||
|
"classList","className","outerHTML","innerHTML","scrollHeight","scrollWidth","scrollTop","scrollLeft",
|
||||||
|
"clientHeight","clientWidth","clientTop","clientLeft","offsetParent","offsetHeight","offsetWidth","offsetTop",
|
||||||
|
"offsetLeft","localName","prefix","namespaceURI","id","style","attributes","tagName","parentElement","textContent",
|
||||||
|
"baseURI","ownerDocument","nextSibling","previousSibling","lastChild","firstChild","childNodes","parentNode",
|
||||||
|
"nodeType","nodeValue","nodeName","closure_lm_714617","__jsaction"];
|
@ -10,11 +10,6 @@ class Math {
|
|||||||
static double random() => _random.nextDouble();
|
static double random() => _random.nextDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
class FIELD {
|
|
||||||
final String definition;
|
|
||||||
const FIELD(this.definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CONST {
|
class CONST {
|
||||||
const CONST();
|
const CONST();
|
||||||
}
|
}
|
||||||
@ -210,7 +205,13 @@ class DateWrapper {
|
|||||||
static DateTime fromMillis(int ms) {
|
static DateTime fromMillis(int ms) {
|
||||||
return new DateTime.fromMillisecondsSinceEpoch(ms);
|
return new DateTime.fromMillisecondsSinceEpoch(ms);
|
||||||
}
|
}
|
||||||
|
static int toMillis(DateTime date) {
|
||||||
|
return date.millisecondsSinceEpoch;
|
||||||
|
}
|
||||||
static DateTime now() {
|
static DateTime now() {
|
||||||
return new DateTime.now();
|
return new DateTime.now();
|
||||||
}
|
}
|
||||||
|
static toJson(DateTime date) {
|
||||||
|
return date.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,6 @@ if (assertionsEnabled_) {
|
|||||||
}
|
}
|
||||||
export {int};
|
export {int};
|
||||||
|
|
||||||
export class FIELD {
|
|
||||||
constructor(definition) {
|
|
||||||
this.definition = definition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CONST {}
|
export class CONST {}
|
||||||
export class ABSTRACT {}
|
export class ABSTRACT {}
|
||||||
export class IMPLEMENTS {}
|
export class IMPLEMENTS {}
|
||||||
@ -214,6 +208,10 @@ export class RegExpWrapper {
|
|||||||
return input.match(regExp.single);
|
return input.match(regExp.single);
|
||||||
}
|
}
|
||||||
static matcher(regExp, input) {
|
static matcher(regExp, input) {
|
||||||
|
// Reset regex state for the case
|
||||||
|
// someone did not loop over all matches
|
||||||
|
// last time.
|
||||||
|
regExp.multiple.lastIndex = 0;
|
||||||
return {
|
return {
|
||||||
re: regExp.multiple,
|
re: regExp.multiple,
|
||||||
input: input
|
input: input
|
||||||
@ -275,7 +273,13 @@ export class DateWrapper {
|
|||||||
static fromMillis(ms) {
|
static fromMillis(ms) {
|
||||||
return new Date(ms);
|
return new Date(ms);
|
||||||
}
|
}
|
||||||
|
static toMillis(date:Date) {
|
||||||
|
return date.getTime();
|
||||||
|
}
|
||||||
static now() {
|
static now() {
|
||||||
return new Date();
|
return new Date();
|
||||||
}
|
}
|
||||||
|
static toJson(date) {
|
||||||
|
return date.toJSON();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
modules/angular2/src/forms/form_builder.js
vendored
Normal file
48
modules/angular2/src/forms/form_builder.js
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import * as modelModule from './model';
|
||||||
|
|
||||||
|
|
||||||
|
export class FormBuilder {
|
||||||
|
group(controlsConfig, extra = null):modelModule.ControlGroup {
|
||||||
|
var controls = this._reduceControls(controlsConfig);
|
||||||
|
var optionals = isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null;
|
||||||
|
var validator = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
|
||||||
|
|
||||||
|
if (isPresent(validator)) {
|
||||||
|
return new modelModule.ControlGroup(controls, optionals, validator);
|
||||||
|
} else {
|
||||||
|
return new modelModule.ControlGroup(controls, optionals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
control(value, validator:Function = null):modelModule.Control {
|
||||||
|
if (isPresent(validator)) {
|
||||||
|
return new modelModule.Control(value, validator);
|
||||||
|
} else {
|
||||||
|
return new modelModule.Control(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_reduceControls(controlsConfig) {
|
||||||
|
var controls = {};
|
||||||
|
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
|
||||||
|
controls[controlName] = this._createControl(controlConfig);
|
||||||
|
});
|
||||||
|
return controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createControl(controlConfig) {
|
||||||
|
if (controlConfig instanceof modelModule.Control || controlConfig instanceof modelModule.ControlGroup) {
|
||||||
|
return controlConfig;
|
||||||
|
|
||||||
|
} else if (ListWrapper.isList(controlConfig)) {
|
||||||
|
var value = ListWrapper.get(controlConfig, 0);
|
||||||
|
var validator = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||||
|
return this.control(value, validator);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return this.control(controlConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
modules/angular2/src/forms/model.js
vendored
79
modules/angular2/src/forms/model.js
vendored
@ -11,7 +11,6 @@ export const INVALID = "INVALID";
|
|||||||
// get status():string;
|
// get status():string;
|
||||||
// get valid():boolean;
|
// get valid():boolean;
|
||||||
// get errors():Map;
|
// get errors():Map;
|
||||||
// get active():boolean {}
|
|
||||||
// updateValue(value:any){}
|
// updateValue(value:any){}
|
||||||
// setParent(parent){}
|
// setParent(parent){}
|
||||||
//}
|
//}
|
||||||
@ -29,10 +28,6 @@ export class AbstractControl {
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
get active():boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
this._updateIfNeeded();
|
this._updateIfNeeded();
|
||||||
return this._value;
|
return this._value;
|
||||||
@ -90,13 +85,30 @@ export class Control extends AbstractControl {
|
|||||||
|
|
||||||
export class ControlGroup extends AbstractControl {
|
export class ControlGroup extends AbstractControl {
|
||||||
controls;
|
controls;
|
||||||
|
optionals;
|
||||||
|
|
||||||
constructor(controls, validator:Function = controlGroupValidator) {
|
constructor(controls, optionals = null, validator:Function = controlGroupValidator) {
|
||||||
super(validator);
|
super(validator);
|
||||||
this.controls = controls;
|
this.controls = controls;
|
||||||
|
this.optionals = isPresent(optionals) ? optionals : {};
|
||||||
this._setParentForControls();
|
this._setParentForControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include(controlName:string) {
|
||||||
|
this._dirty = true;
|
||||||
|
StringMapWrapper.set(this.optionals, controlName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
exclude(controlName:string) {
|
||||||
|
this._dirty = true;
|
||||||
|
StringMapWrapper.set(this.optionals, controlName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(controlName:string) {
|
||||||
|
var c = StringMapWrapper.contains(this.controls, controlName);
|
||||||
|
return c && this._included(controlName);
|
||||||
|
}
|
||||||
|
|
||||||
_setParentForControls() {
|
_setParentForControls() {
|
||||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||||
control.setParent(this);
|
control.setParent(this);
|
||||||
@ -115,7 +127,7 @@ export class ControlGroup extends AbstractControl {
|
|||||||
_reduceValue() {
|
_reduceValue() {
|
||||||
var newValue = {};
|
var newValue = {};
|
||||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||||
if (control.active) {
|
if (this._included(name)) {
|
||||||
newValue[name] = control.value;
|
newValue[name] = control.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -126,56 +138,9 @@ export class ControlGroup extends AbstractControl {
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
this._updateParent();
|
this._updateParent();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export class OptionalControl {
|
_included(controlName:string):boolean {
|
||||||
_control:Control;
|
var isOptional = StringMapWrapper.contains(this.optionals, controlName);
|
||||||
_cond:boolean;
|
return !isOptional || StringMapWrapper.get(this.optionals, controlName);
|
||||||
|
|
||||||
constructor(control:Control, cond:boolean) {
|
|
||||||
super();
|
|
||||||
this._control = control;
|
|
||||||
this._cond = cond;
|
|
||||||
}
|
|
||||||
|
|
||||||
get active():boolean {
|
|
||||||
return this._cond;
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this._control.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get status() {
|
|
||||||
return this._control.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
get errors() {
|
|
||||||
return this._control.errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
set validator(v) {
|
|
||||||
this._control.validator = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
get validator() {
|
|
||||||
return this._control.validator;
|
|
||||||
}
|
|
||||||
|
|
||||||
set cond(value:boolean){
|
|
||||||
this._cond = value;
|
|
||||||
this._control._updateParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
get cond():boolean{
|
|
||||||
return this._cond;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateValue(value:any){
|
|
||||||
this._control.updateValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
setParent(parent){
|
|
||||||
this._control.setParent(parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
12
modules/angular2/src/forms/validators.js
vendored
12
modules/angular2/src/forms/validators.js
vendored
@ -1,18 +1,18 @@
|
|||||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {ControlGroup, Control} from 'angular2/forms';
|
import * as modelModule from './model';
|
||||||
|
|
||||||
export function required(c:Control) {
|
export function required(c:modelModule.Control) {
|
||||||
return isBlank(c.value) || c.value == "" ? {"required" : true} : null;
|
return isBlank(c.value) || c.value == "" ? {"required" : true} : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nullValidator(c:Control) {
|
export function nullValidator(c:modelModule.Control) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compose(validators:List<Function>):Function {
|
export function compose(validators:List<Function>):Function {
|
||||||
return function(c:Control) {
|
return function(c:modelModule.Control) {
|
||||||
var res = ListWrapper.reduce(validators, (res, validator) => {
|
var res = ListWrapper.reduce(validators, (res, validator) => {
|
||||||
var errors = validator(c);
|
var errors = validator(c);
|
||||||
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
|
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
|
||||||
@ -21,10 +21,10 @@ export function compose(validators:List<Function>):Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function controlGroupValidator(c:ControlGroup) {
|
export function controlGroupValidator(c:modelModule.ControlGroup) {
|
||||||
var res = {};
|
var res = {};
|
||||||
StringMapWrapper.forEach(c.controls, (control, name) => {
|
StringMapWrapper.forEach(c.controls, (control, name) => {
|
||||||
if (control.active && isPresent(control.errors)) {
|
if (c.contains(name) && isPresent(control.errors)) {
|
||||||
StringMapWrapper.forEach(control.errors, (value, error) => {
|
StringMapWrapper.forEach(control.errors, (value, error) => {
|
||||||
if (! StringMapWrapper.contains(res, error)) {
|
if (! StringMapWrapper.contains(res, error)) {
|
||||||
res[error] = [];
|
res[error] = [];
|
||||||
|
12
modules/angular2/src/services/title.js
vendored
Normal file
12
modules/angular2/src/services/title.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
export class Title {
|
||||||
|
|
||||||
|
getTitle():string {
|
||||||
|
return DOM.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTitle(newTitle:string) {
|
||||||
|
DOM.setTitle(newTitle);
|
||||||
|
}
|
||||||
|
}
|
116
modules/angular2/src/test_lib/test_injector.js
vendored
Normal file
116
modules/angular2/src/test_lib/test_injector.js
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import {bind} from 'angular2/di';
|
||||||
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
|
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection} from 'angular2/change_detection';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
|
||||||
|
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||||
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
|
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||||
|
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
||||||
|
import {XHRMock} from 'angular2/src/mock/xhr_mock';
|
||||||
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
|
||||||
|
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
|
||||||
|
import {StyleInliner} from 'angular2/src/core/compiler/style_inliner';
|
||||||
|
import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
|
||||||
|
|
||||||
|
import {Injector} from 'angular2/di';
|
||||||
|
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {FunctionWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root injector bindings.
|
||||||
|
*
|
||||||
|
* This must be kept in sync with the _rootBindings in application.js
|
||||||
|
*
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
function _getRootBindings() {
|
||||||
|
return [
|
||||||
|
bind(Reflector).toValue(reflector),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application injector bindings.
|
||||||
|
*
|
||||||
|
* This must be kept in sync with _injectorBindings() in application.js
|
||||||
|
*
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
function _getAppBindings() {
|
||||||
|
return [
|
||||||
|
bind(ShadowDomStrategy).toClass(NativeShadowDomStrategy),
|
||||||
|
Compiler,
|
||||||
|
CompilerCache,
|
||||||
|
TemplateResolver,
|
||||||
|
bind(ChangeDetection).toValue(dynamicChangeDetection),
|
||||||
|
TemplateLoader,
|
||||||
|
DirectiveMetadataReader,
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ExceptionHandler,
|
||||||
|
bind(XHR).toClass(XHRMock),
|
||||||
|
ComponentUrlMapper,
|
||||||
|
UrlResolver,
|
||||||
|
StyleUrlResolver,
|
||||||
|
StyleInliner,
|
||||||
|
bind(CssProcessor).toFactory(() => new CssProcessor(null), []),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTestInjector(bindings: List) {
|
||||||
|
var rootInjector = new Injector(_getRootBindings());
|
||||||
|
return rootInjector.createChild(ListWrapper.concat(_getAppBindings(), bindings));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows injecting dependencies in `beforeEach()` and `it()`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* beforeEach(inject([Dependency, AClass], (dep, object) => {
|
||||||
|
* // some code that uses `dep` and `object`
|
||||||
|
* // ...
|
||||||
|
* }));
|
||||||
|
*
|
||||||
|
* it('...', inject([AClass, AsyncTestCompleter], (object, async) => {
|
||||||
|
* object.doSomething().then(() => {
|
||||||
|
* expect(...);
|
||||||
|
* async.done();
|
||||||
|
* });
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - injecting an `AsyncTestCompleter` allow completing async tests - this is the equivalent of
|
||||||
|
* adding a `done` parameter in Jasmine,
|
||||||
|
* - inject is currently a function because of some Traceur limitation the syntax should eventually
|
||||||
|
* becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });`
|
||||||
|
*
|
||||||
|
* @param {Array} tokens
|
||||||
|
* @param {Function} fn
|
||||||
|
* @return {FunctionWithParamTokens}
|
||||||
|
*/
|
||||||
|
export function inject(tokens: List, fn: Function) {
|
||||||
|
return new FunctionWithParamTokens(tokens, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FunctionWithParamTokens {
|
||||||
|
_tokens: List;
|
||||||
|
_fn: Function;
|
||||||
|
|
||||||
|
constructor(tokens: List, fn: Function) {
|
||||||
|
this._tokens = tokens;
|
||||||
|
this._fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(injector: Injector) {
|
||||||
|
var params = ListWrapper.map(this._tokens, (t) => injector.get(t));
|
||||||
|
FunctionWrapper.apply(this._fn, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,74 @@
|
|||||||
library test_lib.test_lib;
|
library test_lib.test_lib;
|
||||||
|
|
||||||
import 'package:guinness/guinness.dart' as gns;
|
import 'package:guinness/guinness.dart' as gns;
|
||||||
export 'package:guinness/guinness.dart' hide Expect, expect, NotExpect, beforeEach, it, iit;
|
export 'package:guinness/guinness.dart' hide Expect, expect, NotExpect, beforeEach, it, iit, xit;
|
||||||
import 'package:unittest/unittest.dart' hide expect;
|
import 'package:unittest/unittest.dart' hide expect;
|
||||||
import 'dart:mirrors';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:angular2/src/reflection/reflection.dart';
|
|
||||||
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
|
||||||
import 'package:collection/equality.dart';
|
|
||||||
import 'package:angular2/src/dom/dom_adapter.dart' show DOM;
|
import 'package:angular2/src/dom/dom_adapter.dart' show DOM;
|
||||||
|
|
||||||
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
|
|
||||||
|
import 'package:angular2/src/di/binding.dart' show bind;
|
||||||
|
import 'package:angular2/src/di/injector.dart' show Injector;
|
||||||
|
|
||||||
|
import './test_injector.dart';
|
||||||
|
export './test_injector.dart' show inject;
|
||||||
|
|
||||||
bool IS_DARTIUM = true;
|
bool IS_DARTIUM = true;
|
||||||
|
bool IS_NODEJS = false;
|
||||||
|
|
||||||
|
List _testBindings = [];
|
||||||
|
Injector _injector;
|
||||||
|
bool _isCurrentTestAsync;
|
||||||
|
bool _inIt = false;
|
||||||
|
|
||||||
|
class AsyncTestCompleter {
|
||||||
|
Completer _completer;
|
||||||
|
|
||||||
|
AsyncTestCompleter() {
|
||||||
|
_completer = new Completer();
|
||||||
|
}
|
||||||
|
|
||||||
|
done() {
|
||||||
|
_completer.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
get future => _completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
testSetup() {
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
|
// beforeEach configuration:
|
||||||
|
// - Priority 3: clear the bindings before each test,
|
||||||
|
// - Priority 2: collect the bindings before each test, see beforeEachBindings(),
|
||||||
|
// - Priority 1: create the test injector to be used in beforeEach() and it()
|
||||||
|
|
||||||
|
gns.beforeEach(
|
||||||
|
() {
|
||||||
|
_testBindings.clear();
|
||||||
|
},
|
||||||
|
priority: 3
|
||||||
|
);
|
||||||
|
|
||||||
|
var completerBinding = bind(AsyncTestCompleter).toFactory(() {
|
||||||
|
// Mark the test as async when an AsyncTestCompleter is injected in an it(),
|
||||||
|
if (!_inIt) throw 'AsyncTestCompleter can only be injected in an "it()"';
|
||||||
|
_isCurrentTestAsync = true;
|
||||||
|
return new AsyncTestCompleter();
|
||||||
|
});
|
||||||
|
|
||||||
|
gns.beforeEach(
|
||||||
|
() {
|
||||||
|
_isCurrentTestAsync = false;
|
||||||
|
_testBindings.add(completerBinding);
|
||||||
|
_injector = createTestInjector(_testBindings);
|
||||||
|
},
|
||||||
|
priority: 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Expect expect(actual, [matcher]) {
|
Expect expect(actual, [matcher]) {
|
||||||
final expect = new Expect(actual);
|
final expect = new Expect(actual);
|
||||||
@ -23,10 +81,8 @@ class Expect extends gns.Expect {
|
|||||||
|
|
||||||
NotExpect get not => new NotExpect(actual);
|
NotExpect get not => new NotExpect(actual);
|
||||||
|
|
||||||
// TODO(tbosch) change back when https://github.com/vsavkin/guinness/issues/41 is fixed
|
void toEqual(expected) => toHaveSameProps(expected);
|
||||||
// void toEqual(expected) => toHaveSameProps(expected);
|
void toThrowError([message=""]) => toThrowWith(message: message);
|
||||||
void toEqual(expected) => _expect(actual, new FixedSamePropsMatcher(expected));
|
|
||||||
void toThrowError([message=""]) => this.toThrowWith(message: message);
|
|
||||||
void toBePromise() => _expect(actual is Future, equals(true));
|
void toBePromise() => _expect(actual is Future, equals(true));
|
||||||
void toImplement(expected) => toBeA(expected);
|
void toImplement(expected) => toBeA(expected);
|
||||||
void toBeNaN() => _expect(double.NAN.compareTo(actual) == 0, equals(true));
|
void toBeNaN() => _expect(double.NAN.compareTo(actual) == 0, equals(true));
|
||||||
@ -37,106 +93,61 @@ class Expect extends gns.Expect {
|
|||||||
class NotExpect extends gns.NotExpect {
|
class NotExpect extends gns.NotExpect {
|
||||||
NotExpect(actual) : super(actual);
|
NotExpect(actual) : super(actual);
|
||||||
|
|
||||||
// TODO(tbosch) change back when https://github.com/vsavkin/guinness/issues/41 is fixed
|
void toEqual(expected) => toHaveSameProps(expected);
|
||||||
// void toEqual(expected) => toHaveSameProps(expected);
|
|
||||||
void toEqual(expected) => _expect(actual, isNot(new FixedSamePropsMatcher(expected)));
|
|
||||||
void toBePromise() => _expect(actual is Future, equals(false));
|
void toBePromise() => _expect(actual is Future, equals(false));
|
||||||
Function get _expect => gns.guinness.matchers.expect;
|
Function get _expect => gns.guinness.matchers.expect;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(fn) {
|
beforeEach(fn) {
|
||||||
gns.beforeEach(_enableReflection(fn));
|
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn);
|
||||||
|
gns.beforeEach(() {
|
||||||
|
fn.execute(_injector);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows overriding default bindings defined in test_injector.js.
|
||||||
|
*
|
||||||
|
* The given function must return a list of DI bindings.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* beforeEachBindings(() => [
|
||||||
|
* bind(Compiler).toClass(MockCompiler),
|
||||||
|
* bind(SomeToken).toValue(myValue),
|
||||||
|
* ]);
|
||||||
|
*/
|
||||||
|
beforeEachBindings(fn) {
|
||||||
|
gns.beforeEach(
|
||||||
|
() {
|
||||||
|
var bindings = fn();
|
||||||
|
if (bindings != null) _testBindings.addAll(bindings);
|
||||||
|
},
|
||||||
|
priority: 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_it(gnsFn, name, fn) {
|
||||||
|
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn);
|
||||||
|
gnsFn(name, () {
|
||||||
|
_inIt = true;
|
||||||
|
fn.execute(_injector);
|
||||||
|
_inIt = false;
|
||||||
|
if (_isCurrentTestAsync) return _injector.get(AsyncTestCompleter).future;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
it(name, fn) {
|
it(name, fn) {
|
||||||
gns.it(name, _enableReflection(_handleAsync(fn)));
|
_it(gns.it, name, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
iit(name, fn) {
|
iit(name, fn) {
|
||||||
gns.iit(name, _enableReflection(_handleAsync(fn)));
|
_it(gns.iit, name, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
_enableReflection(fn) {
|
xit(name, fn) {
|
||||||
return () {
|
_it(gns.xit, name, fn);
|
||||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
|
||||||
return fn();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleAsync(fn) {
|
|
||||||
ClosureMirror cm = reflect(fn);
|
|
||||||
MethodMirror mm = cm.function;
|
|
||||||
|
|
||||||
var completer = new Completer();
|
|
||||||
|
|
||||||
if (mm.parameters.length == 1) {
|
|
||||||
return () {
|
|
||||||
cm.apply([completer.complete]);
|
|
||||||
return completer.future;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(tbosch): remove when https://github.com/vsavkin/guinness/issues/41
|
|
||||||
// is fixed
|
|
||||||
class FixedSamePropsMatcher extends Matcher {
|
|
||||||
final Object _expected;
|
|
||||||
|
|
||||||
const FixedSamePropsMatcher(this._expected);
|
|
||||||
|
|
||||||
bool matches(actual, Map matchState) {
|
|
||||||
return compare(toData(_expected), toData(actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
Description describeMismatch(item, Description mismatchDescription,
|
|
||||||
Map matchState, bool verbose) =>
|
|
||||||
mismatchDescription.add('is equal to ${toData(item)}. Expected: ${toData(_expected)}');
|
|
||||||
|
|
||||||
Description describe(Description description) =>
|
|
||||||
description.add('has different properties');
|
|
||||||
|
|
||||||
toData(obj) => new _FixedObjToData().call(obj);
|
|
||||||
compare(d1, d2) => new DeepCollectionEquality().equals(d1, d2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(tbosch): remove when https://github.com/vsavkin/guinness/issues/41
|
|
||||||
// is fixed
|
|
||||||
class _FixedObjToData {
|
|
||||||
final visitedObjects = new Set();
|
|
||||||
|
|
||||||
call(obj) {
|
|
||||||
if (visitedObjects.contains(obj)) return null;
|
|
||||||
visitedObjects.add(obj);
|
|
||||||
|
|
||||||
if (obj is num || obj is String || obj is bool) return obj;
|
|
||||||
if (obj is Iterable) return obj.map(call).toList();
|
|
||||||
if (obj is Map) return mapToData(obj);
|
|
||||||
return toDataUsingReflection(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapToData(obj) {
|
|
||||||
var res = {};
|
|
||||||
obj.forEach((k,v) {
|
|
||||||
res[call(k)] = call(v);
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
toDataUsingReflection(obj) {
|
|
||||||
final clazz = reflectClass(obj.runtimeType);
|
|
||||||
final instance = reflect(obj);
|
|
||||||
|
|
||||||
return clazz.declarations.values.fold({}, (map, decl) {
|
|
||||||
if (decl is VariableMirror && !decl.isPrivate && !decl.isStatic) {
|
|
||||||
final field = instance.getField(decl.simpleName);
|
|
||||||
final name = MirrorSystem.getName(decl.simpleName);
|
|
||||||
map[name] = call(field.reflectee);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String elementText(n) {
|
String elementText(n) {
|
||||||
|
@ -1,23 +1,166 @@
|
|||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
import {bind} from 'angular2/di';
|
||||||
|
|
||||||
|
import {createTestInjector, FunctionWithParamTokens, inject} from './test_injector';
|
||||||
|
|
||||||
|
export {inject} from './test_injector';
|
||||||
|
|
||||||
export {proxy} from 'rtts_assert/rtts_assert';
|
export {proxy} from 'rtts_assert/rtts_assert';
|
||||||
export var describe = window.describe;
|
|
||||||
export var xdescribe = window.xdescribe;
|
var _global = typeof window === 'undefined' ? global : window;
|
||||||
export var ddescribe = window.ddescribe;
|
|
||||||
export var it = window.it;
|
export var afterEach = _global.afterEach;
|
||||||
export var xit = window.xit;
|
export var expect = _global.expect;
|
||||||
export var iit = window.iit;
|
|
||||||
export var beforeEach = window.beforeEach;
|
|
||||||
export var afterEach = window.afterEach;
|
|
||||||
export var expect = window.expect;
|
|
||||||
export var IS_DARTIUM = false;
|
export var IS_DARTIUM = false;
|
||||||
|
export var IS_NODEJS = typeof window === 'undefined';
|
||||||
|
|
||||||
|
export class AsyncTestCompleter {
|
||||||
|
_done: Function;
|
||||||
|
|
||||||
|
constructor(done: Function) {
|
||||||
|
this._done = done;
|
||||||
|
}
|
||||||
|
|
||||||
|
done() {
|
||||||
|
this._done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsmBeforeEach = _global.beforeEach;
|
||||||
|
var jsmDescribe = _global.describe;
|
||||||
|
var jsmDDescribe = _global.ddescribe;
|
||||||
|
var jsmXDescribe = _global.xdescribe;
|
||||||
|
var jsmIt = _global.it;
|
||||||
|
var jsmIIt = _global.iit;
|
||||||
|
var jsmXIt = _global.xit;
|
||||||
|
|
||||||
|
var runnerStack = [];
|
||||||
|
var inIt = false;
|
||||||
|
|
||||||
|
var testBindings;
|
||||||
|
|
||||||
|
class BeforeEachRunner {
|
||||||
|
constructor(parent: BeforeEachRunner) {
|
||||||
|
this._fns = [];
|
||||||
|
this._parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(fn: FunctionWithParamTokens) {
|
||||||
|
this._fns.push(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(injector) {
|
||||||
|
if (this._parent) this._parent.run();
|
||||||
|
this._fns.forEach((fn) => fn.execute(injector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the test bindings before each test
|
||||||
|
jsmBeforeEach(() => { testBindings = []; });
|
||||||
|
|
||||||
|
function _describe(jsmFn, ...args) {
|
||||||
|
var parentRunner = runnerStack.length === 0 ? null : runnerStack[runnerStack.length - 1];
|
||||||
|
var runner = new BeforeEachRunner(parentRunner);
|
||||||
|
runnerStack.push(runner);
|
||||||
|
var suite = jsmFn(...args);
|
||||||
|
runnerStack.pop();
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function describe(...args) {
|
||||||
|
return _describe(jsmDescribe, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ddescribe(...args) {
|
||||||
|
return _describe(jsmDDescribe, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function xdescribe(...args) {
|
||||||
|
return _describe(jsmXDescribe, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function beforeEach(fn) {
|
||||||
|
if (runnerStack.length > 0) {
|
||||||
|
// Inside a describe block, beforeEach() uses a BeforeEachRunner
|
||||||
|
var runner = runnerStack[runnerStack.length - 1];
|
||||||
|
if (!(fn instanceof FunctionWithParamTokens)) {
|
||||||
|
fn = inject([], fn);
|
||||||
|
}
|
||||||
|
runner.beforeEach(fn);
|
||||||
|
} else {
|
||||||
|
// Top level beforeEach() are delegated to jasmine
|
||||||
|
jsmBeforeEach(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows overriding default bindings defined in test_injector.js.
|
||||||
|
*
|
||||||
|
* The given function must return a list of DI bindings.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* beforeEachBindings(() => [
|
||||||
|
* bind(Compiler).toClass(MockCompiler),
|
||||||
|
* bind(SomeToken).toValue(myValue),
|
||||||
|
* ]);
|
||||||
|
*/
|
||||||
|
export function beforeEachBindings(fn) {
|
||||||
|
jsmBeforeEach(() => {
|
||||||
|
var bindings = fn();
|
||||||
|
if (!bindings) return;
|
||||||
|
testBindings = [...testBindings, ...bindings];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _it(jsmFn, name, fn) {
|
||||||
|
var runner = runnerStack[runnerStack.length - 1];
|
||||||
|
|
||||||
|
jsmFn(name, function(done) {
|
||||||
|
var async = false;
|
||||||
|
|
||||||
|
var completerBinding = bind(AsyncTestCompleter).toFactory(() => {
|
||||||
|
// Mark the test as async when an AsyncTestCompleter is injected in an it()
|
||||||
|
if (!inIt) throw new Error('AsyncTestCompleter can only be injected in an "it()"');
|
||||||
|
async = true;
|
||||||
|
return new AsyncTestCompleter(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
var injector = createTestInjector([...testBindings, completerBinding]);
|
||||||
|
|
||||||
|
runner.run(injector);
|
||||||
|
|
||||||
|
if (!(fn instanceof FunctionWithParamTokens)) {
|
||||||
|
fn = inject([], fn);
|
||||||
|
}
|
||||||
|
inIt = true;
|
||||||
|
fn.execute(injector);
|
||||||
|
inIt = false;
|
||||||
|
|
||||||
|
if (!async) done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function it(name, fn) {
|
||||||
|
return _it(jsmIt, name, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function xit(name, fn) {
|
||||||
|
return _it(jsmXIt, name, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function iit(name, fn) {
|
||||||
|
return _it(jsmIIt, name, fn);
|
||||||
|
}
|
||||||
|
|
||||||
// To make testing consistent between dart and js
|
// To make testing consistent between dart and js
|
||||||
window.print = function(msg) {
|
_global.print = function(msg) {
|
||||||
if (window.dump) {
|
if (_global.dump) {
|
||||||
window.dump(msg);
|
_global.dump(msg);
|
||||||
} else {
|
} else {
|
||||||
window.console.log(msg);
|
_global.console.log(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,7 +168,7 @@ window.print = function(msg) {
|
|||||||
// gives us bad error messages in tests.
|
// gives us bad error messages in tests.
|
||||||
// The only way to do this in Jasmine is to monkey patch a method
|
// The only way to do this in Jasmine is to monkey patch a method
|
||||||
// to the object :-(
|
// to the object :-(
|
||||||
window.Map.prototype.jasmineToString = function() {
|
_global.Map.prototype.jasmineToString = function() {
|
||||||
var m = this;
|
var m = this;
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return ''+m;
|
return ''+m;
|
||||||
@ -37,7 +180,7 @@ window.Map.prototype.jasmineToString = function() {
|
|||||||
return `{ ${res.join(',')} }`;
|
return `{ ${res.join(',')} }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.beforeEach(function() {
|
_global.beforeEach(function() {
|
||||||
jasmine.addMatchers({
|
jasmine.addMatchers({
|
||||||
// Custom handler for Map as Jasmine does not support it yet
|
// Custom handler for Map as Jasmine does not support it yet
|
||||||
toEqual: function(util, customEqualityTesters) {
|
toEqual: function(util, customEqualityTesters) {
|
||||||
@ -148,17 +291,25 @@ export class SpyObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function elementText(n) {
|
function elementText(n) {
|
||||||
var hasNodes = (n) => {var children = DOM.childNodes(n); return children && children.length > 0;}
|
var hasNodes = (n) => {var children = DOM.childNodes(n); return children && children.length > 0;}
|
||||||
|
if (!IS_NODEJS) {
|
||||||
|
if (n instanceof Comment) return '';
|
||||||
|
|
||||||
if (n instanceof Comment) return '';
|
if (n instanceof Array) return n.map((nn) => elementText(nn)).join("");
|
||||||
|
if (n instanceof Element && DOM.tagName(n) == 'CONTENT')
|
||||||
|
return elementText(Array.prototype.slice.apply(n.getDistributedNodes()));
|
||||||
|
if (DOM.hasShadowRoot(n)) return elementText(DOM.childNodesAsList(n.shadowRoot));
|
||||||
|
if (hasNodes(n)) return elementText(DOM.childNodesAsList(n));
|
||||||
|
|
||||||
if (n instanceof Array) return n.map((nn) => elementText(nn)).join("");
|
return n.textContent;
|
||||||
if (n instanceof Element && DOM.tagName(n) == 'CONTENT')
|
} else {
|
||||||
return elementText(Array.prototype.slice.apply(n.getDistributedNodes()));
|
if (n instanceof Array) {
|
||||||
if (DOM.hasShadowRoot(n)) return elementText(DOM.childNodesAsList(n.shadowRoot));
|
return n.map((nn) => elementText(nn)).join("");
|
||||||
if (hasNodes(n)) return elementText(DOM.childNodesAsList(n));
|
} else if (hasNodes(n)) {
|
||||||
|
return elementText(DOM.childNodesAsList(n));
|
||||||
return n.textContent;
|
} else {
|
||||||
|
return DOM.getText(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
53
modules/angular2/src/transform/bind_generator/generator.dart
Normal file
53
modules/angular2/src/transform/bind_generator/generator.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
library angular2.src.transform.bind_generator.generator;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
|
import 'package:angular2/src/transform/common/parser.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'visitor.dart';
|
||||||
|
|
||||||
|
Future<String> createNgSetters(AssetReader reader, AssetId entryPoint) async {
|
||||||
|
var parser = new Parser(reader);
|
||||||
|
NgDeps ngDeps = await parser.parse(entryPoint);
|
||||||
|
|
||||||
|
String code = ngDeps.code;
|
||||||
|
var setters = _generateSetters(_createBindMap(ngDeps));
|
||||||
|
|
||||||
|
if (setters.length == 0) return code;
|
||||||
|
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
||||||
|
return '${code.substring(0, codeInjectIdx)}'
|
||||||
|
'..registerSetters({${setters.join(', ')}})'
|
||||||
|
'${code.substring(codeInjectIdx)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the map generated by [_createBindMap] to codegen setters.
|
||||||
|
List<String> _generateSetters(Map<String, String> bindMap) {
|
||||||
|
var setters = [];
|
||||||
|
// TODO(kegluneq): Include types for receivers. See #886.
|
||||||
|
bindMap.forEach((prop, type) {
|
||||||
|
setters.add(''''$prop': (o, String v) => o.$prop = v''');
|
||||||
|
});
|
||||||
|
return setters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collapses all `bindProperties` in [ngDeps] into a map where the keys are
|
||||||
|
/// the bind properties and the values are either the one and only type
|
||||||
|
/// binding to that property or the empty string.
|
||||||
|
Map<String, String> _createBindMap(NgDeps ngDeps) {
|
||||||
|
var visitor = new ExtractSettersVisitor();
|
||||||
|
var bindMap = {};
|
||||||
|
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
||||||
|
visitor.bindMappings.clear();
|
||||||
|
t.annotations.accept(visitor);
|
||||||
|
visitor.bindMappings.forEach((String prop, _) {
|
||||||
|
if (bindMap.containsKey(prop)) {
|
||||||
|
bindMap[prop] = '';
|
||||||
|
} else {
|
||||||
|
bindMap[prop] = '${t.typeName}';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return bindMap;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
library angular2.src.transform.bind_generator.transformer;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
|
import 'package:angular2/src/transform/common/formatter.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'generator.dart';
|
||||||
|
|
||||||
|
/// Transformer responsible for reading .ng_deps.dart files and generating
|
||||||
|
/// setters from the "annotations" information in the generated
|
||||||
|
/// `registerType` calls.
|
||||||
|
///
|
||||||
|
/// These setters are registered in the same `setupReflection` function with
|
||||||
|
/// the `registerType` calls.
|
||||||
|
class BindGenerator extends Transformer {
|
||||||
|
final TransformerOptions options;
|
||||||
|
|
||||||
|
BindGenerator(this.options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future apply(Transform transform) async {
|
||||||
|
log.init(transform);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var id = transform.primaryInput.id;
|
||||||
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
|
var transformedCode = await createNgSetters(reader, id);
|
||||||
|
transform.addOutput(new Asset.fromString(
|
||||||
|
id, formatter.format(transformedCode, uri: id.path)));
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
log.logger.error('Creating ng setters failed.\n'
|
||||||
|
'Exception: $ex\n'
|
||||||
|
'Stack Trace: $stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
modules/angular2/src/transform/bind_generator/visitor.dart
Normal file
39
modules/angular2/src/transform/bind_generator/visitor.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
library angular2.src.transform.bind_generator.visitor;
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
|
||||||
|
/// Visitor responsible for crawling the "annotations" value in a
|
||||||
|
/// `registerType` call and pulling out the properties of any "bind"
|
||||||
|
/// values found.
|
||||||
|
class ExtractSettersVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||||
|
final Map<String, String> bindMappings = {};
|
||||||
|
|
||||||
|
void _extractFromMapLiteral(MapLiteral map) {
|
||||||
|
map.entries.forEach((entry) {
|
||||||
|
// TODO(kegluneq): Remove this restriction
|
||||||
|
if (entry.key is SimpleStringLiteral &&
|
||||||
|
entry.value is SimpleStringLiteral) {
|
||||||
|
bindMappings[stringLiteralToString(entry.key)] =
|
||||||
|
stringLiteralToString(entry.value);
|
||||||
|
} else {
|
||||||
|
logger.error('`bind` currently only supports string literals '
|
||||||
|
'(${entry})');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitNamedExpression(NamedExpression node) {
|
||||||
|
if ('${node.name.label}' == 'bind') {
|
||||||
|
// TODO(kegluneq): Remove this restriction.
|
||||||
|
if (node.expression is MapLiteral) {
|
||||||
|
_extractFromMapLiteral(node.expression);
|
||||||
|
} else {
|
||||||
|
logger.error('`bind` currently only supports map literals');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.visitNamedExpression(node);
|
||||||
|
}
|
||||||
|
}
|
28
modules/angular2/src/transform/common/asset_reader.dart
Normal file
28
modules/angular2/src/transform/common/asset_reader.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
library angular2.src.transform.common.asset_reader;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
/// A class that allows fetching code using [AssetId]s without all the
|
||||||
|
/// additional baggage of a [Transform].
|
||||||
|
abstract class AssetReader {
|
||||||
|
Future<String> readAsString(AssetId id, {Encoding encoding});
|
||||||
|
Future<bool> hasInput(AssetId id);
|
||||||
|
|
||||||
|
/// Creates an [AssetReader] using the `transform`, which should be a
|
||||||
|
/// [Transform] or [AggregateTransform].
|
||||||
|
factory AssetReader.fromTransform(dynamic transform) =>
|
||||||
|
new _TransformAssetReader(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TransformAssetReader implements AssetReader {
|
||||||
|
final dynamic t;
|
||||||
|
_TransformAssetReader(this.t);
|
||||||
|
|
||||||
|
Future<String> readAsString(AssetId id, {Encoding encoding}) =>
|
||||||
|
t.readInputAsString(id, encoding: encoding);
|
||||||
|
|
||||||
|
Future<bool> hasInput(AssetId id) => t.hasInput(id);
|
||||||
|
}
|
22
modules/angular2/src/transform/common/formatter.dart
Normal file
22
modules/angular2/src/transform/common/formatter.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
library angular2.src.transform.common.formatter;
|
||||||
|
|
||||||
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
|
import 'logging.dart';
|
||||||
|
|
||||||
|
DartFormatter _formatter = null;
|
||||||
|
|
||||||
|
void init(DartFormatter formatter) {
|
||||||
|
if (_formatter != null) {
|
||||||
|
logger.warning('Formatter is being overwritten.');
|
||||||
|
}
|
||||||
|
_formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
DartFormatter get formatter {
|
||||||
|
if (_formatter == null) {
|
||||||
|
logger.info('Formatter never initialized, using default formatter.');
|
||||||
|
_formatter = new DartFormatter();
|
||||||
|
}
|
||||||
|
return _formatter;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user