Compare commits
219 Commits
starting
...
2.0.0-alph
Author | SHA1 | Date | |
---|---|---|---|
7d29636087 | |||
70433e6b73 | |||
3667854a8f | |||
c5c1c9e38e | |||
308823b6ea | |||
c05bad381c | |||
4a961f4ecb | |||
6c8398df9b | |||
ff6e7754ae | |||
28ba179e31 | |||
b96e560c8d | |||
7c95cea3a8 | |||
34501aaae6 | |||
dbfc4c1c16 | |||
301863b105 | |||
ef8dc40492 | |||
6dbd4d969b | |||
3dd0ac1f0a | |||
5b42272365 | |||
1f6c6dbf2f | |||
0012caa4d5 | |||
8499cf84c3 | |||
daf0f472b3 | |||
a3decad4c2 | |||
7b790a3369 | |||
e18920884e | |||
6f8fef4f13 | |||
e295940833 | |||
2ed7622239 | |||
6ce085a21a | |||
e34146fc14 | |||
4e2316c742 | |||
785900f722 | |||
5ce5a87abe | |||
afe5465862 | |||
678d541da7 | |||
f0477e164a | |||
b5002fb46b | |||
82127571b5 | |||
f6e9d1f857 | |||
2cab7c79c3 | |||
bba849909c | |||
cac74c73e1 | |||
f375dbd013 | |||
ea58ef85fc | |||
bf7933714a | |||
564477b8a0 | |||
7e2c04e805 | |||
8fa1539bac | |||
f45281a10a | |||
e9f70293ac | |||
61cb99ea42 | |||
5408a9a72d | |||
22c1a0d030 | |||
8c3007e4b5 | |||
838aa2aaa9 | |||
3285ffba16 | |||
17e8857efc | |||
226cbc7db3 | |||
cc7c7b3321 | |||
70cea03b4b | |||
a027912891 | |||
3bdf669ddf | |||
b94b04c074 | |||
cfc5dd830c | |||
a3097aaf05 | |||
69c3bff086 | |||
50098767fc | |||
de581ea8b3 | |||
9f8a9c6fc7 | |||
ad083ed28f | |||
105ba30ce9 | |||
ee8bf0b3c0 | |||
41262f4265 | |||
c349eb4fa4 | |||
ca958464c4 | |||
d6003ee0ab | |||
bc248e9a15 | |||
42c0171b40 | |||
1a99090b45 | |||
b7eea4f577 | |||
9c62b5867e | |||
2560af731a | |||
86211eb5f0 | |||
a3387b7f48 | |||
94a48e8640 | |||
d8aeb40b49 | |||
52c55d0ee8 | |||
438c2b31e4 | |||
57e308dd46 | |||
c922b5a112 | |||
d552303cd5 | |||
1d4d18d9db | |||
069bbf3ed0 | |||
a4a2d4e56d | |||
d77f409093 | |||
25c709c58e | |||
bc909d1d0f | |||
a6736ff9f2 | |||
894a0f0ee5 | |||
abea92af59 | |||
bcbed2812d | |||
c0b04ca0bc | |||
86dc3e5b07 | |||
c1aa65239e | |||
be5ccf6957 | |||
09067ebdc5 | |||
08697e71fa | |||
90d9a1df3f | |||
a96c149793 | |||
1037cef22e | |||
09948f4403 | |||
788461b7e2 | |||
4f56628566 | |||
bcbf1ccc68 | |||
ae30d7ba40 | |||
9adf41ca2d | |||
1d79d534d9 | |||
60e4197026 | |||
2fabca77b9 | |||
47542b0cb0 | |||
6c60c3e547 | |||
814d389b6e | |||
e81e5fb2b9 | |||
f68cdf3878 | |||
91e0e9e1dd | |||
59c1299168 | |||
27c6afbeb4 | |||
514ba54282 | |||
a11f683e7b | |||
b65b145122 | |||
982bb8b01d | |||
eb7b7581ca | |||
adab6c0728 | |||
609201e109 | |||
54a4e4a67c | |||
aca4604879 | |||
48811cd805 | |||
136f64f4ac | |||
123ee8e06f | |||
a55efbd8b8 | |||
7bf9525353 | |||
3915e1b242 | |||
ed5975d3e5 | |||
1a788e6b0d | |||
d822793229 | |||
b46d0bc48c | |||
65320126c2 | |||
c63b3164bd | |||
dbffa88dc2 | |||
8c5d9d372f | |||
50f8892c6b | |||
3bfbfa8ae0 | |||
8598c87ef4 | |||
33bfc4c24a | |||
3afb744e77 | |||
e92918bbfe | |||
723e8fde93 | |||
507f7ea70a | |||
6b985d56a5 | |||
c8385ad998 | |||
9d21a6f40d | |||
d304f41197 | |||
8d85b839b6 | |||
dd235f38a3 | |||
5306b6dd0c | |||
b09624024b | |||
edc3709451 | |||
e706f3477b | |||
6298cb3999 | |||
878fce6482 | |||
b02bd65871 | |||
ee36aaf163 | |||
ff84506bd5 | |||
0ae33b7e3c | |||
b1dc6239ef | |||
3ce0f1146f | |||
3ec837bfdb | |||
18ff2be9bb | |||
c0d296334c | |||
9a0a2e319c | |||
a0d86ac2bb | |||
99045b2f6a | |||
c34ca36778 | |||
58dd75a1c8 | |||
f995b07876 | |||
101a4aa3cf | |||
65d759316b | |||
19c1773133 | |||
9b3b3d325f | |||
43f4374944 | |||
81e6d13241 | |||
f8e7a37c0d | |||
c686e7ea30 | |||
7e89af8190 | |||
539e8e2cce | |||
aab084866c | |||
0e61a86763 | |||
1c9938ed98 | |||
47c1a0f381 | |||
514529b5d9 | |||
a12dc7d75a | |||
41b53e71e1 | |||
0fb9f3bd6c | |||
81f3f32217 | |||
b35f288794 | |||
4e82cc0861 | |||
c735644c57 | |||
5d479fa0ae | |||
8baedca972 | |||
02aa8e7945 | |||
ee523efcb4 | |||
eef5f7e06d | |||
83402930f2 | |||
bd48c927d0 | |||
b61b8d60b7 | |||
f1fca5abb6 | |||
045ce3c77a | |||
f822066e2a |
3
.clang-format
Normal file
3
.clang-format
Normal file
@ -0,0 +1,3 @@
|
||||
Language: JavaScript
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -7,6 +7,9 @@ bower_components
|
||||
.pub
|
||||
.DS_STORE
|
||||
|
||||
# Or broccoli working directory
|
||||
tmp
|
||||
|
||||
# Or the files created by dart2js.
|
||||
*.dart.js
|
||||
*.dart.precompiled.js
|
||||
@ -14,6 +17,9 @@ bower_components
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
# Or type definitions we mirror from github
|
||||
**/typings/**/*.d.ts
|
||||
|
||||
# Include when developing application packages.
|
||||
pubspec.lock
|
||||
.c9
|
||||
@ -23,4 +29,7 @@ pubspec.lock
|
||||
# Don't check in secret files
|
||||
*secret.js
|
||||
|
||||
# Ignore npm debug log
|
||||
npm-debug.log
|
||||
|
||||
/docs/bower_components/
|
||||
|
11
.travis.yml
11
.travis.yml
@ -8,13 +8,20 @@ env:
|
||||
- E2E_BROWSERS=Dartium
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- ARCH=linux-x64
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# because those are not visible for pull requests, and those should also be reliable.
|
||||
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# (password is in Valentine)
|
||||
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
matrix:
|
||||
- MODE=js DART_CHANNEL=dev
|
||||
# Dissabled until Dart v1.9 hits stable
|
||||
# - MODE=dart DART_CHANNEL=stable
|
||||
- MODE=dart DART_CHANNEL=stable
|
||||
- MODE=dart DART_CHANNEL=dev
|
||||
|
||||
before_install:
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
|
79
DEVELOPER.md
79
DEVELOPER.md
@ -3,24 +3,24 @@
|
||||
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
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
* [Getting the Sources](#getting-the-sources)
|
||||
* [Environment Variable Setup](#environment-variable-setup)
|
||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
* [Project Information](#project-information)
|
||||
* [CI using Travis](#ci-using-travis)
|
||||
* [Debugging](#debugging)
|
||||
|
||||
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)
|
||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
if you'd like to contribute to Angular.
|
||||
|
||||
## 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
|
||||
* [Dart](https://www.dartlang.org) (version ` >=1.9.0 <2.0.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)
|
||||
@ -39,6 +39,8 @@ following products on your development machine:
|
||||
* [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).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
@ -95,27 +97,26 @@ 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 get
|
||||
```
|
||||
|
||||
**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:
|
||||
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
|
||||
|
||||
*Option 1*: 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.
|
||||
Since global installs can become stale, and required versions can vary by project, we avoid their
|
||||
use in these instructions.
|
||||
|
||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||
|
||||
## Build commands
|
||||
|
||||
To build Angular and prepare tests run
|
||||
To build Angular and prepare tests run:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp build
|
||||
@ -133,24 +134,37 @@ $(npm bin)/gulp clean
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Basic tests
|
||||
### Full test suite
|
||||
|
||||
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; runs in **watch mode**
|
||||
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests as
|
||||
those run on Travis.
|
||||
|
||||
If you prefer running tests in "single-run" mode rather than watch mode use
|
||||
You can selectively run either the JS or Dart versions as follows:
|
||||
|
||||
* `$(npm bin)/gulp test.all.js`
|
||||
* `$(npm bin)/gulp test.all.dart`
|
||||
|
||||
### Unit tests
|
||||
|
||||
You can run just the unit tests as follows:
|
||||
|
||||
* `$(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).
|
||||
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
|
||||
* `$(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.cjs/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**: 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
|
||||
**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).
|
||||
|
||||
@ -232,4 +246,3 @@ 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.
|
||||
|
||||
|
@ -36,6 +36,7 @@ comments in the source `modules/examples/src/hello_world/index.js`.
|
||||
You can build this example as either JS or Dart app:
|
||||
|
||||
* JS:
|
||||
* `$(npm bin)/gulp build.js.dev`, and
|
||||
* `$(npm bin)/gulp serve.js.dev`, and
|
||||
* open `localhost:8000/examples/src/hello_world/` in Chrome.
|
||||
* Dart:
|
||||
|
@ -364,3 +364,16 @@ md-content.demo-source-container > hljs > pre > code.highlight {
|
||||
padding-left:32px !important;
|
||||
padding-right:32px !important;
|
||||
}
|
||||
|
||||
|
||||
.member .name {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: monospace;
|
||||
font-size: 1.17em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.left-nav {
|
||||
min-width: 300px;
|
||||
}
|
@ -28,7 +28,7 @@
|
||||
|
||||
<section layout="row">
|
||||
|
||||
<md-content>
|
||||
<md-content class="left-nav">
|
||||
<h2>Navigation</h2>
|
||||
<section ng-repeat="area in nav.areas">
|
||||
<h3>{{ area.name }}</h3>
|
||||
|
@ -37,15 +37,19 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/generateDocsFromComments'))
|
||||
.processor(require('./processors/processModuleDocs'))
|
||||
.processor(require('./processors/processClassDocs'))
|
||||
.processor(require('./processors/captureModuleExports'))
|
||||
.processor(require('./processors/captureClassMembers'))
|
||||
.processor(require('./processors/captureModuleDocs'))
|
||||
.processor(require('./processors/attachModuleDocs'))
|
||||
.processor(require('./processors/cloneExportedFromDocs'))
|
||||
.processor(require('./processors/generateNavigationDoc'))
|
||||
.processor(require('./processors/extractTitleFromGuides'))
|
||||
.processor(require('./processors/createOverviewDump'))
|
||||
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'info';
|
||||
log.level = 'warning';
|
||||
})
|
||||
|
||||
|
||||
@ -62,6 +66,12 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
})
|
||||
|
||||
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/public'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/exportedAs'));
|
||||
})
|
||||
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/docs';
|
||||
@ -144,4 +154,4 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
pathTemplate: '${id}',
|
||||
outputPathTemplate: GUIDES_PATH + '/${id}.html'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
22
docs/dgeni-package/processors/attachModuleDocs.js
Normal file
22
docs/dgeni-package/processors/attachModuleDocs.js
Normal file
@ -0,0 +1,22 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function attachModuleDocs(log) {
|
||||
|
||||
return {
|
||||
$runAfter: ['tags-extracted'],
|
||||
$runBefore: ['computing-ids'],
|
||||
$process: function(docs) {
|
||||
return _.filter(docs, function(doc) {
|
||||
if (doc.docType !== 'moduleDoc') {
|
||||
return true;
|
||||
}
|
||||
if (doc.module || doc.module === '') {
|
||||
doc.moduleDoc.description = doc.description;
|
||||
doc.moduleDoc.public = doc.public;
|
||||
log.debug('attached', doc.moduleDoc.id, doc.moduleDoc.description);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -1,10 +1,10 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
module.exports = function captureClassMembers(log, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['processModuleDocs'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
$runAfter: ['captureModuleExports'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
ignorePrivateMembers: false,
|
||||
$process: function(docs) {
|
||||
var memberDocs = [];
|
||||
@ -17,15 +17,20 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
// Create a new doc for each member of the class
|
||||
_.forEach(classDoc.elements, function(memberDoc) {
|
||||
|
||||
if (ignorePrivateMembers && memberDoc.name.literalToken.value.charAt(0) === '_') return;
|
||||
var memberName = memberDoc.name.location.toString();
|
||||
|
||||
if (ignorePrivateMembers && memberName.charAt(0) === '_') return;
|
||||
|
||||
classDoc.members.push(memberDoc);
|
||||
memberDocs.push(memberDoc);
|
||||
|
||||
memberDoc.docType = 'member';
|
||||
memberDoc.classDoc = classDoc;
|
||||
memberDoc.name = memberDoc.name.literalToken.value;
|
||||
|
||||
memberDoc.name = memberName;
|
||||
if (memberDoc.parameterList) {
|
||||
memberDoc.params = memberDoc.parameterList.parameters.map(function(param) {
|
||||
return param.location.toString();
|
||||
});
|
||||
}
|
||||
|
||||
if (memberDoc.commentBefore ) {
|
||||
// If this export has a comment, remove it from the list of
|
||||
@ -37,6 +42,14 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
|
||||
_.assign(memberDoc, getJSDocComment(memberDoc.commentBefore));
|
||||
}
|
||||
|
||||
// Constuctor is a special case member
|
||||
if (memberName === 'constructor') {
|
||||
classDoc.constructorDoc = memberDoc;
|
||||
} else {
|
||||
insertSorted(classDoc.members, memberDoc, 'name');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -45,3 +58,13 @@ module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function insertSorted(collection, item, property) {
|
||||
var index = collection.length;
|
||||
while(index>0) {
|
||||
if(collection[index-1][property] < item[property]) break;
|
||||
index -= 1;
|
||||
}
|
||||
collection.splice(index, 0, item);
|
||||
}
|
31
docs/dgeni-package/processors/captureModuleDocs.js
Normal file
31
docs/dgeni-package/processors/captureModuleDocs.js
Normal file
@ -0,0 +1,31 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function captureModuleDocs(log, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['captureClassMembers'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
$process: function(docs) {
|
||||
// Generate docs for each module's file's comments not already captured
|
||||
_.forEach(docs, function(moduleDoc) {
|
||||
|
||||
if ( moduleDoc.docType !== 'module' ) return;
|
||||
|
||||
moduleDoc.extraComments = [];
|
||||
_.forEach(moduleDoc.comments, function(comment) {
|
||||
var jsDocComment = getJSDocComment(comment);
|
||||
if (jsDocComment) {
|
||||
jsDocComment.docType = 'moduleDoc';
|
||||
jsDocComment.moduleDoc = moduleDoc;
|
||||
moduleDoc.extraComments.push(jsDocComment);
|
||||
docs.push(jsDocComment);
|
||||
// console.log('found', jsDocComment.content);
|
||||
}
|
||||
});
|
||||
if ( moduleDoc.extraComments.length > 0 ) {
|
||||
// console.log(moduleDoc.extraComments.length);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -1,12 +1,12 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComment) {
|
||||
module.exports = function captureModuleExports(log, ExportTreeVisitor, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
$process: function(docs) {
|
||||
var exportDocs = [];
|
||||
var extraDocs = [];
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
|
||||
@ -21,7 +21,7 @@ module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComm
|
||||
_.forEach(visitor.exports, function(exportDoc) {
|
||||
|
||||
doc.exports.push(exportDoc);
|
||||
exportDocs.push(exportDoc);
|
||||
extraDocs.push(exportDoc);
|
||||
exportDoc.moduleDoc = doc;
|
||||
|
||||
if (exportDoc.comment) {
|
||||
@ -40,7 +40,7 @@ module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComm
|
||||
}
|
||||
});
|
||||
|
||||
return docs.concat(exportDocs);
|
||||
return docs.concat(extraDocs);
|
||||
}
|
||||
};
|
||||
};
|
37
docs/dgeni-package/processors/cloneExportedFromDocs.js
Normal file
37
docs/dgeni-package/processors/cloneExportedFromDocs.js
Normal file
@ -0,0 +1,37 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function cloneExportedFromDocs(modules, EXPORT_DOC_TYPES) {
|
||||
return {
|
||||
$runAfter: ['tags-parsed', 'attachModuleDocs'],
|
||||
$runBefore: ['computing-ids'],
|
||||
$process: function(docs) {
|
||||
|
||||
var extraPublicDocs = [];
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
|
||||
if (EXPORT_DOC_TYPES.indexOf(doc.docType) === -1 || !doc.exportedAs) return;
|
||||
|
||||
_.forEach(doc.exportedAs, function(exportedAs) {
|
||||
var exportedAsModule = modules[exportedAs];
|
||||
|
||||
if (!exportedAsModule) {
|
||||
throw new Error('Missing module definition: "' + doc.exportedAs + '"\n' +
|
||||
'Referenced in "@exportedAs" tag on class: "' + doc.moduleDoc.id + '/' + doc.name + '"');
|
||||
} else {
|
||||
|
||||
// Add a clone of export to its "exportedAs" module
|
||||
var clonedDoc = _.clone(doc);
|
||||
clonedDoc.moduleDoc = exportedAsModule;
|
||||
exportedAsModule.exports.push(clonedDoc);
|
||||
extraPublicDocs.push(clonedDoc);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
docs = docs.concat(extraPublicDocs);
|
||||
|
||||
return docs;
|
||||
}
|
||||
};
|
||||
};
|
24
docs/dgeni-package/processors/createOverviewDump.js
Normal file
24
docs/dgeni-package/processors/createOverviewDump.js
Normal file
@ -0,0 +1,24 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function createOverviewDump() {
|
||||
|
||||
return {
|
||||
$runAfter: ['captureModuleExports', 'captureClassMembers'],
|
||||
$runBefore: ['docs-processed'],
|
||||
$process: function(docs) {
|
||||
var overviewDoc = {
|
||||
id: 'overview-dump',
|
||||
aliases: ['overview-dump'],
|
||||
path: 'overview-dump',
|
||||
outputPath: 'overview-dump.html',
|
||||
modules: []
|
||||
};
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
overviewDoc.modules.push(doc);
|
||||
}
|
||||
});
|
||||
docs.push(overviewDoc);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function generateDocsFromComments(log) {
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
$process: function(docs) {
|
||||
var commentDocs = [];
|
||||
docs = _.filter(docs, function(doc) {
|
||||
if (doc.docType !== 'atScriptFile') {
|
||||
return true;
|
||||
} else {
|
||||
_.forEach(doc.fileInfo.comments, function(comment) {
|
||||
|
||||
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
|
||||
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
|
||||
|
||||
// Create a doc from this comment
|
||||
commentDocs.push({
|
||||
fileInfo: doc.fileInfo,
|
||||
startingLine: comment.range.start.line,
|
||||
endingLine: comment.range.end.line,
|
||||
content: commentBody,
|
||||
codeTree: comment.treeAfter,
|
||||
docType: 'atScriptDoc'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return docs.concat(commentDocs);
|
||||
}
|
||||
};
|
||||
};
|
@ -30,11 +30,18 @@ module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
|
||||
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;
|
||||
this.currentComment.treeAfter = tree;
|
||||
var commentText = this.currentComment.range.toString();
|
||||
|
||||
// Only store the comment if it is JSDOC style (e.g. /** some comment */)
|
||||
if (/^\/\*\*([\w\W]*)\*\/$/.test(commentText)) {
|
||||
log.info('comment: ' + this.currentComment.range.start.line + ' - ' +
|
||||
this.currentComment.range.end.line + ' : ' +
|
||||
commentText);
|
||||
|
||||
tree.commentBefore = this.currentComment;
|
||||
this.currentComment.treeAfter = tree;
|
||||
}
|
||||
|
||||
this.index++;
|
||||
this.currentComment = this.comments[this.index];
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = tree.name.identifierToken.value;
|
||||
this.currentExport.functionKind = tree.functionKind;
|
||||
this.currentExport.parameters = tree.parameterList.parameters;
|
||||
this.currentExport.parameters = tree.parameterList.parameters.map(function(param) {
|
||||
return param.location.toString();
|
||||
});
|
||||
this.currentExport.typeAnnotation = tree.typeAnnotation;
|
||||
this.currentExport.annotations = tree.annotations;
|
||||
this.currentExport.docType = 'function';
|
||||
|
@ -34,13 +34,18 @@ module.exports = function atParser(AttachCommentTreeVisitor, SourceFile, Traceur
|
||||
var sourceFile = new SourceFile(moduleName, fileInfo.content);
|
||||
var comments = [];
|
||||
var moduleTree;
|
||||
var parser = new TraceurParser(sourceFile);
|
||||
var errorReporter = {
|
||||
reportError: function(position, message) {
|
||||
}
|
||||
};
|
||||
|
||||
traceurOptions.setFromObject(service.traceurOptions);
|
||||
var parser = new TraceurParser(sourceFile, errorReporter, traceurOptions);
|
||||
|
||||
// Configure the parser
|
||||
parser.handleComment = function(range) {
|
||||
comments.push({ range: range });
|
||||
};
|
||||
traceurOptions.setFromObject(service.traceurOptions);
|
||||
|
||||
try {
|
||||
// Parse the file as a module, attaching the comments
|
||||
|
@ -1,3 +1,4 @@
|
||||
module.exports = function traceurOptions() {
|
||||
return System.get(System.map.traceur + '/src/Options.js').options;
|
||||
var Options = System.get(System.map.traceur + "/src/Options.js").Options;
|
||||
return new Options();
|
||||
};
|
4
docs/dgeni-package/tag-defs/exportedAs.js
Normal file
4
docs/dgeni-package/tag-defs/exportedAs.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
name: 'exportedAs',
|
||||
multi: true
|
||||
};
|
4
docs/dgeni-package/tag-defs/public.js
Normal file
4
docs/dgeni-package/tag-defs/public.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
name: 'public',
|
||||
transforms: function(doc, tag) { return true; }
|
||||
};
|
@ -1,14 +1,33 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
{% include "lib/paramList.html" -%}
|
||||
{% extends 'layout/base.template.html' -%}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.name $} <span class="type">class</span></h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
|
||||
<h1 class="class export">{$ doc.name $} <span class="type">class</span></h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a><br/>
|
||||
defined in <a href="https://github.com/angular/angular/tree/master/modules/{$ doc.location.start.source.name $}.js#L{$ doc.location.start.line $}">{$ doc.location.start.source.name $}.js (line {$ doc.location.start.line $})</a></p>
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
{%- if doc.constructorDoc or doc.members.length -%}
|
||||
<h2>Members</h2>
|
||||
{% for member in doc.members %}
|
||||
<h3>{$ member.name $}</h3>
|
||||
<p>{$ member.description | marked $}</p>
|
||||
|
||||
{%- if doc.constructorDoc %}
|
||||
<section class="member constructor">
|
||||
<h1 class="name">{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h1>
|
||||
{% marked %}
|
||||
{$ doc.constructorDoc.description $}
|
||||
{% endmarked %}
|
||||
</section>
|
||||
{% endif -%}
|
||||
|
||||
{%- for member in doc.members %}
|
||||
<section class="member">
|
||||
<h1 class="name">{$ member.name $}{$ paramList(member.params) $}</h1>
|
||||
{% marked %}
|
||||
{$ member.description $}
|
||||
{% endmarked %}
|
||||
</section>
|
||||
|
||||
{% endfor %}
|
||||
{%- endif -%}
|
||||
|
||||
{% endblock %}
|
9
docs/dgeni-package/templates/function.template.html
Normal file
9
docs/dgeni-package/templates/function.template.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% include "lib/paramList.html" -%}
|
||||
{% extends 'layout/base.template.html' -%}
|
||||
|
||||
{% block body %}
|
||||
<h1 class="function export">{$ doc.name $}{$ paramList(doc.parameters) $}</h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
{% endblock %}
|
7
docs/dgeni-package/templates/lib/paramList.html
Normal file
7
docs/dgeni-package/templates/lib/paramList.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% macro paramList(params) -%}
|
||||
{%- if params -%}<span class="params">(
|
||||
{%- for param in params -%}
|
||||
<span class="param">{$ param | escape $}{% if not loop.last %}, {% endif %}</span>
|
||||
{%- endfor %})</span>
|
||||
{%- endif %}
|
||||
{%- endmacro -%}
|
@ -9,7 +9,7 @@
|
||||
<h2>Exports</h2>
|
||||
<ul>
|
||||
{%- for exportDoc in doc.exports %}
|
||||
<li><a href="/{$ exportDoc.path $}">{$ exportDoc.name $} {$ exportDoc.docType $}</a></li>
|
||||
<li><a href="/{$ exportDoc.path $}"><strong>{$ exportDoc.name $}</strong> {$ exportDoc.docType $}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
43
docs/dgeni-package/templates/overview-dump.template.html
Normal file
43
docs/dgeni-package/templates/overview-dump.template.html
Normal file
@ -0,0 +1,43 @@
|
||||
{% include "lib/paramList.html" -%}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<style>
|
||||
h2 {
|
||||
padding-left: 20px;
|
||||
}
|
||||
h3 {
|
||||
padding-left: 50px;
|
||||
}
|
||||
h4 {
|
||||
padding-left: 60px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<h1>Modules</h1>
|
||||
|
||||
{% for module in doc.modules %}
|
||||
|
||||
<h2>{$ module.id $}
|
||||
{%- if module.public %} (public){% endif %}</h2>
|
||||
|
||||
{% for export in module.exports %}
|
||||
<h3>{$ export.name $}</h3>
|
||||
|
||||
{%- if export.constructorDoc %}
|
||||
<h4>{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h4>
|
||||
{% endif -%}
|
||||
{%- for member in export.members %}
|
||||
<h4>{$ member.name $}{$ paramList(member.params) $}</h4>
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</body>
|
||||
</html>
|
8
docs/dgeni-package/templates/var.template.html
Normal file
8
docs/dgeni-package/templates/var.template.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.name $} <span class="type">variable</span></h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
{% endblock %}
|
@ -6,16 +6,11 @@ module.exports = new Package('angular-public', [basePackage])
|
||||
|
||||
.processor(require('./processors/filterPublicDocs'))
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
|
||||
})
|
||||
|
||||
.config(function(processClassDocs, filterPublicDocs, EXPORT_DOC_TYPES) {
|
||||
processClassDocs.ignorePrivateMembers = true;
|
||||
filterPublicDocs.docTypes = EXPORT_DOC_TYPES;
|
||||
.config(function(captureClassMembers) {
|
||||
captureClassMembers.ignorePrivateMembers = true;
|
||||
})
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/public_docs';
|
||||
});
|
||||
});
|
@ -1,51 +1,28 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function filterPublicDocs(modules) {
|
||||
module.exports = function filterPublicDocs(modules, EXPORT_DOC_TYPES) {
|
||||
return {
|
||||
$runAfter: ['tags-parsed'],
|
||||
$runAfter: ['tags-parsed', 'cloneExportedFromDocs'],
|
||||
$runBefore: ['computing-ids'],
|
||||
docTypes: [],
|
||||
$validate: {
|
||||
docTypes: { presence: true }
|
||||
},
|
||||
$process: function(docs) {
|
||||
|
||||
docTypes = this.docTypes;
|
||||
// Filter out the documents that are not public
|
||||
return _.filter(docs, function(doc) {
|
||||
|
||||
if (doc.docType === 'module') {
|
||||
// doc is a module - is it public?
|
||||
return doc.public;
|
||||
}
|
||||
|
||||
docs = _.filter(docs, function(doc) {
|
||||
if (EXPORT_DOC_TYPES.indexOf(doc.docType) === -1) {
|
||||
// doc is not a type we care about
|
||||
return true;
|
||||
}
|
||||
|
||||
if (docTypes.indexOf(doc.docType) === -1) return true;
|
||||
if (!doc.publicModule) return false;
|
||||
// doc is in a public module
|
||||
return doc.moduleDoc && doc.moduleDoc.public;
|
||||
|
||||
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;
|
||||
|
||||
_.remove(classDoc.moduleDoc.exports, function(doc) { return doc === classDoc; });
|
||||
classDoc.moduleDoc = publicModule;
|
||||
publicModule.exports.push(classDoc);
|
||||
|
||||
}
|
||||
};
|
328
gulpfile.js
328
gulpfile.js
@ -1,12 +1,13 @@
|
||||
var format = require('gulp-clang-format');
|
||||
var gulp = require('gulp');
|
||||
var gulpPlugins = require('gulp-load-plugins')();
|
||||
var shell = require('gulp-shell');
|
||||
var runSequence = require('run-sequence');
|
||||
var madge = require('madge');
|
||||
var merge = require('merge');
|
||||
var path = require('path');
|
||||
|
||||
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
|
||||
|
||||
var clean = require('./tools/build/clean');
|
||||
var transpile = require('./tools/build/transpile');
|
||||
var html = require('./tools/build/html');
|
||||
@ -23,11 +24,24 @@ var karma = require('karma').server;
|
||||
var minimist = require('minimist');
|
||||
var es5build = require('./tools/build/es5build');
|
||||
var runServerDartTests = require('./tools/build/run_server_dart_tests');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var transformCJSTests = require('./tools/build/transformCJSTests');
|
||||
var ts2dart = require('gulp-ts2dart');
|
||||
var tsc = require('gulp-typescript');
|
||||
var util = require('./tools/build/util');
|
||||
var bundler = require('./tools/build/bundle');
|
||||
var replace = require('gulp-replace');
|
||||
var insert = require('gulp-insert');
|
||||
|
||||
// dynamic require in build.broccoli.tools so we can bootstrap TypeScript compilation
|
||||
function missingDynamicBroccoli() {
|
||||
throw new Error('ERROR: build.broccoli.tools task should have been run before using broccoli');
|
||||
}
|
||||
var makeBroccoliTree = missingDynamicBroccoli, broccoliBuild = missingDynamicBroccoli;
|
||||
|
||||
// Note: when DART_SDK is not found, all gulp tasks ending with `.dart` will be skipped.
|
||||
|
||||
var DART_SDK = require('./tools/build/dartdetect')(gulp);
|
||||
|
||||
// -----------------------
|
||||
// configuration
|
||||
|
||||
@ -48,6 +62,8 @@ var _HTML_DEFAULT_SCRIPTS_JS = [
|
||||
{src: 'node_modules/zone.js/long-stack-trace-zone.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/systemjs/dist/system.src.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/systemjs/lib/extension-register.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/systemjs/lib/extension-cjs.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/rx/dist/rx.all.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'tools/build/snippets/runtime_paths.js', mimeType: 'text/javascript', copy: true},
|
||||
{
|
||||
inline: 'System.import(\'$MODULENAME$\').then(function(m) { m.main(); }, console.error.bind(console))',
|
||||
@ -116,10 +132,8 @@ var CONFIG = {
|
||||
transpile: {
|
||||
src: {
|
||||
js: ['modules/**/*.js', 'modules/**/*.es6'],
|
||||
dart: ['modules/**/*.js'],
|
||||
// Migrating to TypeScript, one package at a time.
|
||||
// See https://docs.google.com/document/d/14RJLhu6uuv7NchFkAb6PKzOOO0L7l3Z507eKWzkEUhQ/edit
|
||||
ts2dart: ['modules/angular2/src/di/*.js', 'modules/angular2/test/di/*.js', 'modules/angular2/src/test_lib/*.js']
|
||||
ts: ['modules/**/*.ts'],
|
||||
dart: ['modules/**/*.js']
|
||||
},
|
||||
options: {
|
||||
js: {
|
||||
@ -134,26 +148,21 @@ var CONFIG = {
|
||||
}),
|
||||
cjs: merge(true, _COMPILER_CONFIG_JS_DEFAULT, {
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
typeAssertions: true,
|
||||
// Don't use type assertions since this is partly transpiled by typescript
|
||||
typeAssertions: false,
|
||||
modules: 'commonjs'
|
||||
})
|
||||
},
|
||||
dart: {
|
||||
sourceMaps: true,
|
||||
annotations: true, // parse annotations
|
||||
types: true, // parse types
|
||||
script: false, // parse as a module
|
||||
memberVariables: true, // parse class fields
|
||||
outputLanguage: 'dart'
|
||||
}
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
js: {
|
||||
cjs: {
|
||||
src: ['modules/**/README.js.md', 'modules/**/package.json', 'modules/**/*.cjs'],
|
||||
src: [
|
||||
'modules/**/*.md', '!modules/**/*.dart.md', 'modules/**/*.png',
|
||||
'modules/**/package.json'
|
||||
],
|
||||
pipes: {
|
||||
'**/*.cjs': gulpPlugins.rename({extname: '.js'}),
|
||||
'**/*.js.md': gulpPlugins.rename(function(file) {
|
||||
file.basename = file.basename.substring(0, file.basename.lastIndexOf('.'));
|
||||
}),
|
||||
@ -170,7 +179,10 @@ var CONFIG = {
|
||||
}
|
||||
},
|
||||
dart: {
|
||||
src: ['modules/**/README.dart.md', 'modules/**/*.dart', 'modules/*/pubspec.yaml', 'modules/**/*.css', '!modules/**/e2e_test/**'],
|
||||
src: [
|
||||
'modules/**/*.md', '!modules/**/*.js.md', 'modules/**/*.png', 'modules/**/*.html',
|
||||
'modules/**/*.dart', 'modules/*/pubspec.yaml', 'modules/**/*.css', '!modules/**/e2e_test/**'
|
||||
],
|
||||
pipes: {
|
||||
'**/*.dart': util.insertSrcFolder(gulpPlugins, SRC_FOLDER_INSERTION.dart),
|
||||
'**/*.dart.md': gulpPlugins.rename(function(file) {
|
||||
@ -203,7 +215,7 @@ var CONFIG = {
|
||||
},
|
||||
dart: {
|
||||
src: ['LICENSE'],
|
||||
exclude: ['rtts_assert/'],
|
||||
exclude: ['rtts_assert'],
|
||||
pipes: {}
|
||||
}
|
||||
},
|
||||
@ -282,13 +294,42 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, {
|
||||
// ------------
|
||||
// transpile
|
||||
|
||||
gulp.task('build/transpile.js.dev.es6', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.js,
|
||||
dest: CONFIG.dest.js.dev.es6,
|
||||
outputExt: 'es6',
|
||||
options: CONFIG.transpile.options.js.dev,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
||||
}));
|
||||
gulp.task('build/transpile.ts.cjs', function() {
|
||||
var tsResult = gulp.src(CONFIG.transpile.src.ts)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(tsc({
|
||||
target: 'ES5',
|
||||
module: /*system.js*/'commonjs',
|
||||
allowNonTsExtensions: false,
|
||||
typescript: require('typescript'),
|
||||
//declarationFiles: true,
|
||||
noEmitOnError: true
|
||||
}));
|
||||
var dest = gulp.dest(CONFIG.dest.js.cjs);
|
||||
return merge([
|
||||
// Write external sourcemap next to the js file
|
||||
tsResult.js.pipe(sourcemaps.write('.')).pipe(dest),
|
||||
tsResult.js.pipe(dest),
|
||||
tsResult.dts.pipe(dest),
|
||||
]);
|
||||
});
|
||||
|
||||
gulp.task('build/transpile.ts.dev.es5', function() {
|
||||
var tsResult = gulp.src(CONFIG.transpile.src.ts)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(tsc({
|
||||
|
||||
target: 'ES5',
|
||||
module: 'commonjs',
|
||||
typescript: require('typescript'),
|
||||
noEmitOnError: true
|
||||
}));
|
||||
return merge([
|
||||
tsResult.js.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(CONFIG.dest.js.dev.es5)),
|
||||
tsResult.js.pipe(gulp.dest(CONFIG.dest.js.dev.es5))
|
||||
]);
|
||||
});
|
||||
|
||||
gulp.task('build/transpile.js.dev.es5', function() {
|
||||
return es5build({
|
||||
@ -298,14 +339,6 @@ gulp.task('build/transpile.js.dev.es5', function() {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build/transpile.js.dev', function(done) {
|
||||
runSequence(
|
||||
'build/transpile.js.dev.es6',
|
||||
'build/transpile.js.dev.es5',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build/transpile.js.prod.es6', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.js,
|
||||
dest: CONFIG.dest.js.prod.es6,
|
||||
@ -338,36 +371,13 @@ gulp.task('build/transpile.js.cjs', transpile(gulp, gulpPlugins, {
|
||||
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/'));
|
||||
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, {
|
||||
src: CONFIG.transpile.src.dart,
|
||||
dest: CONFIG.dest.dart,
|
||||
outputExt: 'dart',
|
||||
options: CONFIG.transpile.options.dart,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.dart
|
||||
}));
|
||||
|
||||
gulp.task('build/transpile.dart.ts2dart', function() {
|
||||
return gulp.src(CONFIG.transpile.src.ts2dart)
|
||||
.pipe(ts2dart.transpile())
|
||||
.pipe(gulp.dest('dist/dart.ts2dart'))
|
||||
});
|
||||
gulp.task('build/format.dart.ts2dart', rundartpackage(gulp, gulpPlugins, {
|
||||
pub: DART_SDK.PUB,
|
||||
packageName: CONFIG.formatDart.packageName,
|
||||
args: ['dart_style:format', '-w', 'dist/dart.ts2dart']
|
||||
}));
|
||||
|
||||
// Temporary tasks for development on ts2dart. Will likely fail.
|
||||
gulp.task('build/transpile.dart.ts2dart.all', function() {
|
||||
return gulp.src(CONFIG.transpile.src.dart)
|
||||
.pipe(ts2dart.transpile())
|
||||
.pipe(gulp.dest('dist/dart.ts2dart'));
|
||||
});
|
||||
gulp.task('ts2dart', function(done) {
|
||||
runSequence('build/transpile.dart.ts2dart.all', 'build/format.dart.ts2dart', done);
|
||||
gulp.task('build/transpile.dart', ['build.broccoli.tools'], function() {
|
||||
return broccoliBuild(makeBroccoliTree('dart'), 'dart');
|
||||
});
|
||||
|
||||
// ------------
|
||||
@ -456,7 +466,11 @@ gulp.task('build/multicopy.dart', copy.multicopy(gulp, gulpPlugins, {
|
||||
// ------------
|
||||
// pubspec
|
||||
|
||||
gulp.task('build/pubspec.dart', pubget(gulp, gulpPlugins, {
|
||||
// Run a top-level `pub get` for this project.
|
||||
gulp.task('pubget.dart', pubget.dir(gulp, gulpPlugins, { dir: '.', command: DART_SDK.PUB }));
|
||||
|
||||
// Run `pub get` over CONFIG.dest.dart
|
||||
gulp.task('build/pubspec.dart', pubget.subDir(gulp, gulpPlugins, {
|
||||
dir: CONFIG.dest.dart,
|
||||
command: DART_SDK.PUB
|
||||
}));
|
||||
@ -486,7 +500,7 @@ gulp.task('build/pubbuild.dart', pubbuild(gulp, gulpPlugins, {
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// format dart
|
||||
// formatting
|
||||
|
||||
gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
|
||||
pub: DART_SDK.PUB,
|
||||
@ -494,6 +508,24 @@ gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
|
||||
args: CONFIG.formatDart.args
|
||||
}));
|
||||
|
||||
function doCheckFormat() {
|
||||
return gulp.src(['Brocfile*.js', 'modules/**/*.ts', 'tools/**/*.ts', '!**/typings/**/*.d.ts'])
|
||||
.pipe(format.checkFormat('file'));
|
||||
}
|
||||
|
||||
gulp.task('check-format', function() {
|
||||
return doCheckFormat().on('warning', function(e) {
|
||||
console.log("NOTE: this will be promoted to an ERROR in the continuous build");
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('enforce-format', function() {
|
||||
return doCheckFormat().on('warning', function(e) {
|
||||
console.log("ERROR: Some files need formatting");
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ------------
|
||||
// check circular dependencies in Node.js context
|
||||
gulp.task('build/checkCircularDependencies', function (done) {
|
||||
@ -545,6 +577,24 @@ gulp.task('serve/benchmarks_external.dart', pubserve(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.dart + '/benchmarks_external'
|
||||
}));
|
||||
|
||||
gulp.task('serve/examples.dart.static', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
mode: 'ngstatic',
|
||||
path: CONFIG.dest.dart + '/examples'
|
||||
}));
|
||||
|
||||
gulp.task('serve/benchmarks.dart.static', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
mode: 'ngstatic',
|
||||
path: CONFIG.dest.dart + '/benchmarks'
|
||||
}));
|
||||
|
||||
gulp.task('serve/benchmarks_external.dart.static', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
mode: 'ngstatic',
|
||||
path: CONFIG.dest.dart + '/benchmarks_external'
|
||||
}));
|
||||
|
||||
// --------------
|
||||
// doc generation
|
||||
var Dgeni = require('dgeni');
|
||||
@ -613,6 +663,22 @@ createDocsTasks(true);
|
||||
createDocsTasks(false);
|
||||
|
||||
// ------------------
|
||||
// CI tests suites
|
||||
|
||||
gulp.task('test.js', function(done) {
|
||||
runSequence('test.transpiler.unittest', 'docs/test', 'test.unit.js/ci',
|
||||
'test.unit.cjs/ci', done);
|
||||
});
|
||||
|
||||
gulp.task('test.dart', function(done) {
|
||||
runSequence('test.transpiler.unittest', 'docs/test', 'test.unit.dart/ci', done);
|
||||
});
|
||||
|
||||
// Reuse the Travis scripts
|
||||
// TODO: rename test_*.sh to test_all_*.sh
|
||||
gulp.task('test.all.js', shell.task(['./scripts/ci/test_js.sh']))
|
||||
gulp.task('test.all.dart', shell.task(['./scripts/ci/test_dart.sh']))
|
||||
|
||||
// karma tests
|
||||
// These tests run in the browser and are allowed to access
|
||||
// HTML DOM APIs.
|
||||
@ -640,6 +706,7 @@ gulp.task('test.unit.cjs/ci', function () {
|
||||
gulp.task('test.unit.cjs', ['build.js.cjs'], function () {
|
||||
//Run tests once
|
||||
runSequence('test.unit.cjs/ci', function() {});
|
||||
|
||||
//Watcher to transpile file changed
|
||||
gulp.watch(CONFIG.transpile.src.js.concat(['modules/**/*.cjs']), function(event) {
|
||||
var relPath = path.relative(__dirname, event.path).replace(/\\/g, "/");
|
||||
@ -652,14 +719,15 @@ gulp.task('test.unit.cjs', ['build.js.cjs'], function () {
|
||||
});
|
||||
//Watcher to run tests when dist/js/cjs/angular2 is updated by the first watcher (after clearing the node cache)
|
||||
gulp.watch(CONFIG.dest.js.cjs + '/angular2/**/*.js', function(event) {
|
||||
for (var id in require.cache) {
|
||||
for (var id in require.cache) {
|
||||
if (id.replace(/\\/g, "/").indexOf(CONFIG.dest.js.cjs) > -1) {
|
||||
delete require.cache[id];
|
||||
delete require.cache[id];
|
||||
}
|
||||
}
|
||||
global.assert = undefined; // https://github.com/angular/angular/issues/1340
|
||||
runSequence('test.unit.cjs/ci', function() {});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// ------------------
|
||||
@ -672,31 +740,22 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {
|
||||
|
||||
// -----------------
|
||||
// test builders
|
||||
gulp.task('test.transpiler.unittest', function (done) {
|
||||
gulp.task('test.transpiler.unittest', function() {
|
||||
return gulp.src('tools/transpiler/unittest/**/*.js')
|
||||
.pipe(jasmine({
|
||||
includeStackTrace: true
|
||||
}))
|
||||
});
|
||||
|
||||
// Copy test resources to dist
|
||||
gulp.task('tests/transform.dart', function() {
|
||||
return gulp.src('modules/angular2/test/transform/**')
|
||||
.pipe(gulp.dest('dist/dart/angular2/test/transform'));
|
||||
});
|
||||
|
||||
|
||||
|
||||
// -----------------
|
||||
// orchestrated targets
|
||||
|
||||
// Builds all Dart packages, but does not compile them
|
||||
gulp.task('build/packages.dart', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.dart.ts2dart', 'build/transpile.dart', 'build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
|
||||
'tests/transform.dart',
|
||||
['build/format.dart.ts2dart', 'build/format.dart'],
|
||||
'build/pubspec.dart',
|
||||
'build/transpile.dart', // Creates the folder structure needed by subsequent tasks.
|
||||
['build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
|
||||
'build/format.dart',
|
||||
done
|
||||
);
|
||||
});
|
||||
@ -705,36 +764,129 @@ gulp.task('build/packages.dart', function(done) {
|
||||
gulp.task('build.dart', function(done) {
|
||||
runSequence(
|
||||
'build/packages.dart',
|
||||
'build/pubspec.dart',
|
||||
'build/analyze.dart',
|
||||
'build/pubbuild.dart',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.broccoli.tools', function() {
|
||||
var tsResult = gulp.src('tools/broccoli/**/*.ts')
|
||||
.pipe(tsc({ target: 'ES5', module: 'commonjs' }));
|
||||
return tsResult.js.pipe(gulp.dest('dist/broccoli'))
|
||||
.on('end', function() {
|
||||
makeBroccoliTree = require('./dist/broccoli/make-broccoli-tree');
|
||||
broccoliBuild = require('./dist/broccoli/gulp');
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('broccoli.js.dev', ['build.broccoli.tools'], function() {
|
||||
return broccoliBuild(makeBroccoliTree('dev'), path.join('js', 'dev'));
|
||||
});
|
||||
|
||||
gulp.task('broccoli.js.prod', ['build.broccoli.tools'], function() {
|
||||
return broccoliBuild(makeBroccoliTree('prod'), path.join('js', 'prod'));
|
||||
});
|
||||
|
||||
gulp.task('build.js.dev', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.dev', 'build/html.js.dev', 'build/copy.js.dev', 'build/multicopy.js.dev.es6'],
|
||||
'broccoli.js.dev',
|
||||
'build/checkCircularDependencies',
|
||||
'check-format',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.js.prod', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.prod', 'build/html.js.prod', 'build/copy.js.prod', 'build/multicopy.js.prod.es6'],
|
||||
done
|
||||
);
|
||||
});
|
||||
gulp.task('build.js.prod', ['broccoli.js.prod']);
|
||||
|
||||
gulp.task('broccoli.js.cjs', ['build.broccoli.tools'], function() {
|
||||
return broccoliBuild(makeBroccoliTree('cjs'), path.join('js', 'cjs'));
|
||||
});
|
||||
gulp.task('build.js.cjs', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'],
|
||||
'broccoli.js.cjs',
|
||||
['build/linknodemodules.js.cjs'],
|
||||
'build/transformCJSTests',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
var bundleConfig = {
|
||||
paths: {
|
||||
"*": "dist/js/prod/es6/*.es6",
|
||||
"rx/*": "node_modules/rx/*.js"
|
||||
},
|
||||
meta: {
|
||||
// auto-detection fails to detect properly here - https://github.com/systemjs/builder/issues/123
|
||||
'rx/dist/rx.all': {
|
||||
format: 'cjs'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// production build
|
||||
gulp.task('bundle.js.prod', ['build.js.prod'], function() {
|
||||
return bundler.bundle(
|
||||
bundleConfig,
|
||||
'angular2/angular2',
|
||||
'./dist/build/angular2.js',
|
||||
{
|
||||
sourceMaps: true
|
||||
});
|
||||
});
|
||||
|
||||
// minified production build
|
||||
// TODO: minify zone.js
|
||||
gulp.task('bundle.js.min', ['build.js.prod'], function() {
|
||||
return bundler.bundle(
|
||||
bundleConfig,
|
||||
'angular2/angular2',
|
||||
'./dist/build/angular2.min.js',
|
||||
{
|
||||
sourceMaps: true,
|
||||
minify: true
|
||||
});
|
||||
});
|
||||
|
||||
// development build
|
||||
gulp.task('bundle.js.dev', ['build.js.dev'], function() {
|
||||
var devBundleConfig = merge(true, bundleConfig);
|
||||
devBundleConfig.paths =
|
||||
merge(true, devBundleConfig.paths, {
|
||||
"*": "dist/js/dev/es6/*.es6"
|
||||
});
|
||||
return bundler.bundle(
|
||||
devBundleConfig,
|
||||
'angular2/angular2',
|
||||
'./dist/build/angular2.dev.js',
|
||||
{
|
||||
sourceMaps: true
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('bundle.js.prod.deps', ['bundle.js.prod'], function() {
|
||||
return bundler.modify(
|
||||
['node_modules/zone.js/zone.js', 'dist/build/angular2.js'], 'angular2.js')
|
||||
.pipe(gulp.dest('dist/bundle'));
|
||||
});
|
||||
|
||||
gulp.task('bundle.js.min.deps', ['bundle.js.min'], function() {
|
||||
return bundler.modify(
|
||||
['node_modules/zone.js/zone.js', 'dist/build/angular2.min.js'], 'angular2.min.js')
|
||||
.pipe(gulp.dest('dist/bundle'));
|
||||
});
|
||||
|
||||
gulp.task('bundle.js.dev.deps', ['bundle.js.dev'], function() {
|
||||
return bundler.modify(
|
||||
['node_modules/zone.js/zone.js', 'node_modules/zone.js/long-stack-trace-zone.js', 'dist/build/angular2.dev.js'],
|
||||
'angular2.dev.js')
|
||||
.pipe(insert.append('\nzone = zone.fork(Zone.longStackTraceZone);\n'))
|
||||
.pipe(gulp.dest('dist/bundle'));
|
||||
});
|
||||
|
||||
gulp.task('bundle.js.deps', ['bundle.js.prod.deps', 'bundle.js.dev.deps', 'bundle.js.min.deps']);
|
||||
|
||||
gulp.task('build.js', ['build.js.dev', 'build.js.prod', 'build.js.cjs']);
|
||||
|
||||
gulp.task('clean', ['build/clean.js', 'build/clean.dart', 'build/clean.docs']);
|
||||
|
@ -8,9 +8,12 @@ module.exports = function(config) {
|
||||
frameworks: ['dart-unittest'],
|
||||
|
||||
files: [
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-init.dart', included: true},
|
||||
// Unit test files needs to be included.
|
||||
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'modules/*/test/**/*_spec.js', included: true},
|
||||
{pattern: 'modules/*/test/**/*_spec.dart', included: true},
|
||||
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
|
||||
|
||||
// These files are not included, they are imported by the unit tests above.
|
||||
@ -56,25 +59,14 @@ module.exports = function(config) {
|
||||
},
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'tools/**/*.js': ['traceur']
|
||||
'modules/**/*.js': ['ts2dart'],
|
||||
'tools/**/*.js': ['ts2dart']
|
||||
},
|
||||
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
outputLanguage: 'dart',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
modules: 'register',
|
||||
memberVariables: true,
|
||||
types: true,
|
||||
// typeAssertions: true,
|
||||
// typeAssertionModule: 'assert',
|
||||
annotations: true
|
||||
},
|
||||
ts2dartPreprocessor: {
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace('.js', '.dart');
|
||||
return fileName.replace(/.js$/, '.dart');
|
||||
}
|
||||
},
|
||||
|
||||
@ -89,5 +81,5 @@ module.exports = function(config) {
|
||||
});
|
||||
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
config.plugins.push(require('./tools/transpiler/karma-ts2dart-preprocessor'));
|
||||
};
|
||||
|
@ -18,6 +18,8 @@ module.exports = function(config) {
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
'node_modules/systemjs/lib/extension-register.js',
|
||||
'node_modules/systemjs/lib/extension-cjs.js',
|
||||
'node_modules/rx/dist/rx.all.js',
|
||||
'node_modules/zone.js/zone.js',
|
||||
'node_modules/zone.js/long-stack-trace-zone.js',
|
||||
|
||||
|
3
modules/angular2/angular2.js
vendored
3
modules/angular2/angular2.js
vendored
@ -1,6 +1,3 @@
|
||||
/**
|
||||
* Define public API for Angular here.
|
||||
*/
|
||||
export * from './change_detection';
|
||||
export * from './core';
|
||||
export * from './annotations';
|
||||
|
9
modules/angular2/annotations.js
vendored
9
modules/angular2/annotations.js
vendored
@ -1,4 +1,11 @@
|
||||
/**
|
||||
* Define public API for Angular here.
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
*
|
||||
* Annotations provide the additional information that Angular requires in order to run your application. This module
|
||||
* contains [Component], [Decorator], and [View] annotations, as well as [Parent] and [Ancestor] annotations that are
|
||||
* used by Angular to resolve dependencies.
|
||||
*
|
||||
*/
|
||||
export * from './src/core/annotations/annotations';
|
||||
|
53
modules/angular2/change_detection.js
vendored
53
modules/angular2/change_detection.js
vendored
@ -1,37 +1,47 @@
|
||||
export {AST} from './src/change_detection/parser/ast';
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Description of the change_detection module
|
||||
*/
|
||||
|
||||
export {
|
||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
||||
} from './src/change_detection/parser/ast';
|
||||
export {Lexer} from './src/change_detection/parser/lexer';
|
||||
export {Parser} from './src/change_detection/parser/parser';
|
||||
export {Locals}
|
||||
from './src/change_detection/parser/locals';
|
||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
|
||||
from './src/change_detection/exceptions';
|
||||
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
|
||||
CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/change_detection/interfaces';
|
||||
export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector, BindingRecord}
|
||||
export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces';
|
||||
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
|
||||
export {DynamicProtoChangeDetector, JitProtoChangeDetector}
|
||||
from './src/change_detection/proto_change_detector';
|
||||
export {BindingRecord}
|
||||
from './src/change_detection/binding_record';
|
||||
export {DirectiveRecord}
|
||||
from './src/change_detection/directive_record';
|
||||
export {DynamicChangeDetector}
|
||||
from './src/change_detection/dynamic_change_detector';
|
||||
export {BindingPropagationConfig}
|
||||
from './src/change_detection/binding_propagation_config';
|
||||
export * from './src/change_detection/pipes/pipe_registry';
|
||||
export {uninitialized} from './src/change_detection/change_detection_util';
|
||||
export * from './src/change_detection/pipes/pipe';
|
||||
|
||||
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
||||
import {DynamicProtoChangeDetector, JitProtoChangeDetector}
|
||||
from './src/change_detection/proto_change_detector';
|
||||
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
||||
import {ArrayChangesFactory} from './src/change_detection/pipes/array_changes';
|
||||
import {IterableChangesFactory} from './src/change_detection/pipes/iterable_changes';
|
||||
import {KeyValueChangesFactory} from './src/change_detection/pipes/keyvalue_changes';
|
||||
import {NullPipeFactory} from './src/change_detection/pipes/null_pipe';
|
||||
|
||||
export class ChangeDetection {
|
||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||
// TODO: this should be abstract, once supported in AtScript
|
||||
return null;
|
||||
}
|
||||
}
|
||||
import {DEFAULT} from './src/change_detection/constants';
|
||||
import {ChangeDetection, ProtoChangeDetector} from './src/change_detection/interfaces';
|
||||
|
||||
export var defaultPipes = {
|
||||
"iterableDiff" : [
|
||||
new ArrayChangesFactory(),
|
||||
new IterableChangesFactory(),
|
||||
new NullPipeFactory()
|
||||
],
|
||||
"keyValDiff" : [
|
||||
@ -40,6 +50,10 @@ export var defaultPipes = {
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class DynamicChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
@ -48,11 +62,14 @@ export class DynamicChangeDetection extends ChangeDetection {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||
return new DynamicProtoChangeDetector(this.registry);
|
||||
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
||||
return new DynamicProtoChangeDetector(this.registry, changeControlStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class JitChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
@ -61,8 +78,8 @@ export class JitChangeDetection extends ChangeDetection {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||
return new JitProtoChangeDetector(this.registry);
|
||||
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
||||
return new JitProtoChangeDetector(this.registry, changeControlStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
|
19
modules/angular2/core.js
vendored
19
modules/angular2/core.js
vendored
@ -1,13 +1,24 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Define public API for Angular here.
|
||||
*/
|
||||
export * from './src/core/annotations/visibility';
|
||||
export * from './src/core/compiler/interfaces';
|
||||
export * from './src/core/annotations/template';
|
||||
export * from './src/core/annotations/view';
|
||||
export * from './src/core/application';
|
||||
export * from './src/core/application_tokens';
|
||||
export * from './src/core/annotations/di';
|
||||
|
||||
export * from './src/core/compiler/compiler';
|
||||
|
||||
export * from './src/core/compiler/template_loader';
|
||||
// TODO(tbosch): remove this once render migration is complete
|
||||
export * from 'angular2/src/render/dom/compiler/template_loader';
|
||||
export * from './src/core/compiler/dynamic_component_loader';
|
||||
export {ElementRef, DirectiveRef, ComponetRef} from './src/core/compiler/element_injector';
|
||||
export * from './src/core/compiler/view';
|
||||
export * from './src/core/compiler/view_container';
|
||||
export * from './src/core/compiler/binding_propagation_config';
|
||||
|
||||
export * from './src/core/dom/element';
|
||||
export * from './src/core/compiler/ng_element';
|
||||
|
||||
|
8
modules/angular2/di.js
vendored
8
modules/angular2/di.js
vendored
@ -1,6 +1,12 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This is a description
|
||||
*/
|
||||
|
||||
export {Inject, InjectPromise, InjectLazy, Injectable, Optional, DependencyAnnotation} from './src/di/annotations';
|
||||
export {Injector} from './src/di/injector';
|
||||
export {Binding, Dependency, bind} from './src/di/binding';
|
||||
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
|
||||
export {Key, KeyRegistry} from './src/di/key';
|
||||
export {KeyMetadataError, NoProviderError, ProviderError, AsyncBindingError, CyclicDependencyError,
|
||||
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions';
|
||||
|
10
modules/angular2/directives.js
vendored
10
modules/angular2/directives.js
vendored
@ -1,4 +1,12 @@
|
||||
export * from './src/directives/foreach';
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Describe the directives module here
|
||||
*/
|
||||
|
||||
export * from './src/directives/class';
|
||||
export * from './src/directives/for';
|
||||
export * from './src/directives/if';
|
||||
export * from './src/directives/non_bindable';
|
||||
export * from './src/directives/switch';
|
||||
|
@ -187,7 +187,7 @@ Example:
|
||||
<pre>
|
||||
```
|
||||
<ul>
|
||||
<li template="foreach: #item in items">
|
||||
<li template="for: #item of items">
|
||||
{{item}}
|
||||
</li>
|
||||
</ul>
|
||||
@ -201,8 +201,8 @@ Example:
|
||||
<pre>
|
||||
```
|
||||
<ul>
|
||||
<template def-foreach:"item"
|
||||
bind-foreach-in="items">
|
||||
<template def-for:"item"
|
||||
bind-for-in="items">
|
||||
<li>
|
||||
{{item}}
|
||||
</li>
|
||||
@ -221,8 +221,8 @@ Example:
|
||||
|
||||
<pre>
|
||||
```
|
||||
<template #foreach="item"
|
||||
[foreach-in]="items">
|
||||
<template #for="item"
|
||||
[for-in]="items">
|
||||
_some_content_to_repeat_
|
||||
</template>
|
||||
```
|
||||
@ -234,8 +234,8 @@ Example:
|
||||
Example:
|
||||
<pre>
|
||||
```
|
||||
<template def-foreach="item"
|
||||
bind-foreach-in="items">
|
||||
<template def-for="item"
|
||||
bind-for-in="items">
|
||||
_some_content_to_repeat_
|
||||
</template>
|
||||
```
|
||||
@ -408,22 +408,22 @@ NOTE: Only Viewport directives can be placed on the template element. (Decorator
|
||||
### Template Microsyntax
|
||||
|
||||
Often times it is necessary to encode a lot of different bindings into a template to control how the instantiation
|
||||
of the templates occurs. One such example is `foreach`.
|
||||
of the templates occurs. One such example is `for`.
|
||||
|
||||
```
|
||||
<form #foo=form>
|
||||
</form>
|
||||
<ul>
|
||||
<template foreach #person [in]="people" #i="index">
|
||||
<template for #person [in]="people" #i="index">
|
||||
<li>{{i}}. {{person}}<li>
|
||||
</template>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Where:
|
||||
* `foreach` triggers the foreach directive.
|
||||
* `[in]="people"` binds an iterable object to the `foreach` controller.
|
||||
* `#person` exports the implicit `foreach` item.
|
||||
* `for` triggers the for directive.
|
||||
* `[in]="people"` binds an iterable object to the `for` controller.
|
||||
* `#person` exports the implicit `for` item.
|
||||
* `#i=index` exports item index as `i`.
|
||||
|
||||
The above example is explicit but quite wordy. For this reason in most situations a short hand version of the
|
||||
@ -431,7 +431,7 @@ syntax is preferable.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="foreach; #person; in=people; #i=index;">{{i}}. {{person}}<li>
|
||||
<li template="for; #person; in=people; #i=index;">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -441,19 +441,28 @@ which allows us to further shorten the text.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="foreach #person in people #i=index">{{i}}. {{person}}<li>
|
||||
<li template="for #person of people #i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
We can also optionally use `var` instead of `#` and add `:` to `foreach` which creates the following recommended
|
||||
microsyntax for `foreach`.
|
||||
We can also optionally use `var` instead of `#` and add `:` to `for` which creates the following recommended
|
||||
microsyntax for `for`.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="foreach: var person in people; var i=index">{{i}}. {{person}}<li>
|
||||
<li template="for: var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Finally, we can move the `for` keyword to the left hand side and prefix it with `*` as so:
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li *for="var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
||||
The format is intentionally defined freely, so that developers of directives can build an expressive microsyntax for
|
||||
their directives. The following code describes a more formal definition.
|
||||
|
||||
|
@ -10,7 +10,7 @@ There are three different kinds of directives (described in more detail in later
|
||||
|
||||
1. *Decorators*: can be placed on any DOM element and can be combined with other directives.
|
||||
2. *Components*: Components have an encapsulated view and can configure injectors.
|
||||
3. *Viewport*: is responsible for adding or removing child views in a parent view. (i.e. foreach, if)
|
||||
3. *Viewport*: is responsible for adding or removing child views in a parent view. (i.e. for, if)
|
||||
|
||||
|
||||
|
||||
@ -67,10 +67,10 @@ Here is a trivial example of a tooltip decorator. The directive will log a toolt
|
||||
```
|
||||
@Decorator({
|
||||
selector: '[tooltip]', // CSS Selector which triggers the decorator
|
||||
bind: { // List which properties need to be bound
|
||||
properties: { // List which properties need to be bound
|
||||
text: 'tooltip' // - DOM element tooltip property should be
|
||||
}, // mapped to the directive text property.
|
||||
event: { // List which events need to be mapped.
|
||||
hostListeners: { // List which events need to be mapped.
|
||||
mouseover: 'show' // - Invoke the show() method every time
|
||||
} // the mouseover event is fired.
|
||||
})
|
||||
@ -112,13 +112,13 @@ Example of a component:
|
||||
```
|
||||
@Component({ | Component annotation
|
||||
selector: 'pane', | CSS selector on <pane> element
|
||||
bind: { | List which property need to be bound
|
||||
properties: { | List which property need to be bound
|
||||
'title': 'title', | - title mapped to component title
|
||||
'open': 'open' | - open attribute mapped to component's open property
|
||||
}, |
|
||||
}) |
|
||||
@Template({ | Template annotation
|
||||
url: 'pane.html' | - URL of template HTML
|
||||
@View({ | View annotation
|
||||
templateUrl: 'pane.html' | - URL of template HTML
|
||||
}) |
|
||||
class Pane { | Component controller class
|
||||
title:string; | - title property
|
||||
@ -140,7 +140,7 @@ class Pane { | Component controller class
|
||||
```
|
||||
<div class="outer">
|
||||
<h1>{{title}}</h1>
|
||||
<div class="inner" [hidden]="!visible">
|
||||
<div class="inner" [hidden]="!open">
|
||||
<content></content>
|
||||
</div>
|
||||
</div>
|
||||
@ -165,7 +165,7 @@ Example of usage:
|
||||
|
||||
## Viewport
|
||||
|
||||
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `foreach`.)
|
||||
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `for`.)
|
||||
|
||||
* Viewports can only be placed on `<template>` elements (or the short hand version which uses `<element template>` attribute.)
|
||||
* Only one viewport can be present per DOM template element.
|
||||
@ -179,7 +179,7 @@ Viewport is a directive which can control instantiation of child views which are
|
||||
```
|
||||
@Viewport({
|
||||
selector: '[if]',
|
||||
bind: {
|
||||
properties: {
|
||||
'condition': 'if'
|
||||
}
|
||||
})
|
||||
@ -220,7 +220,7 @@ To better understand the kinds of injections which are supported in Angular we h
|
||||
|
||||
### Injecting Services
|
||||
|
||||
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.
|
||||
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `injectables` and then letting the directive ask for the configured service.
|
||||
|
||||
This example illustrates how to inject `MyService` into `House` directive.
|
||||
|
||||
@ -231,10 +231,10 @@ class MyService {} | Assume a service which needs to be inject
|
||||
|
|
||||
@Component({ | Assume a top level application component which
|
||||
selector: 'my-app', | configures the services to be injected.
|
||||
services: [MyService] |
|
||||
injectables: [MyService] |
|
||||
}) |
|
||||
@Template({ | Assume we have a template that needs to be
|
||||
url: 'my_app.html', | configured with directives to be injected.
|
||||
@View({ | Assume we have a template that needs to be
|
||||
templateUrl: 'my_app.html', | configured with directives to be injected.
|
||||
directives: [House] |
|
||||
}) |
|
||||
class MyApp {} |
|
||||
@ -279,7 +279,7 @@ Here is an example of the kinds of injections which can be achieved:
|
||||
@Component({ |
|
||||
selector: 'my-app', |
|
||||
template: new TemplateConfig({ |
|
||||
url: 'my_app.html', |
|
||||
templateUrl: 'my_app.html', |
|
||||
directives: [Form, FieldSet, |
|
||||
Field, Primary] |
|
||||
}) |
|
||||
@ -322,8 +322,8 @@ Assume the following DOM structure for `my_app.html`:
|
||||
<fieldset> |
|
||||
<field primary></field> |
|
||||
<field></field> |
|
||||
</div> |
|
||||
</fieldset> |
|
||||
</fieldset> |
|
||||
</div> |
|
||||
</form> |
|
||||
```
|
||||
|
||||
|
@ -12,12 +12,12 @@ Views form a tree structure which mimics the DOM tree.
|
||||
nested in a tree like structure. The View tree is a simplified version of the DOM tree. A View can
|
||||
have a single DOM Element or large DOM structures. The key is that the DOM tree in the View can
|
||||
not undergo structural changes (only property changes).
|
||||
* Views represent a running instance of a DOM Template. This implies that while elements in a View
|
||||
* Views represent a running instance of a DOM View. This implies that while elements in a View
|
||||
can change properties, they can not change structurally. (Structural changes such as, adding or
|
||||
removing elements requires adding or removing child Views into ViewContainers).
|
||||
* View can have zero or more ViewPorts. A ViewPort is a marker in the DOM which allows
|
||||
the insertion of child Views.
|
||||
* Views are created from a ProtoView. A ProtoView is a compiled DOM Template which is efficient at
|
||||
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
|
||||
creating Views.
|
||||
* View contains a context object. The context represents the object instance against which all
|
||||
expressions are evaluated.
|
||||
@ -40,7 +40,7 @@ class Greeter {
|
||||
}
|
||||
```
|
||||
|
||||
And assume following HTML Template:
|
||||
And assume following HTML View:
|
||||
|
||||
```
|
||||
<div>
|
||||
@ -90,7 +90,7 @@ Note:
|
||||
An important part of an application is to be able to change the DOM structure to render data for the
|
||||
user. In Angular this is done by inserting child views into the ViewPort.
|
||||
|
||||
Let's start with a Template such as:
|
||||
Let's start with a View such as:
|
||||
|
||||
```
|
||||
<ul>
|
||||
@ -194,7 +194,7 @@ class Greeter {
|
||||
}
|
||||
```
|
||||
|
||||
And assume the following HTML Template:
|
||||
And assume the following HTML View:
|
||||
|
||||
```
|
||||
<div> | viewA(greeter)
|
||||
|
@ -43,7 +43,7 @@ class Car {
|
||||
}
|
||||
}
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Car).toClass(Car),
|
||||
bind(Engine).toClass(Engine)
|
||||
]);
|
||||
@ -86,7 +86,7 @@ To avoid bugs make sure the registered objects have side-effect-free constructor
|
||||
Injectors are hierarchical.
|
||||
|
||||
```
|
||||
var child = injector.createChild([
|
||||
var child = injector.resolveAndCreateChild([
|
||||
bind(Engine).toClass(TurboEngine)
|
||||
]);
|
||||
|
||||
@ -99,21 +99,21 @@ var car = child.get(Car); // uses the Car binding from the parent injector and E
|
||||
You can bind to a class, a value, or a factory. It is also possible to alias existing bindings.
|
||||
|
||||
```
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Car).toClass(Car),
|
||||
bind(Engine).toClass(Engine)
|
||||
]);
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
Car, // syntax sugar for bind(Car).toClass(Car)
|
||||
Engine
|
||||
]);
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Car).toValue(new Car(new Engine()))
|
||||
]);
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Car).toFactory((e) => new Car(e), [Engine]),
|
||||
bind(Engine).toFactory(() => new Engine())
|
||||
]);
|
||||
@ -122,7 +122,7 @@ var inj = new Injector([
|
||||
You can bind any token.
|
||||
|
||||
```
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Car).toFactory((e) => new Car(), ["engine!"]),
|
||||
bind("engine!").toClass(Engine)
|
||||
]);
|
||||
@ -131,7 +131,7 @@ var inj = new Injector([
|
||||
If you want to alias an existing binding, you can do so using `toAlias`:
|
||||
|
||||
```
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Engine).toClass(Engine),
|
||||
bind("engine!").toAlias(Engine)
|
||||
]);
|
||||
@ -152,7 +152,7 @@ The `someFactory` function does not have to know that it creates an object for `
|
||||
Injector can create binding on the fly if we enable default bindings.
|
||||
|
||||
```
|
||||
var inj = new Injector([], {defaultBindings: true});
|
||||
var inj = Injector.resolveAndCreate([], {defaultBindings: true});
|
||||
var car = inj.get(Car); //this works as if `bind(Car).toClass(Car)` and `bind(Engine).toClass(Engine)` were present.
|
||||
```
|
||||
|
||||
@ -226,7 +226,7 @@ class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
|
||||
UserController
|
||||
])
|
||||
@ -252,7 +252,7 @@ class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
|
||||
UserController
|
||||
])
|
||||
@ -276,7 +276,7 @@ class UserController {
|
||||
constructor(ul:UserList){}
|
||||
}
|
||||
|
||||
var inj = new Injector([UserList, UserController]);
|
||||
var inj = Injector.resolveAndCreate([UserList, UserController]);
|
||||
var ctrl:UserController = inj.get(UserController);
|
||||
```
|
||||
|
||||
@ -290,7 +290,7 @@ class UserController {
|
||||
constructor(@InjectPromise(UserList) ul){}
|
||||
}
|
||||
|
||||
var inj = new Injector([UserList, UserController]);
|
||||
var inj = Injector.resolveAndCreate([UserList, UserController]);
|
||||
var ctrl:UserController = inj.get(UserController);
|
||||
// UserController responsible for dealing with asynchrony.
|
||||
expect(ctrl.ul).toBePromise();
|
||||
@ -306,7 +306,7 @@ class UserController {
|
||||
constructor(ul:UserList){}
|
||||
}
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
|
||||
UserController
|
||||
]);
|
||||
@ -331,7 +331,7 @@ class UserController {
|
||||
constructor(@InjectPromise(UserList) ul){}
|
||||
}
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
|
||||
UserController
|
||||
]);
|
||||
@ -369,14 +369,14 @@ If we need a transient dependency, something that we want a new instance of ever
|
||||
We can create a child injector:
|
||||
|
||||
```
|
||||
var child = inj.createChild([MyClass]);
|
||||
var child = inj.resolveAndCreateChild([MyClass]);
|
||||
child.get(MyClass);
|
||||
```
|
||||
|
||||
Or we can register a factory function:
|
||||
|
||||
```
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind('MyClassFactory').toFactory(dep => () => new MyClass(dep), [SomeDependency])
|
||||
]);
|
||||
|
||||
@ -393,7 +393,7 @@ expect(instance1).not.toBe(instance2);
|
||||
Most of the time we do not have to deal with keys.
|
||||
|
||||
```
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(Engine).toFactory(() => new TurboEngine()) //the passed in token Engine gets mapped to a key
|
||||
]);
|
||||
var engine = inj.get(Engine); //the passed in token Engine gets mapped to a key
|
||||
@ -404,7 +404,7 @@ Now, the same example, but with keys
|
||||
```
|
||||
var ENGINE_KEY = Key.get(Engine);
|
||||
|
||||
var inj = new Injector([
|
||||
var inj = Injector.resolveAndCreate([
|
||||
bind(ENGINE_KEY).toFactory(() => new TurboEngine()) // no mapping
|
||||
]);
|
||||
var engine = inj.get(ENGINE_KEY); // no mapping
|
||||
|
12
modules/angular2/forms.js
vendored
12
modules/angular2/forms.js
vendored
@ -1,5 +1,15 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This module is used for handling complex input, by defining and building a [FormObject] that consists of [Control]
|
||||
* objects, and mapping them onto the DOM. [Control] objects can then be used to read information from the form DOM
|
||||
* elements.
|
||||
*
|
||||
*/
|
||||
|
||||
export * from './src/forms/model';
|
||||
export * from './src/forms/directives';
|
||||
export * from './src/forms/validators';
|
||||
export * from './src/forms/validator_directives';
|
||||
export * from './src/forms/form_builder';
|
||||
export * from './src/forms/form_builder';
|
||||
|
2
modules/angular2/globals.dart
Normal file
2
modules/angular2/globals.dart
Normal file
@ -0,0 +1,2 @@
|
||||
// Globals are provided by lang.dart in Dart.
|
||||
// This file exists to prevent global.ts from being transpiled.
|
24
modules/angular2/globals.ts
Normal file
24
modules/angular2/globals.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* This file contains declarations of global symbols we reference in our code
|
||||
*/
|
||||
|
||||
declare var assert: any;
|
||||
declare var global: Window;
|
||||
type int = number;
|
||||
|
||||
interface List<T> extends Array<T> {
|
||||
}
|
||||
|
||||
interface Window {
|
||||
Object: typeof Object;
|
||||
Array: typeof Array;
|
||||
Map: typeof Map;
|
||||
Set: typeof Set;
|
||||
Date: typeof Date;
|
||||
RegExp: typeof RegExp;
|
||||
JSON: typeof JSON;
|
||||
Math: typeof Math;
|
||||
assert: typeof assert;
|
||||
NaN: typeof NaN;
|
||||
gc(): void;
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
"dependencies": {
|
||||
"traceur": "<%= packageJson.dependencies.traceur %>",
|
||||
"rtts_assert": "<%= packageJson.version %>",
|
||||
"rx": "<%= packageJson.dependencies['rx'] %>",
|
||||
"zone.js": "<%= packageJson.dependencies['zone.js'] %>"
|
||||
},
|
||||
"devDependencies": <%= JSON.stringify(packageJson.devDependencies) %>
|
||||
|
6
modules/angular2/pipes.js
vendored
Normal file
6
modules/angular2/pipes.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This module provides advanced support for extending change detection.
|
||||
*/
|
@ -9,11 +9,13 @@ homepage: <%= packageJson.homepage %>
|
||||
environment:
|
||||
sdk: '>=1.9.0-dev.8.0'
|
||||
dependencies:
|
||||
analyzer: '^0.22.4'
|
||||
analyzer: '>=0.22.4 <0.25.0'
|
||||
barback: '^0.15.2+2'
|
||||
code_transformers: '^0.2.5'
|
||||
dart_style: '^0.1.3'
|
||||
html5lib: '^0.12.0'
|
||||
html: '^0.12.0'
|
||||
stack_trace: '^1.1.1'
|
||||
transformers:
|
||||
- angular2
|
||||
dev_dependencies:
|
||||
guinness: "^0.1.17"
|
||||
|
22
modules/angular2/src/analysis/analyzer_plugin/plugin.dart
Normal file
22
modules/angular2/src/analysis/analyzer_plugin/plugin.dart
Normal file
@ -0,0 +1,22 @@
|
||||
library angular2.src.analysis.analyzer_plugin;
|
||||
|
||||
import 'package:analyzer/plugin/plugin.dart';
|
||||
|
||||
/// Contribute a plugin to the dart analyzer for analysis of
|
||||
/// Angular 2 dart code.
|
||||
class AngularAnalyzerPlugin implements Plugin {
|
||||
|
||||
/// the unique indetifier for this plugin
|
||||
static const String UNIQUE_IDENTIFIER = 'angular2.analysis';
|
||||
|
||||
@override
|
||||
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
|
||||
|
||||
@override
|
||||
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {}
|
||||
|
||||
@override
|
||||
void registerExtensions(RegisterExtension registerExtension) {
|
||||
// TODO(keerti): register extension for analysis
|
||||
}
|
||||
}
|
23
modules/angular2/src/analysis/server_plugin/plugin.dart
Normal file
23
modules/angular2/src/analysis/server_plugin/plugin.dart
Normal file
@ -0,0 +1,23 @@
|
||||
library angular2.src.analysis.server_plugin;
|
||||
|
||||
import 'package:analyzer/plugin/plugin.dart';
|
||||
|
||||
/// Contribute a plugin for services such as completions, indexing and refactoring
|
||||
/// of Angular 2 dart code.
|
||||
class AngularServerPlugin implements Plugin {
|
||||
|
||||
/// the unique indetifier for this plugin
|
||||
static const String UNIQUE_IDENTIFIER = 'angular2.analysis.services';
|
||||
|
||||
@override
|
||||
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
|
||||
|
||||
@override
|
||||
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {}
|
||||
|
||||
@override
|
||||
void registerExtensions(RegisterExtension registerExtension) {
|
||||
// TODO: register extension for code completions, indexing etc
|
||||
|
||||
}
|
||||
}
|
@ -1,25 +1,36 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
|
||||
import {BindingPropagationConfig} from './binding_propagation_config';
|
||||
import {ChangeDetector} from './interfaces';
|
||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
||||
|
||||
export class AbstractChangeDetector extends ChangeDetector {
|
||||
children:List;
|
||||
lightDomChildren:List;
|
||||
shadowDomChildren:List;
|
||||
parent:ChangeDetector;
|
||||
mode:string;
|
||||
bindingPropagationConfig:BindingPropagationConfig;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.children = [];
|
||||
this.mode = CHECK_ALWAYS;
|
||||
this.lightDomChildren = [];
|
||||
this.shadowDomChildren = [];
|
||||
this.bindingPropagationConfig = new BindingPropagationConfig(this);
|
||||
this.mode = null;
|
||||
}
|
||||
|
||||
addChild(cd:ChangeDetector) {
|
||||
ListWrapper.push(this.children, cd);
|
||||
ListWrapper.push(this.lightDomChildren, cd);
|
||||
cd.parent = this;
|
||||
}
|
||||
|
||||
removeChild(cd:ChangeDetector) {
|
||||
ListWrapper.remove(this.children, cd);
|
||||
ListWrapper.remove(this.lightDomChildren, cd);
|
||||
}
|
||||
|
||||
addShadowDomChild(cd:ChangeDetector) {
|
||||
ListWrapper.push(this.shadowDomChildren, cd);
|
||||
cd.parent = this;
|
||||
}
|
||||
|
||||
remove() {
|
||||
@ -38,17 +49,30 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||
if (this.mode === DETACHED || this.mode === CHECKED) return;
|
||||
|
||||
this.detectChangesInRecords(throwOnChange);
|
||||
this._detectChangesInChildren(throwOnChange);
|
||||
|
||||
this._detectChangesInLightDomChildren(throwOnChange);
|
||||
|
||||
this.callOnAllChangesDone();
|
||||
|
||||
this._detectChangesInShadowDomChildren(throwOnChange);
|
||||
|
||||
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
||||
}
|
||||
|
||||
detectChangesInRecords(throwOnChange:boolean){}
|
||||
callOnAllChangesDone(){}
|
||||
|
||||
_detectChangesInChildren(throwOnChange:boolean) {
|
||||
var children = this.children;
|
||||
for(var i = 0; i < children.length; ++i) {
|
||||
children[i]._detectChanges(throwOnChange);
|
||||
_detectChangesInLightDomChildren(throwOnChange:boolean) {
|
||||
var c = this.lightDomChildren;
|
||||
for(var i = 0; i < c.length; ++i) {
|
||||
c[i]._detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
_detectChangesInShadowDomChildren(throwOnChange:boolean) {
|
||||
var c = this.shadowDomChildren;
|
||||
for(var i = 0; i < c.length; ++i) {
|
||||
c[i]._detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'angular2/change_detection';
|
||||
import {ChangeDetector} from './interfaces';
|
||||
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
|
||||
|
||||
/**
|
||||
* @publicModule angular2/angular2
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class BindingPropagationConfig {
|
||||
_cd:ChangeDetector;
|
58
modules/angular2/src/change_detection/binding_record.js
vendored
Normal file
58
modules/angular2/src/change_detection/binding_record.js
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {SetterFn} from 'angular2/src/reflection/types';
|
||||
import {AST} from './parser/ast';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
|
||||
const DIRECTIVE="directive";
|
||||
const ELEMENT="element";
|
||||
const TEXT_NODE="textNode";
|
||||
|
||||
export class BindingRecord {
|
||||
mode:string;
|
||||
ast:AST;
|
||||
|
||||
elementIndex:number;
|
||||
propertyName:string;
|
||||
setter:SetterFn;
|
||||
|
||||
directiveRecord:DirectiveRecord;
|
||||
|
||||
constructor(mode:string, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
this.mode = mode;
|
||||
this.ast = ast;
|
||||
|
||||
this.elementIndex = elementIndex;
|
||||
this.propertyName = propertyName;
|
||||
this.setter = setter;
|
||||
|
||||
this.directiveRecord = directiveRecord;
|
||||
}
|
||||
|
||||
callOnChange() {
|
||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
|
||||
}
|
||||
|
||||
isDirective() {
|
||||
return this.mode === DIRECTIVE;
|
||||
}
|
||||
|
||||
isElement() {
|
||||
return this.mode === ELEMENT;
|
||||
}
|
||||
|
||||
isTextNode() {
|
||||
return this.mode === TEXT_NODE;
|
||||
}
|
||||
|
||||
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
return new BindingRecord(DIRECTIVE, ast, 0, propertyName, setter, directiveRecord);
|
||||
}
|
||||
|
||||
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
|
||||
return new BindingRecord(ELEMENT, ast, elementIndex, propertyName, null, null);
|
||||
}
|
||||
|
||||
static createForTextNode(ast:AST, elementIndex:number) {
|
||||
return new BindingRecord(TEXT_NODE, ast, elementIndex, null, null, null);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
library change_detectoin.change_detection_jit_generator;
|
||||
|
||||
class ChangeDetectorJITGenerator {
|
||||
ChangeDetectorJITGenerator(typeName, records) {
|
||||
ChangeDetectorJITGenerator(typeName, strategy, records, directiveMementos) {
|
||||
}
|
||||
|
||||
generate() {
|
||||
throw "Not supported in Dart";
|
||||
throw "Jit Change Detection is not supported in Dart";
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||
|
||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
|
||||
import {
|
||||
ProtoRecord,
|
||||
@ -15,6 +16,7 @@ import {
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_BINDING_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
|
||||
@ -23,103 +25,47 @@ import {
|
||||
* that "emulates" what the developer would write by hand to implement the same
|
||||
* kind of behaviour.
|
||||
*
|
||||
* For example: An expression `address.city` will result in the following class:
|
||||
*
|
||||
* var ChangeDetector0 = function ChangeDetector0(dispatcher, protos) {
|
||||
* AbstractChangeDetector.call(this);
|
||||
* this.dispatcher = dispatcher;
|
||||
* this.protos = protos;
|
||||
*
|
||||
* this.context = ChangeDetectionUtil.unitialized();
|
||||
* this.address0 = ChangeDetectionUtil.unitialized();
|
||||
* this.city1 = ChangeDetectionUtil.unitialized();
|
||||
* }
|
||||
* ChangeDetector0.prototype = Object.create(AbstractChangeDetector.prototype);
|
||||
*
|
||||
* ChangeDetector0.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||
* var address0;
|
||||
* var city1;
|
||||
* var change;
|
||||
* var changes = null;
|
||||
* var temp;
|
||||
* var context = this.context;
|
||||
*
|
||||
* address0 = context.address;
|
||||
* if (address0 !== this.address0) {
|
||||
* this.address0 = address0;
|
||||
* }
|
||||
*
|
||||
* city1 = address0.city;
|
||||
* if (city1 !== this.city1) {
|
||||
* changes = ChangeDetectionUtil.addRecord(changes,
|
||||
* ChangeDetectionUtil.simpleChangeRecord(this.protos[1].bindingMemento, this.city1, city1));
|
||||
* this.city1 = city1;
|
||||
* }
|
||||
*
|
||||
* if (changes.length > 0) {
|
||||
* if(throwOnChange) ChangeDetectionUtil.throwOnChange(this.protos[1], changes[0]);
|
||||
* this.dispatcher.onRecordChange('address.city', changes);
|
||||
* changes = null;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* ChangeDetector0.prototype.hydrate = function(context, locals) {
|
||||
* this.context = context;
|
||||
* this.locals = locals;
|
||||
* }
|
||||
*
|
||||
* ChangeDetector0.prototype.dehydrate = function(context) {
|
||||
* this.context = ChangeDetectionUtil.unitialized();
|
||||
* this.address0 = ChangeDetectionUtil.unitialized();
|
||||
* this.city1 = ChangeDetectionUtil.unitialized();
|
||||
* this.locals = null;
|
||||
* }
|
||||
*
|
||||
* ChangeDetector0.prototype.hydrated = function() {
|
||||
* return this.context !== ChangeDetectionUtil.unitialized();
|
||||
* }
|
||||
*
|
||||
* return ChangeDetector0;
|
||||
*
|
||||
*
|
||||
* The only thing the generated class depends on is the super class AbstractChangeDetector.
|
||||
*
|
||||
* The implementation comprises two parts:
|
||||
* * ChangeDetectorJITGenerator has the logic of how everything fits together.
|
||||
* * template functions (e.g., constructorTemplate) define what code is generated.
|
||||
*/
|
||||
|
||||
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
var UTIL = "ChangeDetectionUtil";
|
||||
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||
var PROTOS_ACCESSOR = "this.protos";
|
||||
var DIRECTIVES_ACCESSOR = "this.directiveRecords";
|
||||
var CONTEXT_ACCESSOR = "this.context";
|
||||
var CHANGE_LOCAL = "change";
|
||||
var CHANGES_LOCAL = "changes";
|
||||
var LOCALS_ACCESSOR = "this.locals";
|
||||
var MODE_ACCESSOR = "this.mode";
|
||||
var TEMP_LOCAL = "temp";
|
||||
var CURRENT_PROTO = "currentProto";
|
||||
|
||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
||||
function typeTemplate(type:string, cons:string, detectChanges:string,
|
||||
notifyOnAllChangesDone:string, setContext:string):string {
|
||||
return `
|
||||
${cons}
|
||||
${detectChanges}
|
||||
${notifyOnAllChangesDone}
|
||||
${setContext};
|
||||
|
||||
return function(dispatcher, pipeRegistry) {
|
||||
return new ${type}(dispatcher, pipeRegistry, protos);
|
||||
return new ${type}(dispatcher, pipeRegistry, protos, directiveRecords);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||
return `
|
||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
|
||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
|
||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||
${DISPATCHER_ACCESSOR} = dispatcher;
|
||||
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
||||
${PROTOS_ACCESSOR} = protos;
|
||||
${DIRECTIVES_ACCESSOR} = directiveRecords;
|
||||
${LOCALS_ACCESSOR} = null;
|
||||
${fieldsDefinitions}
|
||||
}
|
||||
|
||||
@ -131,15 +77,23 @@ function pipeOnDestroyTemplate(pipeNames:List) {
|
||||
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
|
||||
}
|
||||
|
||||
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
|
||||
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
|
||||
directiveFieldNames:List<String>):string {
|
||||
var directiveInit = "";
|
||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
directiveInit += `${directiveFieldNames[i]} = directives.directive(this.directiveRecords[${i}]);\n`;
|
||||
}
|
||||
|
||||
return `
|
||||
${type}.prototype.hydrate = function(context, locals) {
|
||||
${type}.prototype.hydrate = function(context, locals, directives) {
|
||||
${MODE_ACCESSOR} = "${mode}";
|
||||
${CONTEXT_ACCESSOR} = context;
|
||||
${LOCALS_ACCESSOR} = locals;
|
||||
${directiveInit}
|
||||
}
|
||||
${type}.prototype.dehydrate = function() {
|
||||
${pipeOnDestroy}
|
||||
${fieldsDefinitions}
|
||||
${fieldDefinitions}
|
||||
${LOCALS_ACCESSOR} = null;
|
||||
}
|
||||
${type}.prototype.hydrated = function() {
|
||||
@ -156,13 +110,26 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||
`;
|
||||
}
|
||||
|
||||
function callOnAllChangesDoneTemplate(type:string, body:string):string {
|
||||
return `
|
||||
${type}.prototype.callOnAllChangesDone = function() {
|
||||
${body}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
||||
function onAllChangesDoneTemplate(directive:string):string {
|
||||
return `${directive}.onAllChangesDone();`;
|
||||
}
|
||||
|
||||
|
||||
function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
||||
return `
|
||||
${localDefinitions}
|
||||
${changeDefinitions}
|
||||
var ${TEMP_LOCAL};
|
||||
var ${CHANGE_LOCAL};
|
||||
var ${CURRENT_PROTO};
|
||||
var ${CHANGES_LOCAL} = null;
|
||||
|
||||
context = ${CONTEXT_ACCESSOR};
|
||||
@ -170,45 +137,41 @@ ${records}
|
||||
`;
|
||||
}
|
||||
|
||||
function notifyTemplate(index:number):string{
|
||||
return `
|
||||
if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
|
||||
if(throwOnChange) ${UTIL}.throwOnChange(${PROTOS_ACCESSOR}[${index}], ${CHANGES_LOCAL}[0]);
|
||||
${DISPATCHER_ACCESSOR}.onRecordChange(${PROTOS_ACCESSOR}[${index}].directiveMemento, ${CHANGES_LOCAL});
|
||||
${CHANGES_LOCAL} = null;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function pipeCheckTemplate(context:string, pipe:string, pipeType:string,
|
||||
value:string, change:string, addRecord:string, notify:string):string{
|
||||
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
|
||||
oldValue:string, newValue:string, change:string, update:string,
|
||||
addToChanges, lastInDirective:string):string{
|
||||
return `
|
||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||
if (${pipe} === ${UTIL}.unitialized()) {
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
|
||||
} else if (!${pipe}.supports(${context})) {
|
||||
${pipe}.onDestroy();
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
|
||||
}
|
||||
|
||||
${CHANGE_LOCAL} = ${pipe}.transform(${context});
|
||||
if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) {
|
||||
${value} = ${CHANGE_LOCAL};
|
||||
${newValue} = ${pipe}.transform(${context});
|
||||
if (! ${UTIL}.noChangeMarker(${newValue})) {
|
||||
${change} = true;
|
||||
${addRecord}
|
||||
${update}
|
||||
${addToChanges}
|
||||
${oldValue} = ${newValue};
|
||||
}
|
||||
${notify}
|
||||
${lastInDirective}
|
||||
`;
|
||||
}
|
||||
|
||||
function referenceCheckTemplate(assignment, newValue, oldValue, change, addRecord, notify) {
|
||||
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
|
||||
update:string, addToChanges:string, lastInDirective:string):string {
|
||||
return `
|
||||
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
|
||||
${assignment}
|
||||
if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
|
||||
${change} = true;
|
||||
${addRecord}
|
||||
${update}
|
||||
${addToChanges}
|
||||
${oldValue} = ${newValue};
|
||||
}
|
||||
${notify}
|
||||
${lastInDirective}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -237,23 +200,49 @@ if (${cond}) {
|
||||
`;
|
||||
}
|
||||
|
||||
function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newValue:string) {
|
||||
return `${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
|
||||
${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`;
|
||||
function addToChangesTemplate(oldValue:string, newValue:string):string {
|
||||
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
|
||||
}
|
||||
|
||||
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
|
||||
return `
|
||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||
${directiveProperty} = ${newValue};
|
||||
`;
|
||||
}
|
||||
|
||||
function updateElementTemplate(oldValue:string, newValue:string):string {
|
||||
return `
|
||||
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
|
||||
`;
|
||||
}
|
||||
|
||||
function notifyOnChangesTemplate(directive:string):string{
|
||||
return `
|
||||
if(${CHANGES_LOCAL}) {
|
||||
${directive}.onChange(${CHANGES_LOCAL});
|
||||
${CHANGES_LOCAL} = null;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
export class ChangeDetectorJITGenerator {
|
||||
typeName:string;
|
||||
records:List<ProtoRecord>;
|
||||
localNames:List<String>;
|
||||
changeNames:List<String>;
|
||||
fieldNames:List<String>;
|
||||
pipeNames:List<String>;
|
||||
directiveRecords:List;
|
||||
localNames:List<string>;
|
||||
changeNames:List<string>;
|
||||
fieldNames:List<string>;
|
||||
pipeNames:List<string>;
|
||||
changeDetectionStrategy:stirng;
|
||||
|
||||
constructor(typeName:string, records:List<ProtoRecord>) {
|
||||
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveRecords:List) {
|
||||
this.typeName = typeName;
|
||||
this.changeDetectionStrategy = changeDetectionStrategy;
|
||||
this.records = records;
|
||||
this.directiveRecords = directiveRecords;
|
||||
|
||||
this.localNames = this.getLocalNames(records);
|
||||
this.changeNames = this.getChangeNames(this.localNames);
|
||||
@ -261,7 +250,7 @@ export class ChangeDetectorJITGenerator {
|
||||
this.pipeNames = this.getPipeNames(this.localNames);
|
||||
}
|
||||
|
||||
getLocalNames(records:List<ProtoRecord>):List<String> {
|
||||
getLocalNames(records:List<ProtoRecord>):List<string> {
|
||||
var index = 0;
|
||||
var names = records.map((r) => {
|
||||
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
||||
@ -270,21 +259,23 @@ export class ChangeDetectorJITGenerator {
|
||||
return ["context"].concat(names);
|
||||
}
|
||||
|
||||
getChangeNames(localNames:List<String>):List<String> {
|
||||
getChangeNames(localNames:List<string>):List<string> {
|
||||
return localNames.map((n) => `change_${n}`);
|
||||
}
|
||||
|
||||
getFieldNames(localNames:List<String>):List<String> {
|
||||
getFieldNames(localNames:List<string>):List<string> {
|
||||
return localNames.map((n) => `this.${n}`);
|
||||
}
|
||||
|
||||
getPipeNames(localNames:List<String>):List<String> {
|
||||
getPipeNames(localNames:List<string>):List<string> {
|
||||
return localNames.map((n) => `this.${n}_pipe`);
|
||||
}
|
||||
|
||||
generate():Function {
|
||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records);
|
||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
||||
this.genCallOnAllChangesDone(), this.genHydrate());
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
|
||||
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
|
||||
}
|
||||
|
||||
genConstructor():string {
|
||||
@ -292,21 +283,31 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
genHydrate():string {
|
||||
return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
|
||||
pipeOnDestroyTemplate(this.getnonNullPipeNames()));
|
||||
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
|
||||
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
|
||||
pipeOnDestroyTemplate(this.getNonNullPipeNames()), this.getDirectiveFieldNames());
|
||||
}
|
||||
|
||||
getDirectiveFieldNames():List<string> {
|
||||
return this.directiveRecords.map((d) => this.getDirective(d));
|
||||
}
|
||||
|
||||
getDirective(d:DirectiveRecord) {
|
||||
return `this.directive_${d.name}`;
|
||||
}
|
||||
|
||||
genFieldDefinitions() {
|
||||
var fields = [];
|
||||
fields = fields.concat(this.fieldNames);
|
||||
fields = fields.concat(this.getnonNullPipeNames());
|
||||
fields = fields.concat(this.getNonNullPipeNames());
|
||||
fields = fields.concat(this.getDirectiveFieldNames());
|
||||
return fieldDefinitionsTemplate(fields);
|
||||
}
|
||||
|
||||
getnonNullPipeNames():List<String> {
|
||||
getNonNullPipeNames():List<string> {
|
||||
var pipes = [];
|
||||
this.records.forEach((r) => {
|
||||
if (r.mode === RECORD_TYPE_PIPE) {
|
||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
pipes.push(this.pipeNames[r.selfIndex]);
|
||||
}
|
||||
});
|
||||
@ -314,13 +315,28 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
genDetectChanges():string {
|
||||
var body = this.genBody();
|
||||
var body = this.genDetectChangesBody();
|
||||
return detectChangesTemplate(this.typeName, body);
|
||||
}
|
||||
|
||||
genBody():string {
|
||||
genCallOnAllChangesDone():string {
|
||||
var notifications = [];
|
||||
var dirs = this.directiveRecords;
|
||||
|
||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||
var dir = dirs[i];
|
||||
if (dir.callOnAllChangesDone) {
|
||||
var directive = `this.directive_${dir.name}`;
|
||||
notifications.push(onAllChangesDoneTemplate(directive));
|
||||
}
|
||||
}
|
||||
|
||||
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
||||
}
|
||||
|
||||
genDetectChangesBody():string {
|
||||
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
||||
return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
||||
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
||||
}
|
||||
|
||||
genLocalDefinitions():string {
|
||||
@ -332,7 +348,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
genRecord(r:ProtoRecord):string {
|
||||
if (r.mode === RECORD_TYPE_PIPE) {
|
||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
return this.genPipeCheck (r);
|
||||
} else {
|
||||
return this.genReferenceCheck(r);
|
||||
@ -341,26 +357,33 @@ export class ChangeDetectorJITGenerator {
|
||||
|
||||
genPipeCheck(r:ProtoRecord):string {
|
||||
var context = this.localNames[r.contextIndex];
|
||||
var pipe = this.pipeNames[r.selfIndex];
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var change = this.changeNames[r.selfIndex];
|
||||
|
||||
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
|
||||
var notify = this.genNotify(r);
|
||||
var pipe = this.pipeNames[r.selfIndex];
|
||||
var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null";
|
||||
|
||||
return pipeCheckTemplate(context, pipe, r.name, newValue, change, addRecord, notify);
|
||||
var update = this.genUpdateDirectiveOrElement(r);
|
||||
var addToChanges = this.genAddToChanges(r);
|
||||
var lastInDirective = this.genNotifyOnChanges(r);
|
||||
|
||||
return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change,
|
||||
update, addToChanges, lastInDirective);
|
||||
}
|
||||
|
||||
genReferenceCheck(r:ProtoRecord):string {
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var change = this.changeNames[r.selfIndex];
|
||||
var assignment = this.genUpdateCurrentValue(r);
|
||||
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
|
||||
var notify = this.genNotify(r);
|
||||
|
||||
var check = referenceCheckTemplate(assignment, newValue, oldValue, change, r.lastInBinding ? addRecord : '', notify);;
|
||||
var update = this.genUpdateDirectiveOrElement(r);
|
||||
var addToChanges = this.genAddToChanges(r);
|
||||
var lastInDirective = this.genNotifyOnChanges(r);
|
||||
|
||||
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
|
||||
update, addToChanges, lastInDirective);
|
||||
if (r.isPureFunction()) {
|
||||
return this.ifChangedGuard(r, check);
|
||||
} else {
|
||||
@ -427,8 +450,34 @@ export class ChangeDetectorJITGenerator {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
genNotify(r):string{
|
||||
return r.lastInDirective ? notifyTemplate(r.selfIndex - 1) : '';
|
||||
genUpdateDirectiveOrElement(r:ProtoRecord):string {
|
||||
if (! r.lastInBinding) return "";
|
||||
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.isDirective()) {
|
||||
var directiveProperty = `${this.getDirective(br.directiveRecord)}.${br.propertyName}`;
|
||||
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
||||
} else {
|
||||
return updateElementTemplate(oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
genAddToChanges(r:ProtoRecord):string {
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var oldValue = this.fieldNames[r.selfIndex];
|
||||
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
|
||||
}
|
||||
|
||||
genNotifyOnChanges(r:ProtoRecord):string{
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.callOnChange()) {
|
||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
genArgs(r:ProtoRecord):string {
|
||||
|
@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||
import {ProtoRecord} from './proto_record';
|
||||
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
||||
import {NO_CHANGE} from './pipes/pipe';
|
||||
import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
|
||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
|
||||
|
||||
export var uninitialized = new Object();
|
||||
|
||||
@ -39,31 +39,7 @@ var _simpleChanges = [
|
||||
new SimpleChange(null, null),
|
||||
new SimpleChange(null, null),
|
||||
new SimpleChange(null, null)
|
||||
]
|
||||
|
||||
var _changeRecordsIndex = 0;
|
||||
var _changeRecords = [
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null),
|
||||
new ChangeRecord(null, null)
|
||||
]
|
||||
];
|
||||
|
||||
function _simpleChange(previousValue, currentValue) {
|
||||
var index = _simpleChangesIndex++ % 20;
|
||||
@ -73,16 +49,6 @@ function _simpleChange(previousValue, currentValue) {
|
||||
return s;
|
||||
}
|
||||
|
||||
function _changeRecord(bindingMemento, change) {
|
||||
var index = _changeRecordsIndex++ % 20;
|
||||
var s = _changeRecords[index];
|
||||
s.bindingMemento = bindingMemento;
|
||||
s.change = change;
|
||||
return s;
|
||||
}
|
||||
|
||||
var _singleElementList = [null];
|
||||
|
||||
export class ChangeDetectionUtil {
|
||||
static unitialized() {
|
||||
return uninitialized;
|
||||
@ -151,29 +117,19 @@ export class ChangeDetectionUtil {
|
||||
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
|
||||
}
|
||||
|
||||
static changeDetectionMode(strategy:string) {
|
||||
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
|
||||
}
|
||||
|
||||
static simpleChange(previousValue:any, currentValue:any):SimpleChange {
|
||||
return _simpleChange(previousValue, currentValue);
|
||||
}
|
||||
|
||||
static changeRecord(memento:any, change:any):ChangeRecord {
|
||||
return _changeRecord(memento, change);
|
||||
}
|
||||
|
||||
static simpleChangeRecord(memento:any, previousValue:any, currentValue:any):ChangeRecord {
|
||||
return _changeRecord(memento, _simpleChange(previousValue, currentValue));
|
||||
}
|
||||
|
||||
static addRecord(updatedRecords:List, changeRecord:ChangeRecord):List {
|
||||
if (isBlank(updatedRecords)) {
|
||||
updatedRecords = _singleElementList;
|
||||
updatedRecords[0] = changeRecord;
|
||||
|
||||
} else if (updatedRecords === _singleElementList) {
|
||||
updatedRecords = [_singleElementList[0], changeRecord];
|
||||
|
||||
} else {
|
||||
ListWrapper.push(updatedRecords, changeRecord);
|
||||
static addChange(changes, propertyName:string, change){
|
||||
if (isBlank(changes)) {
|
||||
changes = {};
|
||||
}
|
||||
return updatedRecords;
|
||||
changes[propertyName] = change;
|
||||
return changes;
|
||||
}
|
||||
}
|
@ -46,8 +46,7 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
selfIndex,
|
||||
r.bindingMemento,
|
||||
r.directiveMemento,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
r.lastInBinding,
|
||||
r.lastInDirective
|
||||
@ -74,8 +73,7 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
selfIndex,
|
||||
r.bindingMemento,
|
||||
r.directiveMemento,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
r.lastInBinding,
|
||||
r.lastInDirective
|
||||
|
35
modules/angular2/src/change_detection/constants.js
vendored
Normal file
35
modules/angular2/src/change_detection/constants.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
//TODO:vsavkin Use enums after switching to TypeScript
|
||||
|
||||
/**
|
||||
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
|
||||
* will become CHECKED.
|
||||
*/
|
||||
export const CHECK_ONCE="CHECK_ONCE";
|
||||
|
||||
/**
|
||||
* CHECKED means that the change detector should be skipped until its mode changes to
|
||||
* CHECK_ONCE or CHECK_ALWAYS.
|
||||
*/
|
||||
export const CHECKED="CHECKED";
|
||||
|
||||
/**
|
||||
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
|
||||
* will remain CHECK_ALWAYS.
|
||||
*/
|
||||
export const CHECK_ALWAYS="ALWAYS_CHECK";
|
||||
|
||||
/**
|
||||
* DETACHED means that the change detector sub tree is not a part of the main tree and
|
||||
* should be skipped.
|
||||
*/
|
||||
export const DETACHED="DETACHED";
|
||||
|
||||
/**
|
||||
* ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration.
|
||||
*/
|
||||
export const ON_PUSH = "ON_PUSH";
|
||||
|
||||
/**
|
||||
* DEFAULT means that the change detector's mode will be set to CHECK_ALWAYS during hydration.
|
||||
*/
|
||||
export const DEFAULT = "DEFAULT";
|
19
modules/angular2/src/change_detection/directive_record.js
vendored
Normal file
19
modules/angular2/src/change_detection/directive_record.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
export class DirectiveRecord {
|
||||
elementIndex:number;
|
||||
directiveIndex:number;
|
||||
callOnAllChangesDone:boolean;
|
||||
callOnChange:boolean;
|
||||
|
||||
constructor(elementIndex:number, directiveIndex:number,
|
||||
callOnAllChangesDone:boolean,
|
||||
callOnChange:boolean) {
|
||||
this.elementIndex = elementIndex;
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callOnAllChangesDone = callOnAllChangesDone;
|
||||
this.callOnChange = callOnChange;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return `${this.elementIndex}_${this.directiveIndex}`;
|
||||
}
|
||||
}
|
@ -2,8 +2,10 @@ import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/f
|
||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
||||
|
||||
|
||||
import {
|
||||
@ -17,6 +19,7 @@ import {
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_BINDING_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
|
||||
@ -33,8 +36,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
prevContexts:List;
|
||||
|
||||
protos:List<ProtoRecord>;
|
||||
directives:any;
|
||||
directiveRecords:List;
|
||||
changeControlStrategy:string;
|
||||
|
||||
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
|
||||
protoRecords:List<ProtoRecord>, directiveRecords:List) {
|
||||
super();
|
||||
this.dispatcher = dispatcher;
|
||||
this.pipeRegistry = pipeRegistry;
|
||||
@ -49,13 +56,18 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
this.locals = null;
|
||||
this.directives = null;
|
||||
|
||||
this.protos = protoRecords;
|
||||
this.directiveRecords = directiveRecords;
|
||||
this.changeControlStrategy = changeControlStrategy;
|
||||
}
|
||||
|
||||
hydrate(context:any, locals:any) {
|
||||
hydrate(context:any, locals:any, directives:any) {
|
||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
|
||||
this.values[0] = context;
|
||||
this.locals = locals;
|
||||
this.directives = directives;
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
@ -82,28 +94,57 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
detectChangesInRecords(throwOnChange:boolean) {
|
||||
var protos:List<ProtoRecord> = this.protos;
|
||||
|
||||
var updatedRecords = null;
|
||||
var changes = null;
|
||||
for (var i = 0; i < protos.length; ++i) {
|
||||
var proto:ProtoRecord = protos[i];
|
||||
var change = this._check(proto);
|
||||
|
||||
var change = this._check(proto);
|
||||
if (isPresent(change)) {
|
||||
var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change);
|
||||
updatedRecords = ChangeDetectionUtil.addRecord(updatedRecords, record);
|
||||
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change);
|
||||
this._updateDirectiveOrElement(change, proto.bindingRecord);
|
||||
changes = this._addChange(proto.bindingRecord, change, changes);
|
||||
}
|
||||
|
||||
if (proto.lastInDirective && isPresent(updatedRecords)) {
|
||||
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, updatedRecords[0]);
|
||||
|
||||
this.dispatcher.onRecordChange(proto.directiveMemento, updatedRecords);
|
||||
updatedRecords = null;
|
||||
if (proto.lastInDirective && isPresent(changes)) {
|
||||
this._directive(proto.bindingRecord.directiveRecord).onChange(changes);
|
||||
changes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callOnAllChangesDone() {
|
||||
var dirs = this.directiveRecords;
|
||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||
var dir = dirs[i];
|
||||
if (dir.callOnAllChangesDone) {
|
||||
this._directive(dir).onAllChangesDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_updateDirectiveOrElement(change, bindingRecord) {
|
||||
if (isBlank(bindingRecord.directiveRecord)) {
|
||||
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
||||
} else {
|
||||
bindingRecord.setter(this._directive(bindingRecord.directiveRecord), change.currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
_addChange(bindingRecord:BindingRecord, change, changes) {
|
||||
if (bindingRecord.callOnChange()) {
|
||||
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
|
||||
} else {
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
_directive(directive:DirectiveRecord) {
|
||||
return this.directives.directive(directive);
|
||||
}
|
||||
|
||||
_check(proto:ProtoRecord) {
|
||||
try {
|
||||
if (proto.mode == RECORD_TYPE_PIPE) {
|
||||
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
return this._pipeCheck(proto);
|
||||
} else {
|
||||
return this._referenceCheck(proto);
|
||||
@ -202,7 +243,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
if (isPresent(storedPipe)) {
|
||||
storedPipe.onDestroy();
|
||||
}
|
||||
var pipe = this.pipeRegistry.get(proto.name, context);
|
||||
|
||||
// Currently, only pipes that used in bindings in the template get
|
||||
// the bindingPropagationConfig of the encompassing component.
|
||||
//
|
||||
// In the future, pipes declared in the bind configuration should
|
||||
// be able to access the bindingPropagationConfig of that component.
|
||||
var bpc = proto.mode === RECORD_TYPE_BINDING_PIPE ? this.bindingPropagationConfig : null;
|
||||
var pipe = this.pipeRegistry.get(proto.name, context, bpc);
|
||||
this._writePipe(proto, pipe);
|
||||
return pipe;
|
||||
}
|
||||
@ -255,11 +303,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
var _singleElementList = [null];
|
||||
|
||||
function isSame(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a instanceof String && b instanceof String && a == b) return true;
|
||||
if ((a !== a) && (b !== b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,52 +1,22 @@
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Locals} from './parser/locals';
|
||||
import {DEFAULT} from './constants';
|
||||
import {BindingRecord} from './binding_record';
|
||||
|
||||
export class ChangeRecord {
|
||||
bindingMemento:any;
|
||||
change:any;
|
||||
|
||||
constructor(bindingMemento, change) {
|
||||
this.bindingMemento = bindingMemento;
|
||||
this.change = change;
|
||||
}
|
||||
|
||||
//REMOVE IT
|
||||
get currentValue() {
|
||||
return this.change.currentValue;
|
||||
}
|
||||
|
||||
get previousValue() {
|
||||
return this.change.previousValue;
|
||||
export class ProtoChangeDetector {
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List):ChangeDetector{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
|
||||
* will become CHECKED.
|
||||
*/
|
||||
export const CHECK_ONCE="CHECK_ONCE";
|
||||
|
||||
/**
|
||||
* CHECKED means that the change detector should be skipped until its mode changes to
|
||||
* CHECK_ONCE or CHECK_ALWAYS.
|
||||
*/
|
||||
export const CHECKED="CHECKED";
|
||||
|
||||
/**
|
||||
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
|
||||
* will remain CHECK_ALWAYS.
|
||||
*/
|
||||
export const CHECK_ALWAYS="ALWAYS_CHECK";
|
||||
|
||||
/**
|
||||
* DETACHED means that the change detector sub tree is not a part of the main tree and
|
||||
* should be skipped.
|
||||
*/
|
||||
export const DETACHED="DETACHED";
|
||||
export class ChangeDetection {
|
||||
createProtoChangeDetector(name:string, changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeDispatcher {
|
||||
onRecordChange(directiveMemento, records:List<ChangeRecord>) {}
|
||||
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
|
||||
}
|
||||
|
||||
export class ChangeDetector {
|
||||
@ -54,9 +24,10 @@ export class ChangeDetector {
|
||||
mode:string;
|
||||
|
||||
addChild(cd:ChangeDetector) {}
|
||||
addShadowDomChild(cd:ChangeDetector) {}
|
||||
removeChild(cd:ChangeDetector) {}
|
||||
remove() {}
|
||||
hydrate(context:any, locals:Locals) {}
|
||||
hydrate(context:any, locals:Locals, directives:any) {}
|
||||
dehydrate() {}
|
||||
markPathToRootAsCheckOnce() {}
|
||||
|
||||
|
@ -168,11 +168,13 @@ export class Pipe extends AST {
|
||||
exp:AST;
|
||||
name:string;
|
||||
args:List<AST>;
|
||||
constructor(exp:AST, name:string, args:List) {
|
||||
inBinding:boolean;
|
||||
constructor(exp:AST, name:string, args:List, inBinding:boolean) {
|
||||
super();
|
||||
this.exp = exp;
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.inBinding = inBinding;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
@ -418,7 +420,6 @@ export class TemplateBinding {
|
||||
name:string;
|
||||
expression:ASTWithSource;
|
||||
constructor(key:string, keyIsVar:boolean, name:string, expression:ASTWithSource) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.keyIsVar = keyIsVar;
|
||||
// only either name or expression will be filled.
|
||||
@ -445,6 +446,72 @@ export class AstVisitor {
|
||||
visitPrefixNot(ast:PrefixNot) {}
|
||||
}
|
||||
|
||||
export class AstTransformer {
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||
return new ImplicitReceiver();
|
||||
}
|
||||
|
||||
visitInterpolation(ast:Interpolation) {
|
||||
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
|
||||
}
|
||||
|
||||
visitLiteralPrimitive(ast:LiteralPrimitive) {
|
||||
return new LiteralPrimitive(ast.value);
|
||||
}
|
||||
|
||||
visitAccessMember(ast:AccessMember) {
|
||||
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
|
||||
}
|
||||
|
||||
visitMethodCall(ast:MethodCall) {
|
||||
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
|
||||
}
|
||||
|
||||
visitFunctionCall(ast:FunctionCall) {
|
||||
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
|
||||
}
|
||||
|
||||
visitLiteralArray(ast:LiteralArray) {
|
||||
return new LiteralArray(this.visitAll(ast.expressions));
|
||||
}
|
||||
|
||||
visitLiteralMap(ast:LiteralMap) {
|
||||
return new LiteralMap(ast.keys, this.visitAll(ast.values));
|
||||
}
|
||||
|
||||
visitBinary(ast:Binary) {
|
||||
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
|
||||
}
|
||||
|
||||
visitPrefixNot(ast:PrefixNot) {
|
||||
return new PrefixNot(ast.expression.visit(this));
|
||||
}
|
||||
|
||||
visitConditional(ast:Conditional) {
|
||||
return new Conditional(
|
||||
ast.condition.visit(this),
|
||||
ast.trueExp.visit(this),
|
||||
ast.falseExp.visit(this)
|
||||
);
|
||||
}
|
||||
|
||||
visitPipe(ast:Pipe) {
|
||||
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
|
||||
}
|
||||
|
||||
visitKeyedAccess(ast:KeyedAccess) {
|
||||
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
|
||||
}
|
||||
|
||||
visitAll(asts:List) {
|
||||
var res = ListWrapper.createFixedSize(asts.length);
|
||||
for (var i = 0; i < asts.length; ++i) {
|
||||
res[i] = asts[i].visit(this);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
var _evalListCache = [[],[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,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,0,0]];
|
||||
|
@ -58,7 +58,7 @@ export class Parser {
|
||||
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
||||
|
||||
var res = ListWrapper.reduce(pipes,
|
||||
(result, currentPipeName) => new Pipe(result, currentPipeName, []),
|
||||
(result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
||||
bindingAst.ast);
|
||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
||||
}
|
||||
@ -220,7 +220,7 @@ class _ParseAST {
|
||||
while (this.optionalCharacter($COLON)) {
|
||||
ListWrapper.push(args, this.parseExpression());
|
||||
}
|
||||
result = new Pipe(result, name, args);
|
||||
result = new Pipe(result, name, args, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -16,17 +16,20 @@ import {
|
||||
|
||||
import {NO_CHANGE, Pipe} from './pipe';
|
||||
|
||||
export class ArrayChangesFactory {
|
||||
export class IterableChangesFactory {
|
||||
supports(obj):boolean {
|
||||
return ArrayChanges.supportsObj(obj);
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
return new ArrayChanges();
|
||||
create(bpc):Pipe {
|
||||
return new IterableChanges();
|
||||
}
|
||||
}
|
||||
|
||||
export class ArrayChanges extends Pipe {
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class IterableChanges extends Pipe {
|
||||
_collection;
|
||||
_length:int;
|
||||
_linkedRecords:_DuplicateMap;
|
||||
@ -66,7 +69,7 @@ export class ArrayChanges extends Pipe {
|
||||
}
|
||||
|
||||
supports(obj):boolean {
|
||||
return ArrayChanges.supportsObj(obj);
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
get collection() {
|
||||
@ -126,7 +129,8 @@ export class ArrayChanges extends Pipe {
|
||||
|
||||
var record:CollectionChangeRecord = this._itHead;
|
||||
var mayBeDirty:boolean = false;
|
||||
var index:int, item;
|
||||
var index:int;
|
||||
var item;
|
||||
|
||||
if (ListWrapper.isList(collection)) {
|
||||
var list = collection;
|
||||
@ -500,6 +504,9 @@ export class ArrayChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class CollectionChangeRecord {
|
||||
currentIndex:int;
|
||||
previousIndex:int;
|
@ -3,16 +3,22 @@ import {stringify, looseIdentical, isJsObject} from 'angular2/src/facade/lang';
|
||||
|
||||
import {NO_CHANGE, Pipe} from './pipe';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KeyValueChangesFactory {
|
||||
supports(obj):boolean {
|
||||
return KeyValueChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
create(bpc):Pipe {
|
||||
return new KeyValueChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KeyValueChanges extends Pipe {
|
||||
_records:Map;
|
||||
|
||||
@ -107,9 +113,9 @@ export class KeyValueChanges extends Pipe {
|
||||
var newSeqRecord;
|
||||
if (oldSeqRecord !== null && key === oldSeqRecord.key) {
|
||||
newSeqRecord = oldSeqRecord;
|
||||
if (!looseIdentical(value, oldSeqRecord._currentValue)) {
|
||||
oldSeqRecord._previousValue = oldSeqRecord._currentValue;
|
||||
oldSeqRecord._currentValue = value;
|
||||
if (!looseIdentical(value, oldSeqRecord.currentValue)) {
|
||||
oldSeqRecord.previousValue = oldSeqRecord.currentValue;
|
||||
oldSeqRecord.currentValue = value;
|
||||
this._addToChanges(oldSeqRecord);
|
||||
}
|
||||
} else {
|
||||
@ -124,7 +130,7 @@ export class KeyValueChanges extends Pipe {
|
||||
} else {
|
||||
newSeqRecord = new KVChangeRecord(key);
|
||||
MapWrapper.set(records, key, newSeqRecord);
|
||||
newSeqRecord._currentValue = value;
|
||||
newSeqRecord.currentValue = value;
|
||||
this._addToAdditions(newSeqRecord);
|
||||
}
|
||||
}
|
||||
@ -158,11 +164,11 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
record._previousValue = record._currentValue;
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
for (record = this._additionsHead; record != null; record = record._nextAdded) {
|
||||
record._previousValue = record._currentValue;
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
// todo(vicb) once assert is supported
|
||||
@ -215,8 +221,8 @@ export class KeyValueChanges extends Pipe {
|
||||
}
|
||||
|
||||
for (var rec:KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
|
||||
rec._previousValue = rec._currentValue;
|
||||
rec._currentValue = null;
|
||||
rec.previousValue = rec.currentValue;
|
||||
rec.currentValue = null;
|
||||
MapWrapper.delete(this._records, rec.key);
|
||||
}
|
||||
}
|
||||
@ -349,10 +355,13 @@ export class KeyValueChanges extends Pipe {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KVChangeRecord {
|
||||
key;
|
||||
_previousValue;
|
||||
_currentValue;
|
||||
previousValue;
|
||||
currentValue;
|
||||
|
||||
_nextPrevious:KVChangeRecord;
|
||||
_next:KVChangeRecord;
|
||||
@ -363,8 +372,8 @@ export class KVChangeRecord {
|
||||
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
this._previousValue = null;
|
||||
this._currentValue = null;
|
||||
this.previousValue = null;
|
||||
this.currentValue = null;
|
||||
|
||||
this._nextPrevious = null;
|
||||
this._next = null;
|
||||
@ -375,9 +384,9 @@ export class KVChangeRecord {
|
||||
}
|
||||
|
||||
toString():string {
|
||||
return looseIdentical(this._previousValue, this._currentValue) ?
|
||||
return looseIdentical(this.previousValue, this.currentValue) ?
|
||||
stringify(this.key) :
|
||||
(stringify(this.key) + '[' + stringify(this._previousValue) + '->' +
|
||||
stringify(this._currentValue) + ']');
|
||||
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
||||
stringify(this.currentValue) + ']');
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Pipe, NO_CHANGE} from './pipe';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class NullPipeFactory {
|
||||
supports(obj):boolean {
|
||||
return NullPipe.supportsObj(obj);
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
create(bpc):Pipe {
|
||||
return new NullPipe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class NullPipe extends Pipe {
|
||||
called:boolean;
|
||||
constructor() {
|
||||
|
@ -1,7 +1,37 @@
|
||||
/**
|
||||
* Indicates that the result of a [Pipe] transformation has not changed since the last time the pipe was called.
|
||||
*
|
||||
* Suppose we have a pipe that computes changes in an array by performing a simple diff operation. If
|
||||
* we call this pipe with the same array twice, it will return `NO_CHANGE` the second time.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
|
||||
export var NO_CHANGE = new Object();
|
||||
|
||||
/**
|
||||
* An interface for extending the list of pipes known to Angular.
|
||||
*
|
||||
* If you are writing a custom [Pipe], you must extend this interface.
|
||||
*
|
||||
* #Example
|
||||
*
|
||||
* ```
|
||||
* class DoublePipe extends Pipe {
|
||||
* supports(obj) {
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
* transform(value) {
|
||||
* return `${value}${value}`;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class Pipe {
|
||||
supports(obj):boolean {return false;}
|
||||
onDestroy() {}
|
||||
transform(value:any):any {return null;}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||
import {Pipe} from './pipe';
|
||||
import {BindingPropagationConfig} from '../binding_propagation_config';
|
||||
|
||||
export class PipeRegistry {
|
||||
config;
|
||||
@ -9,7 +10,7 @@ export class PipeRegistry {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
get(type:string, obj):Pipe {
|
||||
get(type:string, obj, bpc:BindingPropagationConfig):Pipe {
|
||||
var listOfConfigs = this.config[type];
|
||||
if (isBlank(listOfConfigs)) {
|
||||
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||
@ -22,6 +23,6 @@ export class PipeRegistry {
|
||||
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||
}
|
||||
|
||||
return matchingConfig.create();
|
||||
return matchingConfig.create(bpc);
|
||||
}
|
||||
}
|
@ -22,11 +22,12 @@ import {
|
||||
PrefixNot
|
||||
} from './parser/ast';
|
||||
|
||||
import {ChangeRecord, ChangeDispatcher, ChangeDetector} from './interfaces';
|
||||
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {BindingRecord} from './binding_record';
|
||||
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
@ -41,47 +42,32 @@ import {
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_BINDING_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
|
||||
export class ProtoChangeDetector {
|
||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{
|
||||
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 {
|
||||
_pipeRegistry:PipeRegistry;
|
||||
_records:List<ProtoRecord>;
|
||||
_changeControlStrategy:string;
|
||||
|
||||
constructor(pipeRegistry:PipeRegistry) {
|
||||
constructor(pipeRegistry:PipeRegistry, changeControlStrategy:string) {
|
||||
super();
|
||||
this._pipeRegistry = pipeRegistry;
|
||||
this._changeControlStrategy = changeControlStrategy;
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
|
||||
this._createRecordsIfNecessary(bindingRecords, variableBindings);
|
||||
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
||||
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
|
||||
this._pipeRegistry, this._records, directiveRecords);
|
||||
}
|
||||
|
||||
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||
if (isBlank(this._records)) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(bindingRecords, (r) => {
|
||||
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||
ListWrapper.forEach(bindingRecords, (b) => {
|
||||
recordBuilder.addAst(b, variableBindings);
|
||||
});
|
||||
this._records = coalesce(recordBuilder.records);
|
||||
}
|
||||
@ -92,28 +78,31 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
|
||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||
_factory:Function;
|
||||
_pipeRegistry;
|
||||
_changeControlStrategy:string;
|
||||
|
||||
constructor(pipeRegistry) {
|
||||
constructor(pipeRegistry, changeControlStrategy:string) {
|
||||
super();
|
||||
this._pipeRegistry = pipeRegistry;
|
||||
this._factory = null;
|
||||
this._changeControlStrategy = changeControlStrategy;
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||
this._createFactoryIfNecessary(bindingRecords, variableBindings);
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
|
||||
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveRecords);
|
||||
return this._factory(dispatcher, this._pipeRegistry);
|
||||
}
|
||||
|
||||
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveRecords:List) {
|
||||
if (isBlank(this._factory)) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(bindingRecords, (r) => {
|
||||
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||
ListWrapper.forEach(bindingRecords, (b) => {
|
||||
recordBuilder.addAst(b, variableBindings);
|
||||
});
|
||||
var c = _jitProtoChangeDetectorClassCounter++;
|
||||
var records = coalesce(recordBuilder.records);
|
||||
var typeName = `ChangeDetector${c}`;
|
||||
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
|
||||
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
|
||||
directiveRecords).generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,13 +114,13 @@ class ProtoRecordBuilder {
|
||||
this.records = [];
|
||||
}
|
||||
|
||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, variableBindings:List = null) {
|
||||
addAst(b:BindingRecord, variableBindings:List = null) {
|
||||
var last = ListWrapper.last(this.records);
|
||||
if (isPresent(last) && last.directiveMemento == directiveMemento) {
|
||||
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
|
||||
last.lastInDirective = false;
|
||||
}
|
||||
|
||||
var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length, variableBindings);
|
||||
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
|
||||
if (! ListWrapper.isEmpty(pr)) {
|
||||
var last = ListWrapper.last(pr);
|
||||
last.lastInBinding = true;
|
||||
@ -144,24 +133,22 @@ class ProtoRecordBuilder {
|
||||
|
||||
class _ConvertAstIntoProtoRecords {
|
||||
protoRecords:List;
|
||||
bindingMemento:any;
|
||||
directiveMemento:any;
|
||||
bindingRecord:BindingRecord;
|
||||
variableBindings:List;
|
||||
contextIndex:number;
|
||||
expressionAsString:string;
|
||||
|
||||
constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string, variableBindings:List) {
|
||||
constructor(bindingRecord:BindingRecord, contextIndex:number, expressionAsString:string, variableBindings:List) {
|
||||
this.protoRecords = [];
|
||||
this.bindingMemento = bindingMemento;
|
||||
this.directiveMemento = directiveMemento;
|
||||
this.bindingRecord = bindingRecord;
|
||||
this.contextIndex = contextIndex;
|
||||
this.expressionAsString = expressionAsString;
|
||||
this.variableBindings = variableBindings;
|
||||
}
|
||||
|
||||
static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number, variableBindings:List) {
|
||||
var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString(), variableBindings);
|
||||
ast.visit(c);
|
||||
static convert(b:BindingRecord, contextIndex:number, variableBindings:List) {
|
||||
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
|
||||
b.ast.visit(c);
|
||||
return c.protoRecords;
|
||||
}
|
||||
|
||||
@ -239,7 +226,8 @@ class _ConvertAstIntoProtoRecords {
|
||||
|
||||
visitPipe(ast:Pipe) {
|
||||
var value = ast.exp.visit(this);
|
||||
return this._addRecord(RECORD_TYPE_PIPE, ast.name, ast.name, [], null, value);
|
||||
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
|
||||
return this._addRecord(type, ast.name, ast.name, [], null, value);
|
||||
}
|
||||
|
||||
visitKeyedAccess(ast:KeyedAccess) {
|
||||
@ -261,7 +249,7 @@ class _ConvertAstIntoProtoRecords {
|
||||
var selfIndex = ++ this.contextIndex;
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
|
||||
this.bindingMemento, this.directiveMemento, this.expressionAsString, false, false));
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
return selfIndex;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {BindingRecord} from './binding_record';
|
||||
|
||||
export const RECORD_TYPE_SELF = 0;
|
||||
export const RECORD_TYPE_CONST = 1;
|
||||
@ -9,7 +10,8 @@ export const RECORD_TYPE_INVOKE_METHOD = 5;
|
||||
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
|
||||
export const RECORD_TYPE_KEYED_ACCESS = 7;
|
||||
export const RECORD_TYPE_PIPE = 8;
|
||||
export const RECORD_TYPE_INTERPOLATE = 9;
|
||||
export const RECORD_TYPE_BINDING_PIPE = 9;
|
||||
export const RECORD_TYPE_INTERPOLATE = 10;
|
||||
|
||||
export class ProtoRecord {
|
||||
mode:number;
|
||||
@ -19,8 +21,7 @@ export class ProtoRecord {
|
||||
fixedArgs:List;
|
||||
contextIndex:number;
|
||||
selfIndex:number;
|
||||
bindingMemento:any;
|
||||
directiveMemento:any;
|
||||
bindingRecord:BindingRecord;
|
||||
lastInBinding:boolean;
|
||||
lastInDirective:boolean;
|
||||
expressionAsString:string;
|
||||
@ -32,8 +33,7 @@ export class ProtoRecord {
|
||||
fixedArgs:List,
|
||||
contextIndex:number,
|
||||
selfIndex:number,
|
||||
bindingMemento:any,
|
||||
directiveMemento:any,
|
||||
bindingRecord:BindingRecord,
|
||||
expressionAsString:string,
|
||||
lastInBinding:boolean,
|
||||
lastInDirective:boolean) {
|
||||
@ -45,8 +45,7 @@ export class ProtoRecord {
|
||||
this.fixedArgs = fixedArgs;
|
||||
this.contextIndex = contextIndex;
|
||||
this.selfIndex = selfIndex;
|
||||
this.bindingMemento = bindingMemento;
|
||||
this.directiveMemento = directiveMemento;
|
||||
this.bindingRecord = bindingRecord;
|
||||
this.lastInBinding = lastInBinding;
|
||||
this.lastInDirective = lastInDirective;
|
||||
this.expressionAsString = expressionAsString;
|
||||
|
714
modules/angular2/src/core/annotations/annotations.js
vendored
714
modules/angular2/src/core/annotations/annotations.js
vendored
File diff suppressed because it is too large
Load Diff
40
modules/angular2/src/core/annotations/di.js
vendored
40
modules/angular2/src/core/annotations/di.js
vendored
@ -12,6 +12,10 @@ export class EventEmitter extends DependencyAnnotation {
|
||||
super();
|
||||
this.eventName = eventName;
|
||||
}
|
||||
|
||||
get token() {
|
||||
return Function;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,4 +29,40 @@ export class PropertySetter extends DependencyAnnotation {
|
||||
super();
|
||||
this.propName = propName;
|
||||
}
|
||||
|
||||
get token() {
|
||||
return Function;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The directive can inject the value of an attribute of the host element
|
||||
*/
|
||||
export class Attribute extends DependencyAnnotation {
|
||||
attributeName: string;
|
||||
@CONST()
|
||||
constructor(attributeName) {
|
||||
super();
|
||||
this.attributeName = attributeName;
|
||||
}
|
||||
|
||||
get token() {
|
||||
//Normally one would default a token to a type of an injected value but here
|
||||
//the type of a variable is "string" and we can't use primitive type as a return value
|
||||
//so we use instance of Attribute instead. This doesn't matter much in practice as arguments
|
||||
//with @Attribute annotation are injected by ElementInjector that doesn't take tokens into account.
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The directive can inject an query that would reflect a list of ancestor directives
|
||||
*/
|
||||
export class Query extends DependencyAnnotation {
|
||||
directive;
|
||||
@CONST()
|
||||
constructor(directive) {
|
||||
super();
|
||||
this.directive = directive;
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* @publicModule angular2/angular2
|
||||
*/
|
||||
export class Template {
|
||||
url:any; //string;
|
||||
inline:any; //string;
|
||||
directives:any; //List<Type>;
|
||||
formatters:any; //List<Type>;
|
||||
source:any;//List<Template>;
|
||||
locale:any; //string
|
||||
device:any; //string
|
||||
@CONST()
|
||||
constructor({
|
||||
url,
|
||||
inline,
|
||||
directives,
|
||||
formatters,
|
||||
source,
|
||||
locale,
|
||||
device
|
||||
}: {
|
||||
url: string,
|
||||
inline: string,
|
||||
directives: List<Type>,
|
||||
formatters: List<Type>,
|
||||
source: List<Template>,
|
||||
locale: string,
|
||||
device: string
|
||||
})
|
||||
{
|
||||
this.url = url;
|
||||
this.inline = inline;
|
||||
this.directives = directives;
|
||||
this.formatters = formatters;
|
||||
this.source = source;
|
||||
this.locale = locale;
|
||||
this.device = device;
|
||||
}
|
||||
}
|
70
modules/angular2/src/core/annotations/view.js
vendored
Normal file
70
modules/angular2/src/core/annotations/view.js
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Declare the available HTML templates for an application.
|
||||
*
|
||||
* Each angular component requires a single `@Component` and at least one `@View` annotation. The @View
|
||||
* annotation specifies the HTML template to use, and lists the directives that are active within the template.
|
||||
*
|
||||
* When a component is instantiated, the template is loaded into the component's shadow root, and the
|
||||
* expressions and statements in the template are evaluated against the component.
|
||||
*
|
||||
* For details on the `@Component` annotation, see [Component].
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'greet'
|
||||
* })
|
||||
* @View({
|
||||
* template: 'Hello {{name}}!',
|
||||
* directives: [GreetUser, Bold]
|
||||
* })
|
||||
* class Greet {
|
||||
* name: string;
|
||||
*
|
||||
* constructor() {
|
||||
* this.name = 'World';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class View {
|
||||
templateUrl:any; //string;
|
||||
template:any; //string;
|
||||
directives:any; //List<Type>;
|
||||
formatters:any; //List<Type>;
|
||||
source:any;//List<View>;
|
||||
locale:any; //string
|
||||
device:any; //string
|
||||
@CONST()
|
||||
constructor({
|
||||
templateUrl,
|
||||
template,
|
||||
directives,
|
||||
formatters,
|
||||
source,
|
||||
locale,
|
||||
device
|
||||
}: {
|
||||
templateUrl: string,
|
||||
template: string,
|
||||
directives: List<Type>,
|
||||
formatters: List<Type>,
|
||||
source: List<View>,
|
||||
locale: string,
|
||||
device: string
|
||||
})
|
||||
{
|
||||
this.templateUrl = templateUrl;
|
||||
this.template = template;
|
||||
this.directives = directives;
|
||||
this.formatters = formatters;
|
||||
this.source = source;
|
||||
this.locale = locale;
|
||||
this.device = device;
|
||||
}
|
||||
}
|
@ -2,10 +2,45 @@ import {CONST} from 'angular2/src/facade/lang';
|
||||
import {DependencyAnnotation} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* The directive can only be injected from the current element
|
||||
* or from its parent.
|
||||
* Specifies that an injector should retrieve a dependency from the direct parent.
|
||||
*
|
||||
* @publicModule angular2/annotations
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from its parent element.
|
||||
*
|
||||
* ```
|
||||
* @Decorator({
|
||||
* selector: '[dependency]',
|
||||
* properties: {
|
||||
* 'id':'dependency'
|
||||
* }
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Decorator({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Parent() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(1);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We use this with the following HTML template:
|
||||
*
|
||||
* ```
|
||||
* <div dependency="1">
|
||||
* <div dependency="2" my-directive></div>
|
||||
* </div>
|
||||
* ```
|
||||
* The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from the
|
||||
* parent element (even thought the current element could resolve it): Angular injects `dependency=1`.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class Parent extends DependencyAnnotation {
|
||||
@CONST()
|
||||
@ -15,10 +50,58 @@ export class Parent extends DependencyAnnotation {
|
||||
}
|
||||
|
||||
/**
|
||||
* The directive can only be injected from the current element
|
||||
* or from its ancestor.
|
||||
* Specifies that an injector should retrieve a dependency from any ancestor element.
|
||||
*
|
||||
* @publicModule angular2/annotations
|
||||
* An ancestor is any element between the parent element and shadow root.
|
||||
*
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from an ancestor element.
|
||||
*
|
||||
* ```
|
||||
* @Decorator({
|
||||
* selector: '[dependency]',
|
||||
* properties: {
|
||||
* 'id':'dependency'
|
||||
* }
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Decorator({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Ancestor() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(2);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We use this with the following HTML template:
|
||||
*
|
||||
* ```
|
||||
* <div dependency="1">
|
||||
* <div dependency="2">
|
||||
* <div>
|
||||
* <div dependency="3" my-directive></div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency from the
|
||||
* nearest ancestor element:
|
||||
* - The current element `dependency="3"` is skipped because it is not an ancestor.
|
||||
* - Next parent has no directives `<div>`
|
||||
* - Next parent has the `Dependency` directive and so the dependency is satisfied.
|
||||
*
|
||||
* Angular injects `dependency=2`.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class Ancestor extends DependencyAnnotation {
|
||||
@CONST()
|
||||
|
150
modules/angular2/src/core/application.js
vendored
150
modules/angular2/src/core/application.js
vendored
@ -3,43 +3,54 @@ import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print, strin
|
||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Compiler, CompilerCache} from './compiler/compiler';
|
||||
import {ProtoView} from './compiler/view';
|
||||
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection, jitChangeDetection} from 'angular2/change_detection';
|
||||
import {ExceptionHandler} from './exception_handler';
|
||||
import {TemplateLoader} from './compiler/template_loader';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {TemplateResolver} from './compiler/template_resolver';
|
||||
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
|
||||
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
|
||||
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
|
||||
import {HammerGesturesPlugin} from 'angular2/src/core/events/hammer_gestures';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {XHR} from 'angular2/src/services/xhr';
|
||||
import {XHRImpl} from 'angular2/src/services/xhr_impl';
|
||||
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
||||
import {Binding} from 'angular2/src/di/binding';
|
||||
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 {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
||||
import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
import * as rc from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {ComponentRef} from 'angular2/src/core/compiler/element_injector';
|
||||
import * as rvf from 'angular2/src/render/dom/view/view_factory';
|
||||
|
||||
import {
|
||||
appComponentRefToken,
|
||||
appChangeDetectorToken,
|
||||
appElementToken,
|
||||
appComponentAnnotatedTypeToken,
|
||||
appDocumentToken,
|
||||
} from './application_tokens';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
|
||||
// Contains everything that is safe to share between applications.
|
||||
var _rootBindings = [
|
||||
bind(Reflector).toValue(reflector)
|
||||
bind(Reflector).toValue(reflector),
|
||||
TestabilityRegistry
|
||||
];
|
||||
|
||||
export var appViewToken = new OpaqueToken('AppView');
|
||||
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
|
||||
export var appElementToken = new OpaqueToken('AppElement');
|
||||
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
|
||||
export var appDocumentToken = new OpaqueToken('AppDocument');
|
||||
|
||||
function _injectorBindings(appComponentType): List<Binding> {
|
||||
return [
|
||||
bind(appDocumentToken).toValue(DOM.defaultDoc()),
|
||||
@ -56,42 +67,45 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
}
|
||||
return element;
|
||||
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
|
||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector, appElement,
|
||||
appComponentAnnotatedType, testability, registry) => {
|
||||
|
||||
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
|
||||
appComponentAnnotatedType, strategy, eventManager) => {
|
||||
var annotation = appComponentAnnotatedType.annotation;
|
||||
if(!isBlank(annotation) && !(annotation instanceof Component)) {
|
||||
var type = appComponentAnnotatedType.type;
|
||||
throw new BaseException(`Only Components can be bootstrapped; ` +
|
||||
`Directive of ${stringify(type)} is not a Component`);
|
||||
}
|
||||
return compiler.compile(appComponentAnnotatedType.type).then(
|
||||
(protoView) => {
|
||||
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
|
||||
appComponentAnnotatedType, changeDetection.createProtoChangeDetector('root'),
|
||||
strategy);
|
||||
// The light Dom of the app element is not considered part of
|
||||
// the angular application. Thus the context and lightDomInjector are
|
||||
// empty.
|
||||
var view = appProtoView.instantiate(null, eventManager);
|
||||
view.hydrate(injector, null, null, new Object(), null);
|
||||
return view;
|
||||
});
|
||||
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
ShadowDomStrategy, EventManager]),
|
||||
// We need to do this here to ensure that we create Testability and
|
||||
// it's ready on the window for users.
|
||||
registry.registerApplication(appElement, testability);
|
||||
return dynamicComponentLoader.loadIntoNewLocation(appElement, appComponentAnnotatedType.type, null, injector);
|
||||
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
Testability, TestabilityRegistry]),
|
||||
|
||||
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
|
||||
[appViewToken]),
|
||||
bind(appComponentType).toFactory((rootView) => rootView.elementInjectors[0].getComponent(),
|
||||
[appViewToken]),
|
||||
bind(appChangeDetectorToken).toFactory((ref) => ref.hostView.changeDetector,
|
||||
[appComponentRefToken]),
|
||||
bind(appComponentType).toFactory((ref) => ref.instance,
|
||||
[appComponentRefToken]),
|
||||
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),[ExceptionHandler]),
|
||||
bind(EventManager).toFactory((zone) => {
|
||||
var plugins = [new HammerGesturesPlugin(), new DomEventsPlugin()];
|
||||
var plugins = [new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
||||
return new EventManager(plugins, zone);
|
||||
}, [VmTurnZone]),
|
||||
bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||
[StyleUrlResolver, appDocumentToken]),
|
||||
bind(Renderer).toClass(DirectDomRenderer),
|
||||
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
||||
// TODO(tbosch): We need an explicit factory here, as
|
||||
// we are getting errors in dart2js with mirrors...
|
||||
bind(rvf.ViewFactory).toFactory(
|
||||
(capacity, eventManager, shadowDomStrategy) => new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy),
|
||||
[rvf.VIEW_POOL_CAPACITY, EventManager, ShadowDomStrategy]
|
||||
),
|
||||
bind(rvf.VIEW_POOL_CAPACITY).toValue(10000),
|
||||
ProtoViewFactory,
|
||||
// TODO(tbosch): We need an explicit factory here, as
|
||||
// we are getting errors in dart2js with mirrors...
|
||||
bind(ViewFactory).toFactory(
|
||||
(capacity) => new ViewFactory(capacity),
|
||||
[VIEW_POOL_CAPACITY]
|
||||
),
|
||||
bind(VIEW_POOL_CAPACITY).toValue(10000),
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
TemplateResolver,
|
||||
@ -106,7 +120,8 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
UrlResolver,
|
||||
StyleUrlResolver,
|
||||
StyleInliner,
|
||||
bind(CssProcessor).toFactory(() => new CssProcessor(null), []),
|
||||
DynamicComponentLoader,
|
||||
Testability
|
||||
];
|
||||
}
|
||||
|
||||
@ -146,8 +161,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
||||
* 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 }}`.)
|
||||
* bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from Angular 2 component double-curly
|
||||
* `{{ syntax }}`.
|
||||
*
|
||||
* We can use this script code:
|
||||
*
|
||||
@ -155,8 +170,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
||||
* @Component({
|
||||
* selector: 'my-app'
|
||||
* })
|
||||
* @Template({
|
||||
* inline: 'Hello {{ name }}!'
|
||||
* @View({
|
||||
* template: 'Hello {{ name }}!'
|
||||
* })
|
||||
* class MyApp {
|
||||
* name:string;
|
||||
@ -177,8 +192,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
||||
* 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.
|
||||
* `injectables`. Optionally, you can also override the injector configuration for an app by invoking
|
||||
* `bootstrap` with the `componentInjectableBindings` 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.
|
||||
@ -202,8 +217,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
@ -220,17 +235,17 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
|
||||
* # 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
|
||||
* - [componentInjectableBindings]: An additional set of bindings that can be added to the [Component.injectables] 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
|
||||
* @exportedAs angular2/core
|
||||
*/
|
||||
export function bootstrap(appComponentType: Type,
|
||||
componentServiceBindings: List<Binding>=null,
|
||||
errorReporter: Function=null): Promise<Injector> {
|
||||
componentInjectableBindings: List<Binding> = null,
|
||||
errorReporter: Function = null): Promise<ComponentRef> {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
var bootstrapProcess = PromiseWrapper.completer();
|
||||
|
||||
@ -239,16 +254,17 @@ export function bootstrap(appComponentType: Type,
|
||||
// TODO(rado): prepopulate template cache, so applications with only
|
||||
// index.html and main.js are possible.
|
||||
|
||||
var appInjector = _createAppInjector(appComponentType, componentServiceBindings, zone);
|
||||
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
||||
|
||||
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
|
||||
(rootView) => {
|
||||
PromiseWrapper.then(appInjector.asyncGet(appComponentRefToken),
|
||||
(componentRef) => {
|
||||
var appChangeDetector = componentRef.hostView.changeDetector;
|
||||
// retrieve life cycle: may have already been created if injected in root component
|
||||
var lc=appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, rootView.changeDetector);
|
||||
var lc = appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, appChangeDetector);
|
||||
lc.tick(); //the first tick that will bootstrap the app
|
||||
|
||||
bootstrapProcess.resolve(appInjector);
|
||||
bootstrapProcess.resolve(componentRef);
|
||||
},
|
||||
|
||||
(err) => {
|
||||
@ -260,10 +276,10 @@ export function bootstrap(appComponentType: Type,
|
||||
}
|
||||
|
||||
function _createAppInjector(appComponentType: Type, bindings: List<Binding>, zone: VmTurnZone): Injector {
|
||||
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
|
||||
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
|
||||
var mergedBindings = isPresent(bindings) ?
|
||||
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
|
||||
_injectorBindings(appComponentType);
|
||||
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
|
||||
return _rootInjector.createChild(mergedBindings);
|
||||
return _rootInjector.resolveAndCreateChild(mergedBindings);
|
||||
}
|
||||
|
7
modules/angular2/src/core/application_tokens.js
vendored
Normal file
7
modules/angular2/src/core/application_tokens.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import {OpaqueToken} from 'angular2/di';
|
||||
|
||||
export var appComponentRefToken = new OpaqueToken('ComponentRef');
|
||||
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
|
||||
export var appElementToken = new OpaqueToken('AppElement');
|
||||
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
|
||||
export var appDocumentToken = new OpaqueToken('AppDocument');
|
278
modules/angular2/src/core/compiler/compiler.js
vendored
278
modules/angular2/src/core/compiler/compiler.js
vendored
@ -3,26 +3,21 @@ import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ChangeDetection, Parser} from 'angular2/change_detection';
|
||||
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {ProtoView} from './view';
|
||||
import {CompilePipeline} from './pipeline/compile_pipeline';
|
||||
import {CompileElement} from './pipeline/compile_element';
|
||||
import {createDefaultSteps} from './pipeline/default_steps';
|
||||
import {TemplateLoader} from './template_loader';
|
||||
import {Component, Viewport, DynamicComponent, Decorator} from '../annotations/annotations';
|
||||
import {AppProtoView} from './view';
|
||||
import {DirectiveBinding} from './element_injector';
|
||||
import {TemplateResolver} from './template_resolver';
|
||||
import {Template} from '../annotations/template';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {CompileStep} from './pipeline/compile_step';
|
||||
import {View} from '../annotations/view';
|
||||
import {ComponentUrlMapper} from './component_url_mapper';
|
||||
import {UrlResolver} from './url_resolver';
|
||||
import {CssProcessor} from './css_processor';
|
||||
import {ProtoViewFactory} from './proto_view_factory';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
|
||||
/**
|
||||
* Cache that stores the ProtoView of the template of a component.
|
||||
* Cache that stores the AppProtoView of the template of a component.
|
||||
* Used to prevent duplicate work and resolve cyclic dependencies.
|
||||
* @publicModule angular2/angular2
|
||||
*/
|
||||
@Injectable()
|
||||
export class CompilerCache {
|
||||
@ -31,11 +26,11 @@ export class CompilerCache {
|
||||
this._cache = MapWrapper.create();
|
||||
}
|
||||
|
||||
set(component:Type, protoView:ProtoView) {
|
||||
set(component:Type, protoView:AppProtoView) {
|
||||
MapWrapper.set(this._cache, component, protoView);
|
||||
}
|
||||
|
||||
get(component:Type):ProtoView {
|
||||
get(component:Type):AppProtoView {
|
||||
var result = MapWrapper.get(this._cache, component);
|
||||
return normalizeBlank(result);
|
||||
}
|
||||
@ -45,74 +40,66 @@ export class CompilerCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The compiler loads and translates the html templates of components into
|
||||
* nested ProtoViews. To decompose its functionality it uses
|
||||
* the CompilePipeline and the CompileSteps.
|
||||
* @publicModule angular2/angular2
|
||||
*/
|
||||
|
||||
@Injectable()
|
||||
export class Compiler {
|
||||
_reader: DirectiveMetadataReader;
|
||||
_parser:Parser;
|
||||
_compilerCache:CompilerCache;
|
||||
_changeDetection:ChangeDetection;
|
||||
_templateLoader:TemplateLoader;
|
||||
_compiling:Map<Type, Promise>;
|
||||
_shadowDomStrategy: ShadowDomStrategy;
|
||||
_templateResolver: TemplateResolver;
|
||||
_componentUrlMapper: ComponentUrlMapper;
|
||||
_urlResolver: UrlResolver;
|
||||
_appUrl: string;
|
||||
_cssProcessor: CssProcessor;
|
||||
_renderer: renderApi.Renderer;
|
||||
_protoViewFactory:ProtoViewFactory;
|
||||
|
||||
constructor(changeDetection:ChangeDetection,
|
||||
templateLoader:TemplateLoader,
|
||||
reader: DirectiveMetadataReader,
|
||||
parser:Parser,
|
||||
constructor(reader: DirectiveMetadataReader,
|
||||
cache:CompilerCache,
|
||||
shadowDomStrategy: ShadowDomStrategy,
|
||||
templateResolver: TemplateResolver,
|
||||
componentUrlMapper: ComponentUrlMapper,
|
||||
urlResolver: UrlResolver,
|
||||
cssProcessor: CssProcessor) {
|
||||
this._changeDetection = changeDetection;
|
||||
renderer: renderApi.Renderer,
|
||||
protoViewFactory: ProtoViewFactory) {
|
||||
this._reader = reader;
|
||||
this._parser = parser;
|
||||
this._compilerCache = cache;
|
||||
this._templateLoader = templateLoader;
|
||||
this._compiling = MapWrapper.create();
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
this._templateResolver = templateResolver;
|
||||
this._componentUrlMapper = componentUrlMapper;
|
||||
this._urlResolver = urlResolver;
|
||||
this._appUrl = urlResolver.resolve(null, './');
|
||||
this._cssProcessor = cssProcessor;
|
||||
this._renderer = renderer;
|
||||
this._protoViewFactory = protoViewFactory;
|
||||
}
|
||||
|
||||
createSteps(component:Type, template: Template):List<CompileStep> {
|
||||
var dirMetadata = ListWrapper.map(this._flattenDirectives(template),
|
||||
(d) => this._reader.read(d));
|
||||
|
||||
var cmpMetadata = this._reader.read(component);
|
||||
|
||||
var templateUrl = this._templateLoader.getTemplateUrl(template);
|
||||
|
||||
return createDefaultSteps(this._changeDetection, this._parser, cmpMetadata, dirMetadata,
|
||||
this._shadowDomStrategy, templateUrl, this._cssProcessor);
|
||||
_bindDirective(directiveTypeOrBinding) {
|
||||
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
||||
return directiveTypeOrBinding;
|
||||
}
|
||||
var meta = this._reader.read(directiveTypeOrBinding);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
compile(component: Type):Promise<ProtoView> {
|
||||
var protoView = this._compile(component);
|
||||
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>.
|
||||
// Used for bootstrapping.
|
||||
compileRoot(elementOrSelector, componentTypeOrBinding:any):Promise<AppProtoView> {
|
||||
return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => {
|
||||
return this._compileNestedProtoViews(null, rootRenderPv, [this._bindDirective(componentTypeOrBinding)], true);
|
||||
});
|
||||
}
|
||||
|
||||
compile(component: Type):Promise<AppProtoView> {
|
||||
var protoView = this._compile(this._bindDirective(component));
|
||||
return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
|
||||
}
|
||||
|
||||
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
|
||||
_compile(component: Type) {
|
||||
// TODO(vicb): union type return AppProtoView or Promise<AppProtoView>
|
||||
_compile(componentBinding: DirectiveBinding) {
|
||||
var component = componentBinding.key.token;
|
||||
var protoView = this._compilerCache.get(component);
|
||||
if (isPresent(protoView)) {
|
||||
// The component has already been compiled into a ProtoView,
|
||||
// returns a resolved Promise.
|
||||
// The component has already been compiled into an AppProtoView,
|
||||
// returns a plain AppProtoView, not wrapped inside of a Promise.
|
||||
// Needed for recursive components.
|
||||
return protoView;
|
||||
}
|
||||
|
||||
@ -125,84 +112,131 @@ export class Compiler {
|
||||
}
|
||||
|
||||
var template = this._templateResolver.resolve(component);
|
||||
var directives = ListWrapper.map(
|
||||
this._flattenDirectives(template),
|
||||
(directive) => this._bindDirective(directive)
|
||||
);
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||
});
|
||||
|
||||
var componentUrl = this._componentUrlMapper.getUrl(component);
|
||||
var baseUrl = this._urlResolver.resolve(this._appUrl, componentUrl);
|
||||
this._templateLoader.setBaseUrl(template, baseUrl);
|
||||
|
||||
var tplElement = this._templateLoader.load(template);
|
||||
|
||||
if (PromiseWrapper.isPromise(tplElement)) {
|
||||
pvPromise = PromiseWrapper.then(tplElement,
|
||||
(el) => this._compileTemplate(template, el, component),
|
||||
(_) => { throw new BaseException(`Failed to load the template for ${stringify(component)}`); }
|
||||
);
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
return pvPromise;
|
||||
}
|
||||
|
||||
return this._compileTemplate(template, tplElement, component);
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
return pvPromise;
|
||||
}
|
||||
|
||||
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
|
||||
_compileTemplate(template: Template, tplElement, component: Type) {
|
||||
var pipeline = new CompilePipeline(this.createSteps(component, template));
|
||||
var compileElements;
|
||||
|
||||
try {
|
||||
compileElements = pipeline.process(tplElement, stringify(component));
|
||||
} catch(ex) {
|
||||
return PromiseWrapper.reject(ex);
|
||||
}
|
||||
|
||||
var protoView = compileElements[0].inheritedProtoView;
|
||||
|
||||
// Populate the cache before compiling the nested components,
|
||||
// so that components can reference themselves in their template.
|
||||
this._compilerCache.set(component, protoView);
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
|
||||
// Compile all the components from the template
|
||||
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
|
||||
_compileNestedProtoViews(componentBinding, renderPv, directives, isComponentRootView) {
|
||||
var nestedPVPromises = [];
|
||||
for (var i = 0; i < compileElements.length; i++) {
|
||||
var ce = compileElements[i];
|
||||
if (ce.hasNestedView) {
|
||||
this._compileNestedProtoView(ce, nestedPVPromises);
|
||||
var protoView = this._protoViewFactory.createProtoView(componentBinding, renderPv, directives);
|
||||
if (isComponentRootView && isPresent(componentBinding)) {
|
||||
// Populate the cache before compiling the nested components,
|
||||
// so that components can reference themselves in their template.
|
||||
var component = componentBinding.key.token;
|
||||
this._compilerCache.set(component, protoView);
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
}
|
||||
|
||||
var binderIndex = 0;
|
||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
|
||||
var elementBinderDone = (nestedPv) => {
|
||||
elementBinder.nestedProtoView = nestedPv;
|
||||
// Can't set the parentProtoView for components,
|
||||
// as their AppProtoView might be used in multiple other components.
|
||||
nestedPv.parentProtoView = isPresent(nestedComponent) ? null : protoView;
|
||||
};
|
||||
var nestedCall = null;
|
||||
if (isPresent(nestedComponent)) {
|
||||
if (!(nestedComponent.annotation instanceof DynamicComponent)) {
|
||||
nestedCall = this._compile(nestedComponent);
|
||||
}
|
||||
} else if (isPresent(nestedRenderProtoView)) {
|
||||
nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (protoView.stylePromises.length > 0) {
|
||||
// The protoView is ready after all asynchronous styles are ready
|
||||
var syncProtoView = protoView;
|
||||
protoView = PromiseWrapper.all(syncProtoView.stylePromises).then((_) => syncProtoView);
|
||||
}
|
||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
||||
} else if (isPresent(nestedCall)) {
|
||||
elementBinderDone(nestedCall);
|
||||
}
|
||||
binderIndex++;
|
||||
});
|
||||
|
||||
var protoViewDone = (_) => {
|
||||
var childComponentRenderPvRefs = [];
|
||||
ListWrapper.forEach(protoView.elementBinders, (eb) => {
|
||||
if (isPresent(eb.componentDirective)) {
|
||||
var componentPv = eb.nestedProtoView;
|
||||
ListWrapper.push(childComponentRenderPvRefs, isPresent(componentPv) ? componentPv.render : null);
|
||||
}
|
||||
});
|
||||
this._renderer.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs);
|
||||
return protoView;
|
||||
};
|
||||
if (nestedPVPromises.length > 0) {
|
||||
// Returns ProtoView Promise when there are any asynchronous nested ProtoViews.
|
||||
// The promise will resolved after nested ProtoViews are compiled.
|
||||
return PromiseWrapper.then(PromiseWrapper.all(nestedPVPromises),
|
||||
(_) => protoView,
|
||||
(e) => { throw new BaseException(`${e.message} -> Failed to compile ${stringify(component)}`); }
|
||||
);
|
||||
}
|
||||
|
||||
return protoView;
|
||||
}
|
||||
|
||||
_compileNestedProtoView(ce: CompileElement, promises: List<Promise>) {
|
||||
var protoView = this._compile(ce.componentDirective.type);
|
||||
|
||||
if (PromiseWrapper.isPromise(protoView)) {
|
||||
ListWrapper.push(
|
||||
promises,
|
||||
protoView.then(function(pv) { ce.inheritedElementBinder.nestedProtoView = pv;})
|
||||
);
|
||||
return PromiseWrapper.all(nestedPVPromises).then(protoViewDone);
|
||||
} else {
|
||||
ce.inheritedElementBinder.nestedProtoView = protoView;
|
||||
return protoViewDone(null);
|
||||
}
|
||||
}
|
||||
|
||||
_flattenDirectives(template: Template):List<Type> {
|
||||
_buildRenderTemplate(component, view, directives) {
|
||||
var componentUrl = this._urlResolver.resolve(
|
||||
this._appUrl, this._componentUrlMapper.getUrl(component)
|
||||
);
|
||||
var templateAbsUrl = null;
|
||||
if (isPresent(view.templateUrl)) {
|
||||
templateAbsUrl = this._urlResolver.resolve(componentUrl, view.templateUrl);
|
||||
} else {
|
||||
// Note: If we have an inline template, we also need to send
|
||||
// the url for the component to the renderer so that it
|
||||
// is able to resolve urls in stylesheets.
|
||||
templateAbsUrl = componentUrl;
|
||||
}
|
||||
return new renderApi.ViewDefinition({
|
||||
componentId: stringify(component),
|
||||
absUrl: templateAbsUrl,
|
||||
template: view.template,
|
||||
directives: ListWrapper.map(directives, this._buildRenderDirective)
|
||||
});
|
||||
}
|
||||
|
||||
_buildRenderDirective(directiveBinding) {
|
||||
var ann = directiveBinding.annotation;
|
||||
var renderType;
|
||||
var compileChildren = true;
|
||||
if ((ann instanceof Component) || (ann instanceof DynamicComponent)) {
|
||||
renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE;
|
||||
} else if (ann instanceof Viewport) {
|
||||
renderType = renderApi.DirectiveMetadata.VIEWPORT_TYPE;
|
||||
} else if (ann instanceof Decorator) {
|
||||
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
|
||||
compileChildren = ann.compileChildren;
|
||||
}
|
||||
var setters = [];
|
||||
var readAttributes = [];
|
||||
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
|
||||
if (isPresent(dep.propSetterName)) {
|
||||
ListWrapper.push(setters, dep.propSetterName);
|
||||
}
|
||||
if (isPresent(dep.attributeName)) {
|
||||
ListWrapper.push(readAttributes, dep.attributeName);
|
||||
}
|
||||
});
|
||||
return new renderApi.DirectiveMetadata({
|
||||
id: stringify(directiveBinding.key.token),
|
||||
type: renderType,
|
||||
selector: ann.selector,
|
||||
compileChildren: compileChildren,
|
||||
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
|
||||
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
|
||||
setters: setters,
|
||||
readAttributes: readAttributes
|
||||
});
|
||||
}
|
||||
|
||||
_flattenDirectives(template: View):List<Type> {
|
||||
if (isBlank(template.directives)) return [];
|
||||
|
||||
var directives = [];
|
||||
@ -223,5 +257,3 @@ export class Compiler {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
import {CompileStep} from './pipeline/compile_step';
|
||||
import {CompileElement} from './pipeline/compile_element';
|
||||
import {CompileControl} from './pipeline/compile_control';
|
||||
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
|
||||
/**
|
||||
* Processes the <style> tags during the compilation:
|
||||
* - Apply any given transformers,
|
||||
* - Apply the shadow DOM strategy style step.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CssProcessor {
|
||||
_transformers: List<CssTransformer>;
|
||||
|
||||
constructor(transformers: List<CssTransformer>) {
|
||||
this._transformers = transformers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a compile step to be added to the compiler pipeline.
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @param {ShadowDomStrategy} shadowDomStrategy
|
||||
* @param {string} templateUrl The base URL of the template
|
||||
*/
|
||||
getCompileStep(cmpMetadata: DirectiveMetadata, shadowDomStrategy: ShadowDomStrategy,
|
||||
templateUrl: string) {
|
||||
var strategyStep = shadowDomStrategy.getStyleCompileStep(cmpMetadata, templateUrl);
|
||||
return new _CssProcessorStep(strategyStep, this._transformers);
|
||||
}
|
||||
}
|
||||
|
||||
export class CssTransformer {
|
||||
transform(styleElement) {};
|
||||
}
|
||||
|
||||
class _CssProcessorStep extends CompileStep {
|
||||
_strategyStep: CompileStep;
|
||||
_transformers: List<CssTransformer>;
|
||||
|
||||
constructor(strategyStep: CompileStep, transformers: List<CssTransformer>) {
|
||||
super();
|
||||
this._strategyStep = strategyStep;
|
||||
this._transformers = transformers;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (DOM.tagName(current.element) == 'STYLE') {
|
||||
current.ignoreBindings = true;
|
||||
|
||||
if (isPresent(this._transformers)) {
|
||||
var styleEl = current.element;
|
||||
for (var i = 0; i < this._transformers.length; i++) {
|
||||
this._transformers[i].transform(styleEl);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(this._strategyStep)) {
|
||||
this._strategyStep.process(parent, current, control);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||
import {ResolvedBinding} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* Combination of a type with the Directive annotation
|
||||
@ -7,9 +9,11 @@ import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||
export class DirectiveMetadata {
|
||||
type:Type;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
|
||||
constructor(type:Type, annotation:Directive) {
|
||||
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
|
||||
this.annotation = annotation;
|
||||
this.type = type;
|
||||
this.resolvedInjectables = resolvedInjectables;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Injectable, Injector} from 'angular2/di';
|
||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||
import {Directive} from '../annotations/annotations';
|
||||
import {Directive, Component} from '../annotations/annotations';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
@ -13,7 +13,11 @@ export class DirectiveMetadataReader {
|
||||
var annotation = annotations[i];
|
||||
|
||||
if (annotation instanceof Directive) {
|
||||
return new DirectiveMetadata(type, annotation);
|
||||
var resolvedInjectables = null;
|
||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||
resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||
}
|
||||
return new DirectiveMetadata(type, annotation, resolvedInjectables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
modules/angular2/src/core/compiler/dynamic_component_loader.js
vendored
Normal file
100
modules/angular2/src/core/compiler/dynamic_component_loader.js
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di'
|
||||
import {Compiler} from './compiler';
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {ElementRef, DirectiveRef, ComponentRef} from './element_injector';
|
||||
|
||||
/**
|
||||
* Service for dynamically loading a Component into an arbitrary position in the internal Angular
|
||||
* application tree.
|
||||
*/
|
||||
@Injectable()
|
||||
export class DynamicComponentLoader {
|
||||
_compiler:Compiler;
|
||||
_viewFactory:ViewFactory;
|
||||
_renderer:Renderer;
|
||||
_directiveMetadataReader:DirectiveMetadataReader;
|
||||
|
||||
constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader,
|
||||
renderer:Renderer, viewFactory:ViewFactory) {
|
||||
this._compiler = compiler;
|
||||
this._directiveMetadataReader = directiveMetadataReader;
|
||||
this._renderer = renderer;
|
||||
this._viewFactory = viewFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a component into the location given by the provided ElementRef. The loaded component
|
||||
* receives injection as if it in the place of the provided ElementRef.
|
||||
*/
|
||||
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
|
||||
var directiveMetadata = this._directiveMetadataReader.read(type);
|
||||
|
||||
var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables);
|
||||
|
||||
var hostEi = location.elementInjector;
|
||||
var hostView = location.hostView;
|
||||
|
||||
return this._compiler.compile(type).then(componentProtoView => {
|
||||
var context = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj);
|
||||
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context);
|
||||
|
||||
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
|
||||
//same problem exists on the render side
|
||||
hostView.addComponentChildView(componentView);
|
||||
|
||||
this._renderer.setDynamicComponentView(hostView.render, location.boundElementIndex, componentView.render);
|
||||
|
||||
// TODO(vsavkin): return a component ref that dehydrates the component view and removes it
|
||||
// from the component child views
|
||||
return new ComponentRef(Key.get(type), hostEi, componentView);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a component as a child of the View given by the provided ElementRef. The loaded
|
||||
* component receives injection normally as a hosted view.
|
||||
*
|
||||
* TODO(vsavkin, jelbourn): remove protoViewFactory after render layer exists.
|
||||
*/
|
||||
loadIntoNewLocation(elementOrSelector:any, type:Type, location:ElementRef,
|
||||
injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
|
||||
var inj = this._componentAppInjector(location, injector, null);
|
||||
|
||||
//TODO(tbosch) this should always be a selector
|
||||
return this._compiler.compileRoot(elementOrSelector, type).then(pv => {
|
||||
var hostView = this._instantiateAndHydrateView(pv, inj, null, new Object());
|
||||
|
||||
// TODO(vsavkin): return a component ref that dehydrates the host view
|
||||
return new ComponentRef(Key.get(type), hostView.elementInjectors[0], hostView.componentChildViews[0]);
|
||||
});
|
||||
}
|
||||
|
||||
_componentAppInjector(location, injector:Injector, resolvedBindings:List<ResolvedBinding>) {
|
||||
var inj = isPresent(injector) ? injector : location.injector;
|
||||
return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj;
|
||||
}
|
||||
|
||||
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {
|
||||
var componentView = this._viewFactory.getView(protoView);
|
||||
componentView.hydrate(injector, hostElementInjector, context, null);
|
||||
return componentView;
|
||||
}
|
||||
|
||||
/** Asserts that the type being dynamically instantiated is a Component. */
|
||||
_assertTypeIsComponent(type:Type) {
|
||||
var annotation = this._directiveMetadataReader.read(type).annotation;
|
||||
if (!(annotation instanceof Component)) {
|
||||
throw new BaseException(`Could not load '${stringify(type)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,22 @@
|
||||
import {int, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import * as eiModule from './element_injector';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {DirectiveBinding} from './element_injector';
|
||||
import {List, StringMap} from 'angular2/src/facade/collection';
|
||||
import * as viewModule from './view';
|
||||
|
||||
export class ElementBinder {
|
||||
protoElementInjector:eiModule.ProtoElementInjector;
|
||||
componentDirective:DirectiveMetadata;
|
||||
viewportDirective:DirectiveMetadata;
|
||||
textNodeIndices:List<int>;
|
||||
hasElementPropertyBindings:boolean;
|
||||
nestedProtoView: viewModule.ProtoView;
|
||||
events:StringMap;
|
||||
contentTagSelector:string;
|
||||
componentDirective:DirectiveBinding;
|
||||
viewportDirective:DirectiveBinding;
|
||||
nestedProtoView: viewModule.AppProtoView;
|
||||
hostListeners:StringMap;
|
||||
parent:ElementBinder;
|
||||
index:int;
|
||||
distanceToParent:int;
|
||||
constructor(
|
||||
index:int, parent:ElementBinder, distanceToParent: int,
|
||||
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveMetadata,
|
||||
viewportDirective:DirectiveMetadata) {
|
||||
index:int, parent:ElementBinder, distanceToParent: int,
|
||||
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveBinding,
|
||||
viewportDirective:DirectiveBinding) {
|
||||
if (isBlank(index)) {
|
||||
throw new BaseException('null index not allowed.');
|
||||
}
|
||||
@ -31,14 +28,8 @@ export class ElementBinder {
|
||||
this.index = index;
|
||||
this.distanceToParent = distanceToParent;
|
||||
// updated later when events are bound
|
||||
this.events = null;
|
||||
// updated later when text nodes are bound
|
||||
this.textNodeIndices = null;
|
||||
// updated later when element properties are bound
|
||||
this.hasElementPropertyBindings = false;
|
||||
this.hostListeners = null;
|
||||
// updated later, so we are able to resolve cycles
|
||||
this.nestedProtoView = null;
|
||||
// updated later in the compilation pipeline
|
||||
this.contentTagSelector = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
import {isPresent, isBlank, Type, int, BaseException} from 'angular2/src/facade/lang';
|
||||
import {Math} from 'angular2/src/facade/math';
|
||||
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, ResolvedBinding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
|
||||
import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {NgElement} from 'angular2/src/core/dom/element';
|
||||
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
|
||||
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';
|
||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||
import {BindingPropagationConfig} from 'angular2/change_detection';
|
||||
import {QueryList} from './query_list';
|
||||
|
||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||
|
||||
@ -20,20 +19,62 @@ var _undefined = new Object();
|
||||
|
||||
var _staticKeys;
|
||||
|
||||
export class ElementRef {
|
||||
elementInjector:ElementInjector;
|
||||
|
||||
constructor(elementInjector:ElementInjector){
|
||||
this.elementInjector = elementInjector;
|
||||
}
|
||||
|
||||
get hostView() {
|
||||
return this.elementInjector._preBuiltObjects.view;
|
||||
}
|
||||
|
||||
get injector() {
|
||||
return this.elementInjector._lightDomAppInjector;
|
||||
}
|
||||
|
||||
get boundElementIndex() {
|
||||
return this.elementInjector._proto.index;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveRef extends ElementRef {
|
||||
_key:Key;
|
||||
|
||||
constructor(key:Key, elementInjector:ElementInjector){
|
||||
super(elementInjector);
|
||||
this._key = key;
|
||||
}
|
||||
|
||||
get instance() {
|
||||
return this.elementInjector.get(this._key);
|
||||
}
|
||||
}
|
||||
|
||||
export class ComponentRef extends DirectiveRef {
|
||||
componentView:viewModule.AppView;
|
||||
|
||||
constructor(key:Key, elementInjector:ElementInjector, componentView:viewModule.AppView){
|
||||
super(key, elementInjector);
|
||||
this.componentView = componentView;
|
||||
}
|
||||
}
|
||||
|
||||
class StaticKeys {
|
||||
viewId:number;
|
||||
ngElementId:number;
|
||||
viewContainerId:number;
|
||||
bindingPropagationConfigId:number;
|
||||
privateComponentLocationId:number;
|
||||
directiveRefId:number;
|
||||
|
||||
constructor() {
|
||||
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||
this.viewId = Key.get(viewModule.View).id;
|
||||
//TODO: vsavkin Key.annotate(Key.get(AppView), 'static')
|
||||
this.viewId = Key.get(viewModule.AppView).id;
|
||||
this.ngElementId = Key.get(NgElement).id;
|
||||
this.viewContainerId = Key.get(ViewContainer).id;
|
||||
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
||||
this.privateComponentLocationId = Key.get(pclModule.PrivateComponentLocation).id;
|
||||
this.directiveRefId = Key.get(DirectiveRef).id;
|
||||
}
|
||||
|
||||
static instance() {
|
||||
@ -42,39 +83,123 @@ class StaticKeys {
|
||||
}
|
||||
}
|
||||
|
||||
class TreeNode {
|
||||
export class TreeNode {
|
||||
_parent:TreeNode;
|
||||
_head:TreeNode;
|
||||
_tail:TreeNode;
|
||||
_next:TreeNode;
|
||||
constructor(parent:TreeNode) {
|
||||
this._parent = parent;
|
||||
this._head = null;
|
||||
this._tail = null;
|
||||
this._next = null;
|
||||
if (isPresent(parent)) parent._addChild(this);
|
||||
if (isPresent(parent)) parent.addChild(this);
|
||||
}
|
||||
|
||||
_assertConsistency() {
|
||||
this._assertHeadBeforeTail();
|
||||
this._assertTailReachable();
|
||||
this._assertPresentInParentList();
|
||||
}
|
||||
|
||||
_assertHeadBeforeTail() {
|
||||
if (isBlank(this._tail) && isPresent(this._head)) throw new BaseException('null tail but non-null head');
|
||||
}
|
||||
|
||||
_assertTailReachable() {
|
||||
if (isBlank(this._tail)) return;
|
||||
if (isPresent(this._tail._next)) throw new BaseException('node after tail');
|
||||
var p = this._head;
|
||||
while (isPresent(p) && p != this._tail) p = p._next;
|
||||
if (isBlank(p) && isPresent(this._tail)) throw new BaseException('tail not reachable.')
|
||||
}
|
||||
|
||||
_assertPresentInParentList() {
|
||||
var p = this._parent;
|
||||
if (isBlank(p)) {
|
||||
return;
|
||||
}
|
||||
var cur = p._head;
|
||||
while (isPresent(cur) && cur != this) cur = cur._next;
|
||||
if (isBlank(cur)) throw new BaseException('node not reachable through parent.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child to the parent node. The child MUST NOT be a part of a tree.
|
||||
*/
|
||||
_addChild(child:TreeNode) {
|
||||
addChild(child:TreeNode) {
|
||||
if (isPresent(this._tail)) {
|
||||
this._tail._next = child;
|
||||
this._tail = child;
|
||||
} else {
|
||||
this._tail = this._head = child;
|
||||
}
|
||||
child._next = null;
|
||||
child._parent = this;
|
||||
this._assertConsistency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child to the parent node after a given sibling.
|
||||
* The child MUST NOT be a part of a tree and the sibling must be present.
|
||||
*/
|
||||
addChildAfter(child:TreeNode, prevSibling:TreeNode) {
|
||||
this._assertConsistency();
|
||||
if (isBlank(prevSibling)) {
|
||||
var prevHead = this._head;
|
||||
this._head = child;
|
||||
child._next = prevHead;
|
||||
if (isBlank(this._tail)) this._tail = child;
|
||||
} else if (isBlank(prevSibling._next)) {
|
||||
this.addChild(child);
|
||||
return;
|
||||
} else {
|
||||
prevSibling._assertPresentInParentList();
|
||||
child._next = prevSibling._next;
|
||||
prevSibling._next = child;
|
||||
}
|
||||
child._parent = this;
|
||||
this._assertConsistency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches a node from the parent's tree.
|
||||
*/
|
||||
remove() {
|
||||
this._assertConsistency();
|
||||
if (isBlank(this.parent)) return;
|
||||
var nextSibling = this._next;
|
||||
var prevSibling = this._findPrev();
|
||||
if (isBlank(prevSibling)) {
|
||||
this.parent._head = this._next;
|
||||
} else {
|
||||
prevSibling._next = this._next;
|
||||
}
|
||||
if (isBlank(nextSibling)) {
|
||||
this._parent._tail = prevSibling;
|
||||
}
|
||||
this._parent._assertConsistency();
|
||||
this._parent = null;
|
||||
this._next = null;
|
||||
this._assertConsistency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a previous sibling or returns null if first child.
|
||||
* Assumes the node has a parent.
|
||||
* TODO(rado): replace with DoublyLinkedList to avoid O(n) here.
|
||||
*/
|
||||
_findPrev() {
|
||||
var node = this.parent._head;
|
||||
if (node == this) return null;
|
||||
while (node._next !== this) node = node._next;
|
||||
return node;
|
||||
}
|
||||
|
||||
get parent() {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
set parent(node:TreeNode) {
|
||||
this._parent = node;
|
||||
}
|
||||
|
||||
// TODO(rado): replace with a function call, does too much work for a getter.
|
||||
get children() {
|
||||
var res = [];
|
||||
var child = this._head;
|
||||
@ -90,56 +215,95 @@ export class DirectiveDependency extends Dependency {
|
||||
depth:int;
|
||||
eventEmitterName:string;
|
||||
propSetterName:string;
|
||||
attributeName:string;
|
||||
queryDirective;
|
||||
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
|
||||
properties:List, depth:int, eventEmitterName: string, propSetterName: string) {
|
||||
properties:List, depth:int, eventEmitterName: string,
|
||||
propSetterName: string, attributeName:string, queryDirective) {
|
||||
super(key, asPromise, lazy, optional, properties);
|
||||
this.depth = depth;
|
||||
this.eventEmitterName = eventEmitterName;
|
||||
this.propSetterName = propSetterName;
|
||||
this.attributeName = attributeName;
|
||||
this.queryDirective = queryDirective;
|
||||
this._verify();
|
||||
}
|
||||
|
||||
_verify() {
|
||||
var count = 0;
|
||||
if (isPresent(this.eventEmitterName)) count++;
|
||||
if (isPresent(this.propSetterName)) count++;
|
||||
if (isPresent(this.queryDirective)) count++;
|
||||
if (isPresent(this.attributeName)) count++;
|
||||
if (count > 1) throw new BaseException(
|
||||
'A directive injectable can contain only one of the following @EventEmitter, @PropertySetter, @Attribute or @Query.');
|
||||
}
|
||||
|
||||
static createFrom(d:Dependency):Dependency {
|
||||
var depth = 0;
|
||||
var eventName = null;
|
||||
var propName = null;
|
||||
var properties = d.properties;
|
||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
||||
d.properties, DirectiveDependency._depth(d.properties),
|
||||
DirectiveDependency._eventEmitterName(d.properties),
|
||||
DirectiveDependency._propSetterName(d.properties),
|
||||
DirectiveDependency._attributeName(d.properties),
|
||||
DirectiveDependency._query(d.properties)
|
||||
);
|
||||
}
|
||||
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var property = properties[i];
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties, depth,
|
||||
eventName, propName);
|
||||
static _eventEmitterName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof EventEmitter);
|
||||
return isPresent(p) ? p.eventName : null;
|
||||
}
|
||||
|
||||
static _propSetterName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof PropertySetter);
|
||||
return isPresent(p) ? p.propName : null;
|
||||
}
|
||||
|
||||
static _attributeName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
|
||||
return isPresent(p) ? p.attributeName : null;
|
||||
}
|
||||
|
||||
static _query(properties) {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof Query);
|
||||
return isPresent(p) ? p.directive : null;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveBinding extends Binding {
|
||||
export class DirectiveBinding extends ResolvedBinding {
|
||||
callOnDestroy:boolean;
|
||||
callOnChange:boolean;
|
||||
callOnAllChangesDone:boolean;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
|
||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
||||
super(key, factory, dependencies, providedAsPromise);
|
||||
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
|
||||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
||||
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
||||
this.annotation = annotation;
|
||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||
this.resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||
}
|
||||
}
|
||||
|
||||
static createFromBinding(b:Binding, annotation:Directive):Binding {
|
||||
var deps = ListWrapper.map(b.dependencies, DirectiveDependency.createFrom);
|
||||
return new DirectiveBinding(b.key, b.factory, deps, b.providedAsPromise, annotation);
|
||||
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
|
||||
var rb = b.resolve();
|
||||
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
|
||||
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, annotation);
|
||||
}
|
||||
|
||||
static createFromType(type:Type, annotation:Directive):Binding {
|
||||
var binding = bind(type).toClass(type);
|
||||
static createFromType(type:Type, annotation:Directive):DirectiveBinding {
|
||||
var binding = new Binding(type, {toClass: type});
|
||||
return DirectiveBinding.createFromBinding(binding, annotation);
|
||||
}
|
||||
|
||||
@ -150,7 +314,7 @@ export class DirectiveBinding extends Binding {
|
||||
|
||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||
export class PreBuiltObjects {
|
||||
view:viewModule.View;
|
||||
view:viewModule.AppView;
|
||||
element:NgElement;
|
||||
viewContainer:ViewContainer;
|
||||
bindingPropagationConfig:BindingPropagationConfig;
|
||||
@ -184,16 +348,16 @@ ElementInjector:
|
||||
*/
|
||||
|
||||
export class ProtoElementInjector {
|
||||
_binding0:Binding;
|
||||
_binding1:Binding;
|
||||
_binding2:Binding;
|
||||
_binding3:Binding;
|
||||
_binding4:Binding;
|
||||
_binding5:Binding;
|
||||
_binding6:Binding;
|
||||
_binding7:Binding;
|
||||
_binding8:Binding;
|
||||
_binding9:Binding;
|
||||
_binding0:ResolvedBinding;
|
||||
_binding1:ResolvedBinding;
|
||||
_binding2:ResolvedBinding;
|
||||
_binding3:ResolvedBinding;
|
||||
_binding4:ResolvedBinding;
|
||||
_binding5:ResolvedBinding;
|
||||
_binding6:ResolvedBinding;
|
||||
_binding7:ResolvedBinding;
|
||||
_binding8:ResolvedBinding;
|
||||
_binding9:ResolvedBinding;
|
||||
_binding0IsComponent:boolean;
|
||||
_keyId0:int;
|
||||
_keyId1:int;
|
||||
@ -207,8 +371,11 @@ export class ProtoElementInjector {
|
||||
_keyId9:int;
|
||||
parent:ProtoElementInjector;
|
||||
index:int;
|
||||
view:viewModule.View;
|
||||
view:viewModule.AppView;
|
||||
distanceToParent:number;
|
||||
attributes:Map;
|
||||
|
||||
numberOfDirectives:number;
|
||||
|
||||
/** Whether the element is exported as $implicit. */
|
||||
exportElement:boolean;
|
||||
@ -238,6 +405,7 @@ export class ProtoElementInjector {
|
||||
this._binding8 = null; this._keyId8 = null;
|
||||
this._binding9 = null; this._keyId9 = null;
|
||||
|
||||
this.numberOfDirectives = bindings.length;
|
||||
var length = bindings.length;
|
||||
|
||||
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
|
||||
@ -255,8 +423,8 @@ export class ProtoElementInjector {
|
||||
}
|
||||
}
|
||||
|
||||
instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector {
|
||||
return new ElementInjector(this, parent, host);
|
||||
instantiate(parent:ElementInjector):ElementInjector {
|
||||
return new ElementInjector(this, parent);
|
||||
}
|
||||
|
||||
directParent(): ProtoElementInjector {
|
||||
@ -276,6 +444,20 @@ export class ProtoElementInjector {
|
||||
return isPresent(this._binding0);
|
||||
}
|
||||
|
||||
getDirectiveBindingAtIndex(index:int) {
|
||||
if (index == 0) return this._binding0;
|
||||
if (index == 1) return this._binding1;
|
||||
if (index == 2) return this._binding2;
|
||||
if (index == 3) return this._binding3;
|
||||
if (index == 4) return this._binding4;
|
||||
if (index == 5) return this._binding5;
|
||||
if (index == 6) return this._binding6;
|
||||
if (index == 7) return this._binding7;
|
||||
if (index == 8) return this._binding8;
|
||||
if (index == 9) return this._binding9;
|
||||
throw new OutOfBoundsAccess(index);
|
||||
}
|
||||
|
||||
hasEventEmitter(eventName: string) {
|
||||
var p = this;
|
||||
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
|
||||
@ -297,6 +479,8 @@ export class ElementInjector extends TreeNode {
|
||||
_lightDomAppInjector:Injector;
|
||||
_shadowDomAppInjector:Injector;
|
||||
_host:ElementInjector;
|
||||
|
||||
// If this element injector has a component, the component instance will be stored in _obj0
|
||||
_obj0:any;
|
||||
_obj1:any;
|
||||
_obj2:any;
|
||||
@ -309,21 +493,17 @@ export class ElementInjector extends TreeNode {
|
||||
_obj9:any;
|
||||
_preBuiltObjects;
|
||||
_constructionCounter;
|
||||
_privateComponent;
|
||||
_privateComponentBinding:DirectiveBinding;
|
||||
|
||||
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) {
|
||||
_dynamicallyCreatedComponent:any;
|
||||
_dynamicallyCreatedComponentBinding:DirectiveBinding;
|
||||
|
||||
// Queries are added during construction or linking with a new parent.
|
||||
// They are never removed.
|
||||
_query0: QueryRef;
|
||||
_query1: QueryRef;
|
||||
_query2: QueryRef;
|
||||
constructor(proto:ProtoElementInjector, parent:ElementInjector) {
|
||||
super(parent);
|
||||
if (isPresent(parent) && isPresent(host)) {
|
||||
throw new BaseException('Only either parent or host is allowed');
|
||||
}
|
||||
this._host = null; // needed to satisfy Dart
|
||||
if (isPresent(parent)) {
|
||||
this._host = parent._host;
|
||||
} else {
|
||||
this._host = host;
|
||||
}
|
||||
|
||||
this._proto = proto;
|
||||
|
||||
//we cannot call clearDirectives because fields won't be detected
|
||||
@ -341,9 +521,13 @@ export class ElementInjector extends TreeNode {
|
||||
this._obj8 = null;
|
||||
this._obj9 = null;
|
||||
this._constructionCounter = 0;
|
||||
|
||||
this._inheritQueries(parent);
|
||||
this._buildQueries();
|
||||
}
|
||||
|
||||
clearDirectives() {
|
||||
this._host = null;
|
||||
this._preBuiltObjects = null;
|
||||
this._lightDomAppInjector = null;
|
||||
this._shadowDomAppInjector = null;
|
||||
@ -360,8 +544,8 @@ export class ElementInjector extends TreeNode {
|
||||
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
|
||||
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
|
||||
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
|
||||
if (isPresent(this._privateComponentBinding) && this._privateComponentBinding.callOnDestroy) {
|
||||
this._privateComponent.onDestroy();
|
||||
if (isPresent(this._dynamicallyCreatedComponentBinding) && this._dynamicallyCreatedComponentBinding.callOnDestroy) {
|
||||
this._dynamicallyCreatedComponent.onDestroy();
|
||||
}
|
||||
|
||||
this._obj0 = null;
|
||||
@ -374,12 +558,14 @@ export class ElementInjector extends TreeNode {
|
||||
this._obj7 = null;
|
||||
this._obj8 = null;
|
||||
this._obj9 = null;
|
||||
this._privateComponent = null;
|
||||
this._dynamicallyCreatedComponent = null;
|
||||
this._dynamicallyCreatedComponentBinding = null;
|
||||
|
||||
this._constructionCounter = 0;
|
||||
}
|
||||
|
||||
instantiateDirectives(lightDomAppInjector:Injector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) {
|
||||
instantiateDirectives(lightDomAppInjector:Injector, host:ElementInjector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) {
|
||||
this._host = host;
|
||||
this._checkShadowDomAppInjector(shadowDomAppInjector);
|
||||
|
||||
this._preBuiltObjects = preBuiltObjects;
|
||||
@ -397,15 +583,13 @@ export class ElementInjector extends TreeNode {
|
||||
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
|
||||
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
|
||||
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;
|
||||
dynamicallyCreateComponent(componentType:Type, annotation:Directive, injector:Injector) {
|
||||
this._shadowDomAppInjector = injector;
|
||||
this._dynamicallyCreatedComponentBinding = DirectiveBinding.createFromType(componentType, annotation);
|
||||
this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding);
|
||||
return this._dynamicallyCreatedComponent;
|
||||
}
|
||||
|
||||
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
|
||||
@ -417,22 +601,22 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
get(token) {
|
||||
if (this._isDynamicallyLoadedComponent(token)) {
|
||||
return this._dynamicallyCreatedComponent;
|
||||
}
|
||||
|
||||
return this._getByKey(Key.get(token), 0, false, null);
|
||||
}
|
||||
|
||||
_isDynamicallyLoadedComponent(token) {
|
||||
return isPresent(this._dynamicallyCreatedComponentBinding) &&
|
||||
Key.get(token) === this._dynamicallyCreatedComponentBinding.key;
|
||||
}
|
||||
|
||||
hasDirective(type:Type):boolean {
|
||||
return this._getDirectiveByKeyId(Key.get(type).id) !== _undefined;
|
||||
}
|
||||
|
||||
hasPreBuiltObject(type:Type):boolean {
|
||||
var pb = this._getPreBuiltObjectByKeyId(Key.get(type).id);
|
||||
return pb !== _undefined && isPresent(pb);
|
||||
}
|
||||
|
||||
forElement(el):boolean {
|
||||
return this._preBuiltObjects.element.domElement === el;
|
||||
}
|
||||
|
||||
/** Gets the NgElement associated with this ElementInjector */
|
||||
getNgElement() {
|
||||
return this._preBuiltObjects.element;
|
||||
@ -442,16 +626,12 @@ export class ElementInjector extends TreeNode {
|
||||
if (this._proto._binding0IsComponent) {
|
||||
return this._obj0;
|
||||
} else {
|
||||
throw new BaseException('There is not component stored in this ElementInjector');
|
||||
throw new BaseException('There is no component stored in this ElementInjector');
|
||||
}
|
||||
}
|
||||
|
||||
getPrivateComponent() {
|
||||
return this._privateComponent;
|
||||
}
|
||||
|
||||
getShadowDomAppInjector() {
|
||||
return this._shadowDomAppInjector;
|
||||
getDynamicallyLoadedComponent() {
|
||||
return this._dynamicallyCreatedComponent;
|
||||
}
|
||||
|
||||
directParent(): ElementInjector {
|
||||
@ -462,11 +642,12 @@ export class ElementInjector extends TreeNode {
|
||||
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
||||
}
|
||||
|
||||
_isPrivateComponentKey(key:Key) {
|
||||
return isPresent(this._privateComponentBinding) && key.id === this._privateComponentBinding.key.id;
|
||||
_isDynamicallyLoadedComponentKey(key:Key) {
|
||||
return isPresent(this._dynamicallyCreatedComponentBinding) && key.id ===
|
||||
this._dynamicallyCreatedComponentBinding.key.id;
|
||||
}
|
||||
|
||||
_new(binding:Binding) {
|
||||
_new(binding:ResolvedBinding) {
|
||||
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
|
||||
throw new CyclicDependencyError(binding.key);
|
||||
}
|
||||
@ -508,16 +689,24 @@ export class ElementInjector extends TreeNode {
|
||||
default: throw `Directive ${binding.key.token} can only have up to 10 dependencies.`;
|
||||
}
|
||||
|
||||
this._addToQueries(obj, binding.key.token);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
|
||||
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
|
||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||
if (dep.key.id === StaticKeys.instance().directiveRefId) {
|
||||
// TODO: we need store component view here and pass it to directive ref
|
||||
return new DirectiveRef(requestor, this);
|
||||
}
|
||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||
}
|
||||
|
||||
_buildEventEmitter(dep) {
|
||||
_buildEventEmitter(dep: DirectiveDependency) {
|
||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||
return (event) => {
|
||||
view.triggerEventHandlers(dep.eventEmitterName, event, this._proto.index);
|
||||
@ -525,10 +714,135 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
_buildPropSetter(dep) {
|
||||
var ngElement = this._getPreBuiltObjectByKeyId(StaticKeys.instance().ngElementId);
|
||||
var domElement = ngElement.domElement;
|
||||
var setter = reflector.setter(dep.propSetterName);
|
||||
return function(v) { setter(domElement, v) };
|
||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||
var renderer = view.proto.renderer;
|
||||
var index = this._proto.index;
|
||||
return function(v) {
|
||||
renderer.setElementProperty(view.render, index, dep.propSetterName, v);
|
||||
};
|
||||
}
|
||||
|
||||
_buildAttribute(dep): string {
|
||||
var attributes = this._proto.attributes;
|
||||
if (isPresent(attributes) && MapWrapper.contains(attributes, dep.attributeName)) {
|
||||
return MapWrapper.get(attributes, dep.attributeName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
_buildQueriesForDeps(deps: List<DirectiveDependency>) {
|
||||
for (var i = 0; i < deps.length; i++) {
|
||||
var dep = deps[i];
|
||||
if (isPresent(dep.queryDirective)) {
|
||||
this._createQueryRef(dep.queryDirective);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_createQueryRef(directive) {
|
||||
var queryList = new QueryList();
|
||||
if (isBlank(this._query0)) {this._query0 = new QueryRef(directive, queryList, this);}
|
||||
else if (isBlank(this._query1)) {this._query1 = new QueryRef(directive, queryList, this);}
|
||||
else if (isBlank(this._query2)) {this._query2 = new QueryRef(directive, queryList, this);}
|
||||
else throw new QueryError();
|
||||
}
|
||||
|
||||
_addToQueries(obj, token) {
|
||||
if (isPresent(this._query0) && (this._query0.directive === token)) {this._query0.list.add(obj);}
|
||||
if (isPresent(this._query1) && (this._query1.directive === token)) {this._query1.list.add(obj);}
|
||||
if (isPresent(this._query2) && (this._query2.directive === token)) {this._query2.list.add(obj);}
|
||||
}
|
||||
|
||||
// TODO(rado): unify with _addParentQueries.
|
||||
_inheritQueries(parent: ElementInjector) {
|
||||
if (isBlank(parent)) return;
|
||||
if (isPresent(parent._query0)) {this._query0 = parent._query0;}
|
||||
if (isPresent(parent._query1)) {this._query1 = parent._query1;}
|
||||
if (isPresent(parent._query2)) {this._query2 = parent._query2;}
|
||||
}
|
||||
|
||||
_buildQueries() {
|
||||
if (isBlank(this._proto)) return;
|
||||
var p = this._proto;
|
||||
if (isPresent(p._binding0)) {this._buildQueriesForDeps(p._binding0.dependencies);}
|
||||
if (isPresent(p._binding1)) {this._buildQueriesForDeps(p._binding1.dependencies);}
|
||||
if (isPresent(p._binding2)) {this._buildQueriesForDeps(p._binding2.dependencies);}
|
||||
if (isPresent(p._binding3)) {this._buildQueriesForDeps(p._binding3.dependencies);}
|
||||
if (isPresent(p._binding4)) {this._buildQueriesForDeps(p._binding4.dependencies);}
|
||||
if (isPresent(p._binding5)) {this._buildQueriesForDeps(p._binding5.dependencies);}
|
||||
if (isPresent(p._binding6)) {this._buildQueriesForDeps(p._binding6.dependencies);}
|
||||
if (isPresent(p._binding7)) {this._buildQueriesForDeps(p._binding7.dependencies);}
|
||||
if (isPresent(p._binding8)) {this._buildQueriesForDeps(p._binding8.dependencies);}
|
||||
if (isPresent(p._binding9)) {this._buildQueriesForDeps(p._binding9.dependencies);}
|
||||
}
|
||||
|
||||
_findQuery(token) {
|
||||
if (isPresent(this._query0) && this._query0.directive === token) {return this._query0;}
|
||||
if (isPresent(this._query1) && this._query1.directive === token) {return this._query1;}
|
||||
if (isPresent(this._query2) && this._query2.directive === token) {return this._query2;}
|
||||
throw new BaseException(`Cannot find query for directive ${token}.`);
|
||||
}
|
||||
|
||||
link(parent: ElementInjector) {
|
||||
parent.addChild(this);
|
||||
this._addParentQueries();
|
||||
}
|
||||
|
||||
linkAfter(parent: ElementInjector, prevSibling: ElementInjector) {
|
||||
parent.addChildAfter(this, prevSibling);
|
||||
this._addParentQueries();
|
||||
}
|
||||
|
||||
_addParentQueries() {
|
||||
if (isPresent(this.parent._query0)) {this._addQueryToTree(this.parent._query0); this.parent._query0.update();}
|
||||
if (isPresent(this.parent._query1)) {this._addQueryToTree(this.parent._query1); this.parent._query1.update();}
|
||||
if (isPresent(this.parent._query2)) {this._addQueryToTree(this.parent._query2); this.parent._query2.update();}
|
||||
}
|
||||
|
||||
unlink() {
|
||||
var queriesToUpDate = [];
|
||||
if (isPresent(this.parent._query0)) {this._pruneQueryFromTree(this.parent._query0); ListWrapper.push(queriesToUpDate, this.parent._query0);}
|
||||
if (isPresent(this.parent._query1)) {this._pruneQueryFromTree(this.parent._query1); ListWrapper.push(queriesToUpDate, this.parent._query1);}
|
||||
if (isPresent(this.parent._query2)) {this._pruneQueryFromTree(this.parent._query2); ListWrapper.push(queriesToUpDate, this.parent._query2);}
|
||||
|
||||
this.remove();
|
||||
|
||||
ListWrapper.forEach(queriesToUpDate, (q) => q.update());
|
||||
}
|
||||
|
||||
|
||||
_pruneQueryFromTree(query: QueryRef) {
|
||||
this._removeQueryRef(query);
|
||||
|
||||
var child = this._head;
|
||||
while (isPresent(child)) {
|
||||
child._pruneQueryFromTree(query);
|
||||
child = child._next;
|
||||
}
|
||||
}
|
||||
|
||||
_addQueryToTree(query: QueryRef) {
|
||||
this._assignQueryRef(query);
|
||||
|
||||
var child = this._head;
|
||||
while (isPresent(child)) {
|
||||
child._addQueryToTree(query);
|
||||
child = child._next;
|
||||
}
|
||||
}
|
||||
|
||||
_assignQueryRef(query: QueryRef) {
|
||||
if (isBlank(this._query0)) {this._query0 = query; return;}
|
||||
else if (isBlank(this._query1)) {this._query1 = query; return;}
|
||||
else if (isBlank(this._query2)) {this._query2 = query; return;}
|
||||
throw new QueryError();
|
||||
}
|
||||
|
||||
_removeQueryRef(query: QueryRef) {
|
||||
if (this._query0 == query) this._query0 = null;
|
||||
if (this._query1 == query) this._query1 = null;
|
||||
if (this._query2 == query) this._query2 = null;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -545,7 +859,6 @@ export class ElementInjector extends TreeNode {
|
||||
*/
|
||||
_getByKey(key:Key, depth:number, optional:boolean, requestor:Key) {
|
||||
var ei = this;
|
||||
|
||||
if (! this._shouldIncludeSelf(depth)) {
|
||||
depth -= ei._proto.distanceToParent;
|
||||
ei = ei._parent;
|
||||
@ -562,10 +875,11 @@ export class ElementInjector extends TreeNode {
|
||||
ei = ei._parent;
|
||||
}
|
||||
|
||||
|
||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||
return this._host.getComponent();
|
||||
} else if (isPresent(this._host) && this._host._isPrivateComponentKey(key)) {
|
||||
return this._host.getPrivateComponent();
|
||||
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
|
||||
return this._host.getDynamicallyLoadedComponent();
|
||||
} else if (optional) {
|
||||
return this._appInjector(requestor).getOptional(key);
|
||||
} else {
|
||||
@ -574,7 +888,7 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
_appInjector(requestor:Key) {
|
||||
if (isPresent(requestor) && this._isComponentKey(requestor)) {
|
||||
if (isPresent(requestor) && (this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
|
||||
return this._shadowDomAppInjector;
|
||||
} else {
|
||||
return this._lightDomAppInjector;
|
||||
@ -587,16 +901,12 @@ export class ElementInjector extends TreeNode {
|
||||
|
||||
_getPreBuiltObjectByKeyId(keyId:int) {
|
||||
var staticKeys = StaticKeys.instance();
|
||||
// TODO: View should not be injectable. Remove it.
|
||||
// TODO: AppView should not be injectable. Remove it.
|
||||
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
|
||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
||||
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
||||
|
||||
if (keyId === staticKeys.privateComponentLocationId) {
|
||||
return new pclModule.PrivateComponentLocation(this, this._preBuiltObjects.element, this._preBuiltObjects.view);
|
||||
}
|
||||
|
||||
//TODO add other objects as needed
|
||||
return _undefined;
|
||||
}
|
||||
@ -632,18 +942,7 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
getDirectiveBindingAtIndex(index:int) {
|
||||
var p = this._proto;
|
||||
if (index == 0) return p._binding0;
|
||||
if (index == 1) return p._binding1;
|
||||
if (index == 2) return p._binding2;
|
||||
if (index == 3) return p._binding3;
|
||||
if (index == 4) return p._binding4;
|
||||
if (index == 5) return p._binding5;
|
||||
if (index == 6) return p._binding6;
|
||||
if (index == 7) return p._binding7;
|
||||
if (index == 8) return p._binding8;
|
||||
if (index == 9) return p._binding9;
|
||||
throw new OutOfBoundsAccess(index);
|
||||
return this._proto.getDirectiveBindingAtIndex(index);
|
||||
}
|
||||
|
||||
hasInstances() {
|
||||
@ -681,3 +980,45 @@ class OutOfBoundsAccess extends Error {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class QueryError extends Error {
|
||||
message:string;
|
||||
// TODO(rado): pass the names of the active directives.
|
||||
constructor() {
|
||||
super();
|
||||
this.message = 'Only 3 queries can be concurrently active in a template.';
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class QueryRef {
|
||||
directive;
|
||||
list: QueryList;
|
||||
originator: ElementInjector;
|
||||
constructor(directive, list: QueryList, originator: ElementInjector) {
|
||||
this.directive = directive;
|
||||
this.list = list;
|
||||
this.originator = originator;
|
||||
}
|
||||
|
||||
update() {
|
||||
var aggregator = [];
|
||||
this.visit(this.originator, aggregator);
|
||||
this.list.reset(aggregator);
|
||||
}
|
||||
|
||||
visit(inj: ElementInjector, aggregator) {
|
||||
if (isBlank(inj)) return;
|
||||
if (inj.hasDirective(this.directive)) {
|
||||
ListWrapper.push(aggregator, inj.get(this.directive));
|
||||
}
|
||||
var child = inj._head;
|
||||
while (isPresent(child)) {
|
||||
this.visit(child, aggregator);
|
||||
child = child._next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @publicModule angular2/angular2
|
||||
* @exportedAs angular2/angular2
|
||||
*/
|
||||
export class OnChange {
|
||||
onChange(changes) {
|
||||
|
34
modules/angular2/src/core/compiler/ng_element.js
vendored
Normal file
34
modules/angular2/src/core/compiler/ng_element.js
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import * as viewModule from '../compiler/view';
|
||||
import {DirectDomViewRef} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
|
||||
/**
|
||||
* Allows direct access to the underlying DOM element.
|
||||
*
|
||||
* Attention: NgElement will be replaced by a different concept
|
||||
* for accessing an element in a way that is compatible with the render layer.
|
||||
*
|
||||
* @exportedAs angular2/core
|
||||
*/
|
||||
export class NgElement {
|
||||
_view:viewModule.AppView;
|
||||
_boundElementIndex:number;
|
||||
|
||||
constructor(view, boundElementIndex) {
|
||||
this._view = view;
|
||||
this._boundElementIndex = boundElementIndex;
|
||||
}
|
||||
|
||||
// TODO(tbosch): Here we expose the real DOM element.
|
||||
// We need a more general way to read/write to the DOM element
|
||||
// via a proper abstraction in the render layer
|
||||
get domElement() {
|
||||
var domViewRef:DirectDomViewRef = this._view.render;
|
||||
return domViewRef.delegate.boundElements[this._boundElementIndex];
|
||||
}
|
||||
|
||||
getAttribute(name:string) {
|
||||
return normalizeBlank(DOM.getAttribute(this.domElement, name));
|
||||
}
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
import {Decorator, Component, Viewport, DynamicComponent} from '../../annotations/annotations';
|
||||
import {ElementBinder} from '../element_binder';
|
||||
import {ProtoElementInjector} from '../element_injector';
|
||||
import * as viewModule from '../view';
|
||||
import {dashCaseToCamelCase} from './util';
|
||||
|
||||
import {AST} from 'angular2/change_detection';
|
||||
|
||||
/**
|
||||
* Collects all data that is needed to process an element
|
||||
* in the compile process. Fields are filled
|
||||
* by the CompileSteps starting out with the pure HTMLElement.
|
||||
*/
|
||||
export class CompileElement {
|
||||
element;
|
||||
_attrs:Map;
|
||||
_classList:List;
|
||||
textNodeBindings:Map;
|
||||
propertyBindings:Map;
|
||||
eventBindings:Map;
|
||||
|
||||
/// Store directive name to template name mapping.
|
||||
/// Directive name is what the directive exports the variable as
|
||||
/// Template name is how it is reffered to it in template
|
||||
variableBindings:Map;
|
||||
decoratorDirectives:List<DirectiveMetadata>;
|
||||
viewportDirective:DirectiveMetadata;
|
||||
componentDirective:DirectiveMetadata;
|
||||
hasNestedView:boolean;
|
||||
_allDirectives:List<DirectiveMetadata>;
|
||||
isViewRoot:boolean;
|
||||
hasBindings:boolean;
|
||||
inheritedProtoView:viewModule.ProtoView;
|
||||
inheritedProtoElementInjector:ProtoElementInjector;
|
||||
inheritedElementBinder:ElementBinder;
|
||||
distanceToParentInjector:int;
|
||||
distanceToParentBinder:int;
|
||||
compileChildren: boolean;
|
||||
ignoreBindings: boolean;
|
||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
||||
contentTagSelector: string;
|
||||
|
||||
constructor(element, compilationUnit = '') {
|
||||
this.element = element;
|
||||
this._attrs = null;
|
||||
this._classList = null;
|
||||
this.textNodeBindings = null;
|
||||
this.propertyBindings = null;
|
||||
this.eventBindings = null;
|
||||
this.variableBindings = null;
|
||||
this.decoratorDirectives = null;
|
||||
this.viewportDirective = null;
|
||||
this.componentDirective = null;
|
||||
this.hasNestedView = false;
|
||||
this._allDirectives = null;
|
||||
this.isViewRoot = false;
|
||||
this.hasBindings = false;
|
||||
// inherited down to children if they don't have
|
||||
// an own protoView
|
||||
this.inheritedProtoView = null;
|
||||
// inherited down to children if they don't have
|
||||
// an own protoElementInjector
|
||||
this.inheritedProtoElementInjector = null;
|
||||
// inherited down to children if they don't have
|
||||
// an own elementBinder
|
||||
this.inheritedElementBinder = null;
|
||||
this.distanceToParentInjector = 0;
|
||||
this.distanceToParentBinder = 0;
|
||||
this.compileChildren = true;
|
||||
// set to true to ignore all the bindings on the element
|
||||
this.ignoreBindings = false;
|
||||
this.contentTagSelector = null;
|
||||
// description is calculated here as compilation steps may change the element
|
||||
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
||||
if (compilationUnit !== '') {
|
||||
this.elementDescription = compilationUnit;
|
||||
if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
|
||||
} else {
|
||||
this.elementDescription = tplDesc;
|
||||
}
|
||||
}
|
||||
|
||||
refreshAttrs() {
|
||||
this._attrs = null;
|
||||
}
|
||||
|
||||
attrs():Map<string,string> {
|
||||
if (isBlank(this._attrs)) {
|
||||
this._attrs = DOM.attributeMap(this.element);
|
||||
}
|
||||
return this._attrs;
|
||||
}
|
||||
|
||||
refreshClassList() {
|
||||
this._classList = null;
|
||||
}
|
||||
|
||||
classList():List<string> {
|
||||
if (isBlank(this._classList)) {
|
||||
this._classList = ListWrapper.create();
|
||||
var elClassList = DOM.classList(this.element);
|
||||
for (var i = 0; i < elClassList.length; i++) {
|
||||
ListWrapper.push(this._classList, elClassList[i]);
|
||||
}
|
||||
}
|
||||
return this._classList;
|
||||
}
|
||||
|
||||
addTextNodeBinding(indexInParent:int, expression:AST) {
|
||||
if (isBlank(this.textNodeBindings)) {
|
||||
this.textNodeBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.textNodeBindings, indexInParent, expression);
|
||||
}
|
||||
|
||||
addPropertyBinding(property:string, expression:AST) {
|
||||
if (isBlank(this.propertyBindings)) {
|
||||
this.propertyBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
|
||||
}
|
||||
|
||||
addVariableBinding(variableName:string, variableValue:string) {
|
||||
if (isBlank(this.variableBindings)) {
|
||||
this.variableBindings = MapWrapper.create();
|
||||
}
|
||||
|
||||
// Store the variable map from value to variable, reflecting how it will be used later by
|
||||
// View. When a local is set to the view, a lookup for the variable name will take place keyed
|
||||
// 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
|
||||
// it.
|
||||
MapWrapper.set(this.variableBindings, variableValue, dashCaseToCamelCase(variableName));
|
||||
}
|
||||
|
||||
addEventBinding(eventName:string, expression:AST) {
|
||||
if (isBlank(this.eventBindings)) {
|
||||
this.eventBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.eventBindings, eventName, expression);
|
||||
}
|
||||
|
||||
addDirective(directive:DirectiveMetadata) {
|
||||
var annotation = directive.annotation;
|
||||
this._allDirectives = null;
|
||||
if (annotation instanceof Decorator) {
|
||||
if (isBlank(this.decoratorDirectives)) {
|
||||
this.decoratorDirectives = ListWrapper.create();
|
||||
}
|
||||
ListWrapper.push(this.decoratorDirectives, directive);
|
||||
if (!annotation.compileChildren) {
|
||||
this.compileChildren = false;
|
||||
}
|
||||
} else if (annotation instanceof Viewport) {
|
||||
this.viewportDirective = directive;
|
||||
} else if (annotation instanceof Component) {
|
||||
this.componentDirective = directive;
|
||||
this.hasNestedView = true;
|
||||
} else if (annotation instanceof DynamicComponent) {
|
||||
this.componentDirective = directive;
|
||||
}
|
||||
}
|
||||
|
||||
getAllDirectives(): List<DirectiveMetadata> {
|
||||
if (this._allDirectives === null) {
|
||||
// Collect all the directives
|
||||
// When present the component directive must be first
|
||||
var directives = ListWrapper.create();
|
||||
if (isPresent(this.componentDirective)) {
|
||||
ListWrapper.push(directives, this.componentDirective);
|
||||
}
|
||||
if (isPresent(this.viewportDirective)) {
|
||||
ListWrapper.push(directives, this.viewportDirective);
|
||||
}
|
||||
if (isPresent(this.decoratorDirectives)) {
|
||||
directives = ListWrapper.concat(directives, this.decoratorDirectives);
|
||||
}
|
||||
this._allDirectives = directives;
|
||||
}
|
||||
return this._allDirectives;
|
||||
}
|
||||
}
|
||||
|
||||
// return an HTML representation of an element start tag - without its content
|
||||
// this is used to give contextual information in case of errors
|
||||
function getElementDescription(domElement):string {
|
||||
var buf = new StringJoiner();
|
||||
var atts = DOM.attributeMap(domElement);
|
||||
|
||||
buf.add("<");
|
||||
buf.add(DOM.tagName(domElement).toLowerCase());
|
||||
|
||||
// show id and class first to ease element identification
|
||||
addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
|
||||
addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
|
||||
MapWrapper.forEach(atts, (attValue, attName) => {
|
||||
if (attName !== "id" && attName !== "class") {
|
||||
addDescriptionAttribute(buf, attName, attValue);
|
||||
}
|
||||
});
|
||||
|
||||
buf.add(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
function addDescriptionAttribute(buffer:StringJoiner, attName:string, attValue) {
|
||||
if (isPresent(attValue)) {
|
||||
if (attValue.length === 0) {
|
||||
buffer.add(' ' + attName);
|
||||
} else {
|
||||
buffer.add(' ' + attName + '="' + attValue + '"');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import {ChangeDetection, Parser} from 'angular2/change_detection';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {PropertyBindingParser} from './property_binding_parser';
|
||||
import {TextInterpolationParser} from './text_interpolation_parser';
|
||||
import {DirectiveParser} from './directive_parser';
|
||||
import {ViewSplitter} from './view_splitter';
|
||||
import {ElementBindingMarker} from './element_binding_marker';
|
||||
import {ProtoViewBuilder} from './proto_view_builder';
|
||||
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
|
||||
import {ElementBinderBuilder} from './element_binder_builder';
|
||||
|
||||
import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy, EmulatedScopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
|
||||
/**
|
||||
* Default steps used for compiling a template.
|
||||
* Takes in an HTMLElement and produces the ProtoViews,
|
||||
* ProtoElementInjectors and ElementBinders in the end.
|
||||
*/
|
||||
export function createDefaultSteps(
|
||||
changeDetection:ChangeDetection,
|
||||
parser:Parser,
|
||||
compiledComponent: DirectiveMetadata,
|
||||
directives: List<DirectiveMetadata>,
|
||||
shadowDomStrategy: ShadowDomStrategy,
|
||||
templateUrl: string,
|
||||
cssProcessor: CssProcessor) {
|
||||
|
||||
var steps = [
|
||||
new ViewSplitter(parser),
|
||||
cssProcessor.getCompileStep(compiledComponent, shadowDomStrategy, templateUrl),
|
||||
shadowDomStrategy.getTemplateCompileStep(compiledComponent),
|
||||
new PropertyBindingParser(parser),
|
||||
new DirectiveParser(directives),
|
||||
new TextInterpolationParser(parser),
|
||||
new ElementBindingMarker(),
|
||||
new ProtoViewBuilder(changeDetection, shadowDomStrategy),
|
||||
new ProtoElementInjectorBuilder(),
|
||||
new ElementBinderBuilder(parser),
|
||||
];
|
||||
|
||||
return steps;
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
import {isPresent, isBlank, BaseException, assertionsEnabled, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||
import {List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {SelectorMatcher} from '../selector';
|
||||
import {CssSelector} from '../selector';
|
||||
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
import {DynamicComponent, Component, Viewport} from '../../annotations/annotations';
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
import {isSpecialProperty} from './element_binder_builder';
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||
|
||||
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
|
||||
|
||||
/**
|
||||
* Parses the directives on a single element. Assumes ViewSplitter has already created
|
||||
* <template> elements for template directives.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#templateDirecitve
|
||||
* - CompileElement#componentDirective.
|
||||
*
|
||||
* Reads:
|
||||
* - CompileElement#propertyBindings (to find directives contained
|
||||
* in the property bindings)
|
||||
* - CompileElement#variableBindings (to find directives contained
|
||||
* in the variable bindings)
|
||||
*/
|
||||
export class DirectiveParser extends CompileStep {
|
||||
_selectorMatcher:SelectorMatcher;
|
||||
constructor(directives:List<DirectiveMetadata>) {
|
||||
super();
|
||||
var selector;
|
||||
|
||||
this._selectorMatcher = new SelectorMatcher();
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
var directiveMetadata = directives[i];
|
||||
selector=CssSelector.parse(directiveMetadata.annotation.selector);
|
||||
this._selectorMatcher.addSelectable(selector, directiveMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var classList = current.classList();
|
||||
|
||||
var cssSelector = new CssSelector();
|
||||
var nodeName = DOM.nodeName(current.element);
|
||||
cssSelector.setElement(nodeName);
|
||||
for (var i=0; i < classList.length; i++) {
|
||||
cssSelector.addClassName(classList[i]);
|
||||
}
|
||||
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
cssSelector.addAttribute(attrName, attrValue);
|
||||
});
|
||||
|
||||
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
||||
// only be present on <template> elements any more!
|
||||
var isTemplateElement = DOM.isTemplateElement(current.element);
|
||||
var matchedProperties; // StringMap - used in dev mode to store all properties that have been matched
|
||||
|
||||
this._selectorMatcher.match(cssSelector, (selector, directive) => {
|
||||
matchedProperties = updateMatchedProperties(matchedProperties, selector, directive);
|
||||
checkDirectiveValidity(directive, current, isTemplateElement);
|
||||
current.addDirective(directive);
|
||||
});
|
||||
|
||||
// raise error if some directives are missing
|
||||
checkMissingDirectives(current, matchedProperties, isTemplateElement);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate all the properties that are used or interpreted by all directives
|
||||
// those properties correspond to the directive selectors and the directive bindings
|
||||
function updateMatchedProperties(matchedProperties, selector, directive) {
|
||||
if (assertionsEnabled()) {
|
||||
var attrs = selector.attrs;
|
||||
if (!isPresent(matchedProperties)) {
|
||||
matchedProperties = StringMapWrapper.create();
|
||||
}
|
||||
if (isPresent(attrs)) {
|
||||
for (var idx = 0; idx<attrs.length; idx+=2) {
|
||||
// attribute name is stored on even indexes
|
||||
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(attrs[idx]), true);
|
||||
}
|
||||
}
|
||||
// some properties can be used by the directive, so we need to register them
|
||||
if (isPresent(directive.annotation) && isPresent(directive.annotation.bind)) {
|
||||
var bindMap = directive.annotation.bind;
|
||||
StringMapWrapper.forEach(bindMap, (value, key) => {
|
||||
// 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
|
||||
|
||||
// keep the property name and remove the pipe
|
||||
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
||||
if (isPresent(bindProp) && isPresent(bindProp[1])) {
|
||||
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(bindProp[1]), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return matchedProperties;
|
||||
}
|
||||
|
||||
// check if the directive is compatible with the current element
|
||||
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 (!isTemplateElement) {
|
||||
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
|
||||
`with template attribute - check ${current.elementDescription}`);
|
||||
} else if (isPresent(current.viewportDirective)) {
|
||||
throw new BaseException(`Only one viewport directive can be used per element - check ${current.elementDescription}`);
|
||||
}
|
||||
} else if (isTemplateElement) {
|
||||
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
|
||||
|
||||
} else if (isComponent && alreadyHasComponent) {
|
||||
throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`);
|
||||
}
|
||||
}
|
||||
|
||||
// validates that there is no missing directive - dev mode only
|
||||
function checkMissingDirectives(current, matchedProperties, isTemplateElement) {
|
||||
if (assertionsEnabled()) {
|
||||
var ppBindings=current.propertyBindings;
|
||||
if (isPresent(ppBindings)) {
|
||||
// check that each property corresponds to a real property or has been matched by a directive
|
||||
MapWrapper.forEach(ppBindings, (expression, prop) => {
|
||||
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
|
||||
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
|
||||
throw new BaseException(`Missing directive to handle '${camelCaseToDashCase(prop)}' in ${current.elementDescription}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// template only store directives as attribute when they are not bound to expressions
|
||||
// so we have to validate the expression case too (e.g. !if="condition")
|
||||
if (isTemplateElement && !current.isViewRoot && !isPresent(current.viewportDirective)) {
|
||||
throw new BaseException(`Missing directive to handle: ${current.elementDescription}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
import {int, isPresent, isBlank, Type, BaseException, StringWrapper, RegExpWrapper, isString, stringify} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ListWrapper, List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
import {Parser, ProtoChangeDetector} from 'angular2/change_detection';
|
||||
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||
|
||||
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
||||
|
||||
const ARIA_PREFIX = 'aria';
|
||||
var ariaSettersCache = StringMapWrapper.create();
|
||||
|
||||
function ariaSetterFactory(attrName:string) {
|
||||
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
||||
var ariaAttrName;
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
ariaAttrName = camelCaseToDashCase(attrName);
|
||||
setterFn = function(element, value) {
|
||||
if (isPresent(value)) {
|
||||
DOM.setAttribute(element, ariaAttrName, stringify(value));
|
||||
} else {
|
||||
DOM.removeAttribute(element, ariaAttrName);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
||||
}
|
||||
|
||||
return setterFn;
|
||||
}
|
||||
|
||||
const CLASS_PREFIX = 'class.';
|
||||
var classSettersCache = StringMapWrapper.create();
|
||||
|
||||
function classSetterFactory(className:string) {
|
||||
var setterFn = StringMapWrapper.get(classSettersCache, className);
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
setterFn = function(element, value) {
|
||||
if (value) {
|
||||
DOM.addClass(element, className);
|
||||
} else {
|
||||
DOM.removeClass(element, className);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(classSettersCache, className, setterFn);
|
||||
}
|
||||
|
||||
return setterFn;
|
||||
}
|
||||
|
||||
const STYLE_PREFIX = 'style.';
|
||||
var styleSettersCache = StringMapWrapper.create();
|
||||
|
||||
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
||||
var cacheKey = styleName + stylesuffix;
|
||||
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
||||
var dashCasedStyleName;
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||
setterFn = function(element, value) {
|
||||
var valAsStr;
|
||||
if (isPresent(value)) {
|
||||
valAsStr = stringify(value);
|
||||
DOM.setStyle(element, dashCasedStyleName, valAsStr + stylesuffix);
|
||||
} else {
|
||||
DOM.removeStyle(element, dashCasedStyleName);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
||||
}
|
||||
|
||||
return setterFn;
|
||||
}
|
||||
|
||||
const ROLE_ATTR = 'role';
|
||||
function roleSetter(element, value) {
|
||||
if (isString(value)) {
|
||||
DOM.setAttribute(element, ROLE_ATTR, value);
|
||||
} else {
|
||||
DOM.removeAttribute(element, ROLE_ATTR);
|
||||
if (isPresent(value)) {
|
||||
throw new BaseException("Invalid role attribute, only string values are allowed, got '" + stringify(value) + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tells if an attribute is handled by the ElementBinderBuilder step
|
||||
export function isSpecialProperty(propName:string) {
|
||||
return StringWrapper.startsWith(propName, ARIA_PREFIX)
|
||||
|| StringWrapper.startsWith(propName, CLASS_PREFIX)
|
||||
|| StringWrapper.startsWith(propName, STYLE_PREFIX)
|
||||
|| StringMapWrapper.contains(DOM.attrToPropMap, propName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ElementBinders and adds watches to the
|
||||
* ProtoChangeDetector.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#inheritedElementBinder
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent) CompileElement#inheritedElementBinder
|
||||
* - CompileElement#hasBindings
|
||||
* - CompileElement#inheritedProtoView
|
||||
* - CompileElement#inheritedProtoElementInjector
|
||||
* - CompileElement#textNodeBindings
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#viewportDirective
|
||||
*
|
||||
* Note: This actually only needs the CompileElements with the flags
|
||||
* `hasBindings` and `isViewRoot`,
|
||||
* and only needs the actual HTMLElement for the ones
|
||||
* with the flag `isViewRoot`.
|
||||
*/
|
||||
export class ElementBinderBuilder extends CompileStep {
|
||||
_parser:Parser;
|
||||
constructor(parser:Parser) {
|
||||
super();
|
||||
this._parser = parser;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var elementBinder = null;
|
||||
var parentElementBinder = null;
|
||||
var distanceToParentBinder = this._getDistanceToParentBinder(parent, current);
|
||||
if (isPresent(parent)) {
|
||||
parentElementBinder = parent.inheritedElementBinder;
|
||||
}
|
||||
if (current.hasBindings) {
|
||||
var protoView = current.inheritedProtoView;
|
||||
var protoInjectorWasBuilt = isBlank(parent) ? true :
|
||||
current.inheritedProtoElementInjector !== parent.inheritedProtoElementInjector;
|
||||
|
||||
var currentProtoElementInjector = protoInjectorWasBuilt ?
|
||||
current.inheritedProtoElementInjector : null;
|
||||
|
||||
elementBinder = protoView.bindElement(parentElementBinder, distanceToParentBinder,
|
||||
currentProtoElementInjector, current.componentDirective, current.viewportDirective);
|
||||
current.distanceToParentBinder = 0;
|
||||
|
||||
if (isPresent(current.textNodeBindings)) {
|
||||
this._bindTextNodes(protoView, current);
|
||||
}
|
||||
if (isPresent(current.propertyBindings)) {
|
||||
this._bindElementProperties(protoView, current);
|
||||
}
|
||||
if (isPresent(current.eventBindings)) {
|
||||
this._bindEvents(protoView, 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)) {
|
||||
elementBinder = parentElementBinder;
|
||||
current.distanceToParentBinder = distanceToParentBinder;
|
||||
}
|
||||
current.inheritedElementBinder = elementBinder;
|
||||
}
|
||||
|
||||
_getDistanceToParentBinder(parent, current) {
|
||||
return isPresent(parent) ? parent.distanceToParentBinder + 1 : 0;
|
||||
}
|
||||
|
||||
_bindTextNodes(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
|
||||
protoView.bindTextNode(indexInParent, expression);
|
||||
});
|
||||
}
|
||||
|
||||
_bindElementProperties(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.propertyBindings, (expression, property) => {
|
||||
var setterFn, styleParts, styleSuffix;
|
||||
|
||||
if (StringWrapper.startsWith(property, ARIA_PREFIX)) {
|
||||
setterFn = ariaSetterFactory(property);
|
||||
} else if (StringWrapper.equals(property, ROLE_ATTR)) {
|
||||
setterFn = roleSetter;
|
||||
} else if (StringWrapper.startsWith(property, CLASS_PREFIX)) {
|
||||
setterFn = classSetterFactory(StringWrapper.substring(property, CLASS_PREFIX.length));
|
||||
} else if (StringWrapper.startsWith(property, STYLE_PREFIX)) {
|
||||
styleParts = StringWrapper.split(property, DOT_REGEXP);
|
||||
styleSuffix = styleParts.length > 2 ? ListWrapper.get(styleParts, 2) : '';
|
||||
setterFn = styleSetterFactory(ListWrapper.get(styleParts, 1), styleSuffix);
|
||||
} else {
|
||||
property = this._resolvePropertyName(property);
|
||||
//TODO(pk): special casing innerHtml, see: https://github.com/angular/angular/issues/789
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(setterFn)) {
|
||||
protoView.bindElementProperty(expression.ast, property, setterFn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_bindEvents(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.eventBindings, (expression, eventName) => {
|
||||
protoView.bindEvent(eventName, expression);
|
||||
});
|
||||
}
|
||||
|
||||
_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>,
|
||||
compileElement: CompileElement) {
|
||||
var protoView = compileElement.inheritedProtoView;
|
||||
|
||||
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
|
||||
var directive = ListWrapper.get(directives, directiveIndex);
|
||||
var annotation = directive.annotation;
|
||||
if (isBlank(annotation.bind)) continue;
|
||||
StringMapWrapper.forEach(annotation.bind, (bindConfig, dirProp) => {
|
||||
var pipes = this._splitBindConfig(bindConfig);
|
||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||
|
||||
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
||||
MapWrapper.get(compileElement.propertyBindings, dashCaseToCamelCase(elProp)) :
|
||||
null;
|
||||
|
||||
if (isBlank(bindingAst)) {
|
||||
var attributeValue = MapWrapper.get(compileElement.attrs(), elProp);
|
||||
if (isPresent(attributeValue)) {
|
||||
bindingAst = this._parser.wrapLiteralPrimitive(attributeValue, compileElement.elementDescription);
|
||||
}
|
||||
}
|
||||
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
if (isPresent(bindingAst)) {
|
||||
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
||||
protoView.bindDirectiveProperty(
|
||||
directiveIndex,
|
||||
fullExpAstWithBindPipes,
|
||||
dirProp,
|
||||
reflector.setter(dashCaseToCamelCase(dirProp))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_splitBindConfig(bindConfig:string) {
|
||||
return ListWrapper.map(bindConfig.split('|'), (s) => s.trim());
|
||||
}
|
||||
|
||||
_resolvePropertyName(attrName:string) {
|
||||
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, attrName);
|
||||
return isPresent(mappedPropName) ? mappedPropName : attrName;
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
|
||||
/**
|
||||
* Marks elements that have bindings with a css class
|
||||
* and sets the CompileElement.hasBindings flag.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#hasBindings
|
||||
*
|
||||
* Reads:
|
||||
* - CompileElement#textNodeBindings
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#variableBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#viewportDirective
|
||||
*/
|
||||
export class ElementBindingMarker extends CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hasBindings =
|
||||
(isPresent(current.textNodeBindings) && MapWrapper.size(current.textNodeBindings)>0) ||
|
||||
(isPresent(current.propertyBindings) && MapWrapper.size(current.propertyBindings)>0) ||
|
||||
(isPresent(current.variableBindings) && MapWrapper.size(current.variableBindings)>0) ||
|
||||
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
|
||||
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
|
||||
isPresent(current.viewportDirective) ||
|
||||
isPresent(current.componentDirective) ||
|
||||
isPresent(current.contentTagSelector);
|
||||
|
||||
if (hasBindings) {
|
||||
var element = current.element;
|
||||
DOM.addClass(element, NG_BINDING_CLASS);
|
||||
current.hasBindings = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
import {isPresent, isBlank, RegExpWrapper, BaseException} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Parser, AST, ExpressionWithSource} from 'angular2/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
||||
// Group 1 = "bind"
|
||||
// Group 2 = "var"
|
||||
// Group 3 = "on"
|
||||
// Group 4 = the identifier after "bind", "var", or "on"
|
||||
// Group 5 = idenitifer inside square braces
|
||||
// Group 6 = identifier inside parenthesis
|
||||
// Group 7 = "#"
|
||||
// Group 8 = identifier after "#"
|
||||
var BIND_NAME_REGEXP = RegExpWrapper.create(
|
||||
'^(?:(?:(?:(bind)|(var)|(on))-(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\)|(#)(.+))$');
|
||||
|
||||
/**
|
||||
* Parses the property bindings on a single element.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#variableBindings
|
||||
*/
|
||||
export class PropertyBindingParser extends CompileStep {
|
||||
_parser:Parser;
|
||||
constructor(parser:Parser) {
|
||||
super();
|
||||
this._parser = parser;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
var attrs = current.attrs();
|
||||
var newAttrs = MapWrapper.create();
|
||||
var desc = current.elementDescription;
|
||||
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
||||
if (isPresent(bindParts)) {
|
||||
if (isPresent(bindParts[1])) {
|
||||
// match: bind-prop
|
||||
current.addPropertyBinding(bindParts[4], this._parseBinding(attrValue, desc));
|
||||
MapWrapper.set(newAttrs, bindParts[4], attrValue);
|
||||
} else if (isPresent(bindParts[2]) || isPresent(bindParts[7])) {
|
||||
// match: var-name / var-name="iden" / #name / #name="iden"
|
||||
var identifier = (isPresent(bindParts[4]) && bindParts[4] !== '') ?
|
||||
bindParts[4] : bindParts[8];
|
||||
var value = attrValue == '' ? '\$implicit' : attrValue;
|
||||
current.addVariableBinding(identifier, value);
|
||||
MapWrapper.set(newAttrs, identifier, value);
|
||||
} else if (isPresent(bindParts[3])) {
|
||||
// match: on-event
|
||||
current.addEventBinding(bindParts[4], this._parseAction(attrValue, desc));
|
||||
} else if (isPresent(bindParts[5])) {
|
||||
// match: [prop]
|
||||
current.addPropertyBinding(bindParts[5], this._parseBinding(attrValue, desc));
|
||||
MapWrapper.set(newAttrs, bindParts[5], attrValue);
|
||||
} else if (isPresent(bindParts[6])) {
|
||||
// match: (event)
|
||||
current.addEventBinding(bindParts[6], this._parseAction(attrValue, desc));
|
||||
}
|
||||
} else {
|
||||
var ast = this._parseInterpolation(attrValue, desc);
|
||||
if (isPresent(ast)) {
|
||||
current.addPropertyBinding(attrName, ast);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MapWrapper.forEach(newAttrs, (attrValue, attrName) => {
|
||||
MapWrapper.set(attrs, attrName, attrValue);
|
||||
});
|
||||
}
|
||||
|
||||
_parseInterpolation(input:string, location:string):AST {
|
||||
return this._parser.parseInterpolation(input, location);
|
||||
}
|
||||
|
||||
_parseBinding(input:string, location:string):AST {
|
||||
return this._parser.parseBinding(input, location);
|
||||
}
|
||||
|
||||
_parseAction(input:string, location:string):AST {
|
||||
return this._parser.parseAction(input, location);
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ProtoElementInjector, ComponentKeyMetaData, DirectiveBinding} from '../element_injector';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
|
||||
/**
|
||||
* Creates the ProtoElementInjectors.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#inheritedProtoElementInjector
|
||||
* - CompileElement#distanceToParentInjector
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent) CompileElement#inheritedProtoElementInjector
|
||||
* - (in parent) CompileElement#distanceToParentInjector
|
||||
* - CompileElement#isViewRoot
|
||||
* - CompileElement#inheritedProtoView
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#viewportDirective
|
||||
*/
|
||||
export class ProtoElementInjectorBuilder extends CompileStep {
|
||||
// public so that we can overwrite it in tests
|
||||
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance) {
|
||||
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance);
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
|
||||
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
||||
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
|
||||
|
||||
// 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
|
||||
// so that, when hydrating, $implicit can be set to the element.
|
||||
if (injectorBindings.length > 0 || isPresent(current.variableBindings)) {
|
||||
var protoView = current.inheritedProtoView;
|
||||
var hasComponent = isPresent(current.componentDirective);
|
||||
|
||||
current.inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
|
||||
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings,
|
||||
hasComponent, distanceToParentInjector
|
||||
);
|
||||
current.distanceToParentInjector = 0;
|
||||
|
||||
// Viewport directives are treated differently than other element with var- definitions.
|
||||
if (isPresent(current.variableBindings) && !isPresent(current.viewportDirective)) {
|
||||
current.inheritedProtoElementInjector.exportComponent = hasComponent;
|
||||
current.inheritedProtoElementInjector.exportElement = !hasComponent;
|
||||
|
||||
// experiment
|
||||
var exportImplicitName = MapWrapper.get(current.variableBindings, '\$implicit');
|
||||
if (isPresent(exportImplicitName)) {
|
||||
current.inheritedProtoElementInjector.exportImplicitName = exportImplicitName;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
current.inheritedProtoElementInjector = parentProtoElementInjector;
|
||||
current.distanceToParentInjector = distanceToParentInjector;
|
||||
}
|
||||
}
|
||||
|
||||
_getDistanceToParentInjector(parent, current) {
|
||||
return isPresent(parent) ? parent.distanceToParentInjector + 1 : 0;
|
||||
}
|
||||
|
||||
_getParentProtoElementInjector(parent, current) {
|
||||
if (isPresent(parent) && !current.isViewRoot) {
|
||||
return parent.inheritedProtoElementInjector;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_createBinding(d:DirectiveMetadata): DirectiveBinding {
|
||||
return DirectiveBinding.createFromType(d.type, d.annotation);
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ProtoView} from '../view';
|
||||
import {ChangeDetection} from 'angular2/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {ShadowDomStrategy} from '../shadow_dom_strategy';
|
||||
|
||||
/**
|
||||
* Creates ProtoViews and forwards variable bindings from parent to children.
|
||||
*
|
||||
* Fills:
|
||||
* - (in parent): CompileElement#inheritedElementBinder.nestedProtoView
|
||||
* - CompileElement#inheritedProtoView
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent): CompileElement#inheritedProtoView
|
||||
* - (in parent): CompileElement#variableBindings
|
||||
* - CompileElement#isViewRoot
|
||||
*/
|
||||
export class ProtoViewBuilder extends CompileStep {
|
||||
changeDetection:ChangeDetection;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
constructor(changeDetection:ChangeDetection, shadowDomStrategy:ShadowDomStrategy) {
|
||||
super();
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
this.changeDetection = changeDetection;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var inheritedProtoView = null;
|
||||
if (current.isViewRoot) {
|
||||
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
|
||||
inheritedProtoView = new ProtoView(current.element, protoChangeDetector,
|
||||
this._shadowDomStrategy, this._getParentProtoView(parent));
|
||||
|
||||
if (isPresent(parent)) {
|
||||
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
|
||||
throw new BaseException('Only one nested view per element is allowed');
|
||||
}
|
||||
parent.inheritedElementBinder.nestedProtoView = inheritedProtoView;
|
||||
|
||||
// When current is a view root, the variable bindings are set to the *nested* proto view.
|
||||
// The root view conceptually signifies a new "block scope" (the nested view), to which
|
||||
// the variables are bound.
|
||||
if (isPresent(parent.variableBindings)) {
|
||||
MapWrapper.forEach(parent.variableBindings, (mappedName, varName) => {
|
||||
inheritedProtoView.bindVariable(varName, mappedName);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (isPresent(parent)) {
|
||||
inheritedProtoView = parent.inheritedProtoView;
|
||||
}
|
||||
|
||||
// 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
|
||||
// to actually create variable bindings for the $implicit bindings, add to the
|
||||
// protoLocals manually.
|
||||
if (isPresent(current.variableBindings)) {
|
||||
MapWrapper.forEach(current.variableBindings, (mappedName, varName) => {
|
||||
MapWrapper.set(inheritedProtoView.protoLocals, mappedName, null);
|
||||
});
|
||||
}
|
||||
|
||||
current.inheritedProtoView = inheritedProtoView;
|
||||
}
|
||||
|
||||
_getParentProtoView(parent:CompileElement) {
|
||||
return isPresent(parent) ? parent.inheritedProtoView : null;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
187
modules/angular2/src/core/compiler/proto_view_factory.js
vendored
Normal file
187
modules/angular2/src/core/compiler/proto_view_factory.js
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
import {ChangeDetection} from 'angular2/change_detection';
|
||||
import {Component, Viewport, DynamicComponent} from '../annotations/annotations';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
import {AppProtoView} from './view';
|
||||
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
|
||||
|
||||
@Injectable()
|
||||
export class ProtoViewFactory {
|
||||
_changeDetection:ChangeDetection;
|
||||
_renderer:renderApi.Renderer;
|
||||
|
||||
constructor(changeDetection:ChangeDetection, renderer:renderApi.Renderer) {
|
||||
this._changeDetection = changeDetection;
|
||||
this._renderer = renderer;
|
||||
}
|
||||
|
||||
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
|
||||
var protoChangeDetector;
|
||||
if (isBlank(componentBinding)) {
|
||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
|
||||
} else {
|
||||
var componentAnnotation:Component = componentBinding.annotation;
|
||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector(
|
||||
'dummy', componentAnnotation.changeDetection
|
||||
);
|
||||
}
|
||||
var protoView = new AppProtoView(this._renderer, renderProtoView.render, protoChangeDetector);
|
||||
|
||||
for (var i=0; i<renderProtoView.elementBinders.length; i++) {
|
||||
var renderElementBinder = renderProtoView.elementBinders[i];
|
||||
var sortedDirectives = new SortedDirectives(renderElementBinder.directives, directives);
|
||||
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
|
||||
i, protoView.elementBinders, renderProtoView.elementBinders
|
||||
);
|
||||
var protoElementInjector = this._createProtoElementInjector(
|
||||
i, parentPeiWithDistance,
|
||||
sortedDirectives, renderElementBinder
|
||||
);
|
||||
this._createElementBinder(
|
||||
protoView, renderElementBinder, protoElementInjector, sortedDirectives
|
||||
);
|
||||
this._createDirectiveBinders(protoView, sortedDirectives);
|
||||
}
|
||||
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
||||
protoView.bindVariable(varName, mappedName);
|
||||
});
|
||||
return protoView;
|
||||
}
|
||||
|
||||
_findParentProtoElementInjectorWithDistance(binderIndex, elementBinders, renderElementBinders) {
|
||||
var distance = 0;
|
||||
do {
|
||||
var renderElementBinder = renderElementBinders[binderIndex];
|
||||
binderIndex = renderElementBinder.parentIndex;
|
||||
if (binderIndex !== -1) {
|
||||
distance += renderElementBinder.distanceToParent;
|
||||
var elementBinder = elementBinders[binderIndex];
|
||||
if (isPresent(elementBinder.protoElementInjector)) {
|
||||
return new ParentProtoElementInjectorWithDistance(elementBinder.protoElementInjector, distance);
|
||||
}
|
||||
}
|
||||
} while (binderIndex !== -1);
|
||||
return new ParentProtoElementInjectorWithDistance(null, -1);
|
||||
}
|
||||
|
||||
_createProtoElementInjector(binderIndex, parentPeiWithDistance, sortedDirectives, renderElementBinder) {
|
||||
var protoElementInjector = null;
|
||||
// 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
|
||||
// so that, when hydrating, $implicit can be set to the element.
|
||||
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
||||
if (sortedDirectives.directives.length > 0 || hasVariables) {
|
||||
protoElementInjector = new ProtoElementInjector(
|
||||
parentPeiWithDistance.protoElementInjector, binderIndex,
|
||||
sortedDirectives.directives,
|
||||
isPresent(sortedDirectives.componentDirective), parentPeiWithDistance.distance
|
||||
);
|
||||
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
||||
// Viewport directives are treated differently than other element with var- definitions.
|
||||
if (hasVariables && !isPresent(sortedDirectives.viewportDirective)) {
|
||||
protoElementInjector.exportComponent = isPresent(sortedDirectives.componentDirective);
|
||||
protoElementInjector.exportElement = isBlank(sortedDirectives.componentDirective);
|
||||
|
||||
// experiment
|
||||
var exportImplicitName = MapWrapper.get(renderElementBinder.variableBindings, '\$implicit');
|
||||
if (isPresent(exportImplicitName)) {
|
||||
protoElementInjector.exportImplicitName = exportImplicitName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return protoElementInjector;
|
||||
}
|
||||
|
||||
_createElementBinder(protoView, renderElementBinder, protoElementInjector, sortedDirectives) {
|
||||
var parent = null;
|
||||
if (renderElementBinder.parentIndex !== -1) {
|
||||
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
||||
}
|
||||
var elBinder = protoView.bindElement(
|
||||
parent,
|
||||
renderElementBinder.distanceToParent,
|
||||
protoElementInjector,
|
||||
sortedDirectives.componentDirective,
|
||||
sortedDirectives.viewportDirective
|
||||
);
|
||||
// text nodes
|
||||
for (var i=0; i<renderElementBinder.textBindings.length; i++) {
|
||||
protoView.bindTextNode(renderElementBinder.textBindings[i].ast);
|
||||
}
|
||||
// element properties
|
||||
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
|
||||
protoView.bindElementProperty(astWithSource.ast, propertyName);
|
||||
});
|
||||
// events
|
||||
protoView.bindEvent(renderElementBinder.eventBindings, -1);
|
||||
// variables
|
||||
// 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
|
||||
// to actually create variable bindings for the $implicit bindings, add to the
|
||||
// protoLocals manually.
|
||||
MapWrapper.forEach(renderElementBinder.variableBindings, (mappedName, varName) => {
|
||||
MapWrapper.set(protoView.protoLocals, mappedName, null);
|
||||
});
|
||||
return elBinder;
|
||||
}
|
||||
|
||||
_createDirectiveBinders(protoView, sortedDirectives) {
|
||||
for (var i=0; i<sortedDirectives.renderDirectives.length; i++) {
|
||||
var renderDirectiveMetadata = sortedDirectives.renderDirectives[i];
|
||||
// directive properties
|
||||
MapWrapper.forEach(renderDirectiveMetadata.propertyBindings, (astWithSource, propertyName) => {
|
||||
// TODO: these setters should eventually be created by change detection, to make
|
||||
// it monomorphic!
|
||||
var setter = reflector.setter(propertyName);
|
||||
protoView.bindDirectiveProperty(i, astWithSource.ast, propertyName, setter);
|
||||
});
|
||||
// directive events
|
||||
protoView.bindEvent(renderDirectiveMetadata.eventBindings, i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SortedDirectives {
|
||||
componentDirective: DirectiveBinding;
|
||||
viewportDirective: DirectiveBinding;
|
||||
renderDirectives: List<renderApi.DirectiveMetadata>;
|
||||
directives: List<DirectiveBinding>;
|
||||
|
||||
constructor(renderDirectives, allDirectives) {
|
||||
this.renderDirectives = [];
|
||||
this.directives = [];
|
||||
this.viewportDirective = null;
|
||||
this.componentDirective = null;
|
||||
ListWrapper.forEach(renderDirectives, (renderDirectiveMetadata) => {
|
||||
var directiveBinding = allDirectives[renderDirectiveMetadata.directiveIndex];
|
||||
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) {
|
||||
// component directives need to be the first binding in ElementInjectors!
|
||||
this.componentDirective = directiveBinding;
|
||||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveMetadata);
|
||||
ListWrapper.insert(this.directives, 0, directiveBinding);
|
||||
} else {
|
||||
if (directiveBinding.annotation instanceof Viewport) {
|
||||
this.viewportDirective = directiveBinding;
|
||||
}
|
||||
ListWrapper.push(this.renderDirectives, renderDirectiveMetadata);
|
||||
ListWrapper.push(this.directives, directiveBinding);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ParentProtoElementInjectorWithDistance {
|
||||
protoElementInjector:ProtoElementInjector;
|
||||
distance:number;
|
||||
constructor(protoElementInjector:ProtoElementInjector, distance:number) {
|
||||
this.protoElementInjector = protoElementInjector;
|
||||
this.distance = distance;
|
||||
}
|
||||
}
|
47
modules/angular2/src/core/compiler/query_list.dart
Normal file
47
modules/angular2/src/core/compiler/query_list.dart
Normal file
@ -0,0 +1,47 @@
|
||||
library angular2.src.core.compiler.query_list;
|
||||
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
import 'dart:collection';
|
||||
|
||||
/**
|
||||
* Injectable Objects that contains a live list of child directives in the light Dom of a directive.
|
||||
* The directives are kept in depth-first pre-order traversal of the DOM.
|
||||
*
|
||||
* In the future this class will implement an Observable interface.
|
||||
* For now it uses a plain list of observable callbacks.
|
||||
*/
|
||||
class QueryList extends Object with IterableMixin<Directive> {
|
||||
List<Directive> _results;
|
||||
List _callbacks;
|
||||
bool _dirty;
|
||||
|
||||
QueryList(): _results = [], _callbacks = [], _dirty = false;
|
||||
|
||||
Iterator<Directive> get iterator => _results.iterator;
|
||||
|
||||
reset(newList) {
|
||||
_results = newList;
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
_results.add(obj);
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
// TODO(rado): hook up with change detection after #995.
|
||||
fireCallbacks() {
|
||||
if (_dirty) {
|
||||
_callbacks.forEach((c) => c());
|
||||
_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(callback) {
|
||||
this._callbacks.add(callback);
|
||||
}
|
||||
|
||||
removeCallback(callback) {
|
||||
this._callbacks.remove(callback);
|
||||
}
|
||||
}
|
51
modules/angular2/src/core/compiler/query_list.es6
Normal file
51
modules/angular2/src/core/compiler/query_list.es6
Normal file
@ -0,0 +1,51 @@
|
||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Directive} from 'angular2/src/core/annotations/annotations';
|
||||
|
||||
/**
|
||||
* Injectable Objects that contains a live list of child directives in the light Dom of a directive.
|
||||
* The directives are kept in depth-first pre-order traversal of the DOM.
|
||||
*
|
||||
* In the future this class will implement an Observable interface.
|
||||
* For now it uses a plain list of observable callbacks.
|
||||
*/
|
||||
export class QueryList {
|
||||
_results: List<Directive>;
|
||||
_callbacks;
|
||||
_dirty;
|
||||
|
||||
constructor() {
|
||||
this._results = [];
|
||||
this._callbacks = [];
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._results[Symbol.iterator]();
|
||||
}
|
||||
|
||||
reset(newList) {
|
||||
this._results = newList;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
ListWrapper.push(this._results, obj);
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
// TODO(rado): hook up with change detection after #995.
|
||||
fireCallbacks() {
|
||||
if (this._dirty) {
|
||||
ListWrapper.forEach(this._callbacks, (c) => c());
|
||||
this._dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(callback) {
|
||||
ListWrapper.push(this._callbacks, callback);
|
||||
}
|
||||
|
||||
removeCallback(callback) {
|
||||
ListWrapper.remove(this._callbacks, callback);
|
||||
}
|
||||
}
|
@ -1,378 +0,0 @@
|
||||
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 {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {ShadowCss} from './shadow_dom_emulation/shadow_css';
|
||||
|
||||
import {StyleInliner} from './style_inliner';
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
|
||||
import * as NS from './pipeline/compile_step';
|
||||
import {CompileElement} from './pipeline/compile_element';
|
||||
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 {
|
||||
attachTemplate(el, view:viewModule.View) {}
|
||||
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { return null; }
|
||||
|
||||
/**
|
||||
* An optional step that can modify the template style elements.
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @param {string} templateUrl the template base URL
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||
*/
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return _emptyStep();
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional step that can modify the template elements (style elements exlcuded).
|
||||
*
|
||||
* This step could be used to modify the template in order to scope the styles.
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||
*/
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep { return _emptyStep(); }
|
||||
|
||||
/**
|
||||
* The application element does not go through the compiler pipeline.
|
||||
*
|
||||
* This methods is called when the root ProtoView is created and to optionnaly update the
|
||||
* application root element.
|
||||
*
|
||||
* @see ProtoView.createRootProtoView
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @param element
|
||||
*/
|
||||
shimAppElement(cmpMetadata: DirectiveMetadata, element) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* This strategy emulates the Shadow DOM for the templates, styles **excluded**:
|
||||
* - components templates are added as children of their component element,
|
||||
* - styles are moved from the templates to the styleHost (i.e. the document head).
|
||||
*
|
||||
* Notes:
|
||||
* - 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
|
||||
*/
|
||||
@Injectable()
|
||||
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||
_styleUrlResolver: StyleUrlResolver;
|
||||
_styleHost;
|
||||
|
||||
constructor(styleUrlResolver: StyleUrlResolver, styleHost) {
|
||||
super();
|
||||
this._styleUrlResolver = styleUrlResolver;
|
||||
this._styleHost = styleHost;
|
||||
}
|
||||
|
||||
attachTemplate(el, view:viewModule.View) {
|
||||
DOM.clearNodes(el);
|
||||
_moveViewNodesIntoParent(el, view);
|
||||
}
|
||||
|
||||
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom {
|
||||
return new LightDom(lightDomView, shadowDomView, el);
|
||||
}
|
||||
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return new _EmulatedUnscopedCssStep(cmpMetadata, templateUrl, this._styleUrlResolver,
|
||||
this._styleHost);
|
||||
}
|
||||
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
|
||||
return new _BaseEmulatedShadowDomStep();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This strategy emulates the Shadow DOM for the templates, styles **included**:
|
||||
* - components templates are added as children of their component element,
|
||||
* - both the template and the styles are modified so that styles are scoped to the component
|
||||
* they belong to,
|
||||
* - styles are moved from the templates to the styleHost (i.e. the document head).
|
||||
*
|
||||
* Notes:
|
||||
* - styles are scoped to their component and will apply only to it,
|
||||
* - a common subset of shadow DOM selectors are supported,
|
||||
* - see `ShadowCss` for more information and limitations.
|
||||
*/
|
||||
@Injectable()
|
||||
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
|
||||
_styleInliner: StyleInliner;
|
||||
|
||||
constructor(styleInliner: StyleInliner, styleUrlResolver: StyleUrlResolver, styleHost) {
|
||||
super(styleUrlResolver, styleHost);
|
||||
this._styleInliner = styleInliner;
|
||||
}
|
||||
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return new _EmulatedScopedCssStep(cmpMetadata, templateUrl, this._styleInliner,
|
||||
this._styleUrlResolver, this._styleHost);
|
||||
}
|
||||
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
|
||||
return new _ShimShadowDomStep(cmpMetadata);
|
||||
}
|
||||
|
||||
shimAppElement(cmpMetadata: DirectiveMetadata, element) {
|
||||
var cmpType = cmpMetadata.type;
|
||||
var hostAttribute = _getHostAttribute(_getComponentId(cmpType));
|
||||
DOM.setAttribute(element, hostAttribute, '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This strategies uses the native Shadow DOM support.
|
||||
*
|
||||
* The templates for the component are inserted in a Shadow Root created on the component element.
|
||||
* Hence they are strictly isolated.
|
||||
*/
|
||||
@Injectable()
|
||||
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
_styleUrlResolver: StyleUrlResolver;
|
||||
|
||||
constructor(styleUrlResolver: StyleUrlResolver) {
|
||||
super();
|
||||
this._styleUrlResolver = styleUrlResolver;
|
||||
}
|
||||
|
||||
attachTemplate(el, view:viewModule.View){
|
||||
_moveViewNodesIntoParent(DOM.createShadowRoot(el), view);
|
||||
}
|
||||
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return new _NativeCssStep(templateUrl, this._styleUrlResolver);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata) {
|
||||
super();
|
||||
var id = _getComponentId(cmpMetadata.type);
|
||||
this._contentAttribute = _getContentAttribute(id);
|
||||
}
|
||||
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
super.process(parent, current, control);
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shim the element as a child of the compiled component
|
||||
DOM.setAttribute(current.element, this._contentAttribute, '');
|
||||
|
||||
// If the current element is also a component, shim it as a host
|
||||
var host = current.componentDirective;
|
||||
if (isPresent(host)) {
|
||||
var hostId = _getComponentId(host.type);
|
||||
var hostAttribute = _getHostAttribute(hostId);
|
||||
DOM.setAttribute(current.element, hostAttribute, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _EmulatedUnscopedCssStep extends NS.CompileStep {
|
||||
_templateUrl: string;
|
||||
_styleUrlResolver: StyleUrlResolver;
|
||||
_styleHost;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata, templateUrl: string,
|
||||
styleUrlResolver: StyleUrlResolver, styleHost) {
|
||||
super();
|
||||
this._templateUrl = templateUrl;
|
||||
this._styleUrlResolver = styleUrlResolver;
|
||||
this._styleHost = styleHost;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var styleEl = current.element;
|
||||
var cssText = DOM.getText(styleEl);
|
||||
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
|
||||
DOM.setText(styleEl, cssText);
|
||||
DOM.remove(styleEl);
|
||||
|
||||
if (!MapWrapper.contains(_sharedStyleTexts, cssText)) {
|
||||
// Styles are unscoped and shared across components, only append them to the head
|
||||
// when there are not present yet
|
||||
MapWrapper.set(_sharedStyleTexts, cssText, true);
|
||||
_insertStyleElement(this._styleHost, styleEl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _EmulatedScopedCssStep extends NS.CompileStep {
|
||||
_templateUrl: string;
|
||||
_component: Type;
|
||||
_styleInliner: StyleInliner;
|
||||
_styleUrlResolver: StyleUrlResolver;
|
||||
_styleHost;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata, templateUrl: string, styleInliner: StyleInliner,
|
||||
styleUrlResolver: StyleUrlResolver, styleHost) {
|
||||
super();
|
||||
this._templateUrl = templateUrl;
|
||||
this._component = cmpMetadata.type;
|
||||
this._styleInliner = styleInliner;
|
||||
this._styleUrlResolver = styleUrlResolver;
|
||||
this._styleHost = styleHost;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var styleEl = current.element;
|
||||
|
||||
var cssText = DOM.getText(styleEl);
|
||||
|
||||
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
|
||||
var css = this._styleInliner.inlineImports(cssText, this._templateUrl);
|
||||
|
||||
if (PromiseWrapper.isPromise(css)) {
|
||||
DOM.setText(styleEl, '');
|
||||
ListWrapper.push(parent.inheritedProtoView.stylePromises, css);
|
||||
return css.then((css) => {
|
||||
css = _shimCssForComponent(css, this._component);
|
||||
DOM.setText(styleEl, css);
|
||||
});
|
||||
} else {
|
||||
css = _shimCssForComponent(css, this._component);
|
||||
DOM.setText(styleEl, css);
|
||||
}
|
||||
|
||||
DOM.remove(styleEl);
|
||||
_insertStyleElement(this._styleHost, styleEl);
|
||||
}
|
||||
}
|
||||
|
||||
class _NativeCssStep extends NS.CompileStep {
|
||||
_styleUrlResolver: StyleUrlResolver;
|
||||
_templateUrl: string;
|
||||
|
||||
constructor(templateUrl: string, styleUrlResover: StyleUrlResolver) {
|
||||
super();
|
||||
this._styleUrlResolver = styleUrlResover;
|
||||
this._templateUrl = templateUrl;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var styleEl = current.element;
|
||||
var cssText = DOM.getText(styleEl);
|
||||
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
|
||||
DOM.setText(styleEl, cssText);
|
||||
}
|
||||
}
|
||||
|
||||
function _moveViewNodesIntoParent(parent, view) {
|
||||
for (var i = 0; i < view.nodes.length; ++i) {
|
||||
DOM.appendChild(parent, view.nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var _componentUIDs: Map<Type, int> = MapWrapper.create();
|
||||
var _nextComponentUID: int = 0;
|
||||
var _sharedStyleTexts: Map<string, boolean> = MapWrapper.create();
|
||||
var _lastInsertedStyleEl;
|
||||
|
||||
function _getComponentId(component: Type) {
|
||||
var id = MapWrapper.get(_componentUIDs, component);
|
||||
if (isBlank(id)) {
|
||||
id = _nextComponentUID++;
|
||||
MapWrapper.set(_componentUIDs, component, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function _insertStyleElement(host, styleEl) {
|
||||
if (isBlank(_lastInsertedStyleEl)) {
|
||||
var firstChild = DOM.firstChild(host);
|
||||
if (isPresent(firstChild)) {
|
||||
DOM.insertBefore(firstChild, styleEl);
|
||||
} else {
|
||||
DOM.appendChild(host, styleEl);
|
||||
}
|
||||
} else {
|
||||
DOM.insertAfter(_lastInsertedStyleEl, styleEl);
|
||||
}
|
||||
_lastInsertedStyleEl = styleEl;
|
||||
}
|
||||
|
||||
// Return the attribute to be added to the component
|
||||
function _getHostAttribute(id: int) {
|
||||
return `_nghost-${id}`;
|
||||
}
|
||||
|
||||
// Returns the attribute to be added on every single element nodes in the component
|
||||
function _getContentAttribute(id: int) {
|
||||
return `_ngcontent-${id}`;
|
||||
}
|
||||
|
||||
function _shimCssForComponent(cssText: string, component: Type): string {
|
||||
var id = _getComponentId(component);
|
||||
var shadowCss = new ShadowCss();
|
||||
return shadowCss.shimCssText(cssText, _getContentAttribute(id), _getHostAttribute(id));
|
||||
}
|
||||
|
||||
// Reset the caches - used for tests only
|
||||
export function resetShadowDomCache() {
|
||||
MapWrapper.clear(_componentUIDs);
|
||||
_nextComponentUID = 0;
|
||||
MapWrapper.clear(_sharedStyleTexts);
|
||||
_lastInsertedStyleEl = null;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user