Compare commits
488 Commits
starting
...
2.0.0-alph
Author | SHA1 | Date | |
---|---|---|---|
f0ef72d6cc | |||
a58c9f83bd | |||
624a33f7f8 | |||
20a033e4c9 | |||
93c331d103 | |||
51c477925a | |||
1daa8aa3a1 | |||
62bf777ef1 | |||
aaf3edd131 | |||
6bba289a3c | |||
b0c735f72c | |||
0e2047f9ca | |||
e30ad2ec2c | |||
c509057f65 | |||
e138add584 | |||
49777648b3 | |||
cb87fa0970 | |||
649e276610 | |||
5ef11774c2 | |||
75db2c5241 | |||
c8ebd11d63 | |||
6651aa1e1d | |||
3f28d08778 | |||
ab28676d02 | |||
577a80371f | |||
01fdb4afc6 | |||
be7504d451 | |||
169e4e862d | |||
abc3de7efe | |||
0b1bb172c9 | |||
0856516ae9 | |||
705d3aacff | |||
1d0078415f | |||
9d1df21d91 | |||
3f36a3c119 | |||
8c15ccecd1 | |||
e966869744 | |||
6b017fb388 | |||
bb6f59e423 | |||
c9cec60007 | |||
f356d03362 | |||
0520ca68b4 | |||
8e1d53b5e9 | |||
f5b56c627b | |||
740d85cad8 | |||
33bba094d2 | |||
f88c4b77ca | |||
75da6e4c4a | |||
1864f60afb | |||
457c15cd6c | |||
28feac9411 | |||
9153331303 | |||
9d5c33f9dd | |||
2713b7877b | |||
2f0fef8ee1 | |||
259f872cea | |||
68ed8f1b6b | |||
ef7014fe19 | |||
46ad3552c7 | |||
4965226f3f | |||
ea546f5069 | |||
cf32213079 | |||
ce6a2ba836 | |||
0f4a089c32 | |||
3c77855b39 | |||
c1579222bd | |||
f863ea0db5 | |||
e4342743c0 | |||
9e8d31d532 | |||
f75a50c1dd | |||
c671706518 | |||
ead21c91a4 | |||
87dcd5eb6f | |||
8faf6364dc | |||
b71fe311fc | |||
bb50fc131b | |||
3aac2fefd7 | |||
fb67e37339 | |||
648c514c28 | |||
511e832ee2 | |||
09f8d8f7ba | |||
1205f54d01 | |||
b5032fd374 | |||
a7a9463624 | |||
59824e40e8 | |||
a51a5c2968 | |||
e3c11045bf | |||
414e58edb5 | |||
3bb3bff1f2 | |||
d2d4e7d783 | |||
ee1b574baf | |||
c0f3778dda | |||
d4925b61ff | |||
5b104936ae | |||
14988d4415 | |||
cd953ceb48 | |||
726fecbfb6 | |||
818bb9b697 | |||
e4586249fa | |||
4c1e978536 | |||
3d62546314 | |||
b9eab463f7 | |||
dff4795e49 | |||
ab74e1ed4e | |||
902984cc10 | |||
ce431f279e | |||
7fb2f2069c | |||
c269bd5d3c | |||
b72eb0783b | |||
a801da6f7c | |||
6fcd3709cf | |||
1b2754dacd | |||
e617ca6323 | |||
15376a6d24 | |||
867705bd2c | |||
6ab19dd095 | |||
99fdb9ac41 | |||
2827ca1559 | |||
8ea03d0380 | |||
d1ec2e18cd | |||
aa58e4bba5 | |||
0a97f0b645 | |||
8a5cf896d0 | |||
22c79df98d | |||
a52798543a | |||
1cbdb9cd17 | |||
4c9b8ebb0c | |||
6aea629cd3 | |||
649fd5a7a9 | |||
40c4eb7240 | |||
cd05ed8de9 | |||
42e7fc5252 | |||
7740fc071c | |||
bdf6af9bd6 | |||
2f83efaac8 | |||
32c5ab956c | |||
b111ca9471 | |||
725f909ff8 | |||
427f0d021c | |||
9fc9d53566 | |||
6dece68bb8 | |||
0676fef61f | |||
1d52cfba13 | |||
bfa381b35a | |||
f78406392b | |||
623edcd2d8 | |||
b5e350b18c | |||
87cf434929 | |||
4bab25b366 | |||
6896305e34 | |||
8ccafb0524 | |||
8a92a1f13e | |||
d0059b5d75 | |||
fa8e059f28 | |||
8e18d6c6cf | |||
afe0e45453 | |||
2e3e41ba64 | |||
d74dd1126b | |||
0ff99081bd | |||
6f4b6783c0 | |||
b1bc792b56 | |||
d4b8a86509 | |||
642e7e5c46 | |||
4650d25a53 | |||
7551a28f1a | |||
e51a48fe4c | |||
22c6c09daf | |||
e70a2f21dd | |||
97e6fb6835 | |||
14a7b9f794 | |||
fa1ec48549 | |||
f7f06c5ad4 | |||
e23004df52 | |||
fe70c2647a | |||
ada1e642c5 | |||
817c79ca77 | |||
a97a2266d3 | |||
8b3c808cb0 | |||
2d929e73ec | |||
681d06386d | |||
77b31ab42f | |||
5b4eb0c6d7 | |||
5c25248582 | |||
64ad74acbe | |||
02997f473a | |||
abda569b55 | |||
447018b54b | |||
0a200aff70 | |||
883e1c1541 | |||
aabe83cf63 | |||
fbd6851860 | |||
d6dae0cc85 | |||
376d508934 | |||
a00cb1de50 | |||
56f3429cc9 | |||
4943c0f887 | |||
00e2d70f05 | |||
e52d71060f | |||
01869f9fa8 | |||
526c51d1a6 | |||
2b4d30d931 | |||
3dc4df2ffa | |||
f830cfca12 | |||
eac5c88893 | |||
abfe175c9e | |||
0fc66daef6 | |||
5a095bb257 | |||
de31aca7a7 | |||
97220dd2ba | |||
e1b0bab9a6 | |||
66a2f9b23a | |||
a8533b2133 | |||
87ac100c66 | |||
487c4d23c1 | |||
8906cdbab8 | |||
88963b438f | |||
4fd4a1d15c | |||
371c8b8a1c | |||
eb87f5f851 | |||
27d227283c | |||
ea9d24be31 | |||
5408abca68 | |||
233cb0f96a | |||
8b28e99373 | |||
923d90bce8 | |||
97fc248e00 | |||
7bd682bb27 | |||
e927342e58 | |||
ae84eb7462 | |||
f89bb8eaf3 | |||
0d0b3a35da | |||
cf7bef58b0 | |||
c65fd31e86 | |||
6600ac7031 | |||
957384ceeb | |||
d3e391d176 | |||
0658d5602e | |||
458213d055 | |||
cd1295a823 | |||
5d302c504e | |||
68faddbf5c | |||
dc9c614da2 | |||
8c1adabe1c | |||
213dabdceb | |||
6ecaa9aebb | |||
cb2e646332 | |||
fef1dee7aa | |||
e14543498c | |||
c25478380c | |||
e819e97f9a | |||
f149ae79c6 | |||
ffe13078e5 | |||
f0d0fe0801 | |||
d630d5baa5 | |||
7cac7c5157 | |||
aba61f22a6 | |||
8475c63a6a | |||
2d09f84182 | |||
ef6e0d8eb8 | |||
db97d73c3b | |||
0e3d0fbec6 | |||
896a1564ef | |||
8b97cf1479 | |||
b5c9f9ed9b | |||
bda120d862 | |||
3177576ad6 | |||
896a0457f8 | |||
caf8e2723d | |||
0107543a33 | |||
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
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,11 +1,17 @@
|
||||
.DS_STORE
|
||||
|
||||
# Don’t commit the following directories created by pub.
|
||||
packages
|
||||
pubspec.lock
|
||||
.pub
|
||||
|
||||
/dist/
|
||||
packages/
|
||||
.buildlog
|
||||
node_modules
|
||||
bower_components
|
||||
.pub
|
||||
.DS_STORE
|
||||
|
||||
# Or broccoli working directory
|
||||
tmp
|
||||
|
||||
# Or the files created by dart2js.
|
||||
*.dart.js
|
||||
@ -14,6 +20,10 @@ bower_components
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
# Or type definitions we mirror from github
|
||||
**/typings/**/*.d.ts
|
||||
**/typings/tsd.cached.json
|
||||
|
||||
# Include when developing application packages.
|
||||
pubspec.lock
|
||||
.c9
|
||||
@ -23,4 +33,7 @@ pubspec.lock
|
||||
# Don't check in secret files
|
||||
*secret.js
|
||||
|
||||
# Ignore npm debug log
|
||||
npm-debug.log
|
||||
|
||||
/docs/bower_components/
|
||||
|
29
.travis.yml
29
.travis.yml
@ -2,31 +2,56 @@ language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '0.10'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
env:
|
||||
global:
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- 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
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${ARCH}
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
|
||||
|
||||
install:
|
||||
# Update npm
|
||||
- npm install -g npm@2.7.4
|
||||
- npm --version
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies and ensure that npm cache is not stale
|
||||
- tools/npm/install-dependencies.sh
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
|
||||
script:
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
|
||||
after_script:
|
||||
- ./scripts/ci/print-logs.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
|
104
CHANGELOG.md
Normal file
104
CHANGELOG.md
Normal file
@ -0,0 +1,104 @@
|
||||
<a name"2.0.0-alpha.22"></a>
|
||||
### 2.0.0-alpha.22 (2015-05-07)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **brocolli:** escape special regexp characters when building regexps ([a58c9f83](https://github.com/angular/angular/commit/a58c9f83), closes [#1721](https://github.com/angular/angular/issues/1721), [#1752](https://github.com/angular/angular/issues/1752))
|
||||
* **build:**
|
||||
* build the broccoli tools with correct typescript version. ([6bba289a](https://github.com/angular/angular/commit/6bba289a))
|
||||
* use correct tsd command to get typings at requested versions ([1205f54d](https://github.com/angular/angular/commit/1205f54d))
|
||||
* revert typescript upgrade which broke the build. ([b5032fd3](https://github.com/angular/angular/commit/b5032fd3))
|
||||
* refer to newest version of hammerjs typings ([a7a94636](https://github.com/angular/angular/commit/a7a94636))
|
||||
* **bundle:** update the bundle config to point to rx.js ([cf322130](https://github.com/angular/angular/commit/cf322130))
|
||||
* **change_detector:** ensure that locals are only used when implicit receiver ([d4925b61](https://github.com/angular/angular/commit/d4925b61), closes [#1542](https://github.com/angular/angular/issues/1542))
|
||||
* **compiler:**
|
||||
* clone templates before compiling them ([9e8d31d5](https://github.com/angular/angular/commit/9e8d31d5), closes [#1058](https://github.com/angular/angular/issues/1058))
|
||||
* changed the compiler to set up event listeners and host properties on host view ([e3c11045](https://github.com/angular/angular/commit/e3c11045), closes [#1584](https://github.com/angular/angular/issues/1584))
|
||||
* only sets viewDefinition absUrl if the view has either a template or templateUrl ([3d625463](https://github.com/angular/angular/commit/3d625463), closes [#1326](https://github.com/angular/angular/issues/1326), [#1327](https://github.com/angular/angular/issues/1327))
|
||||
* **decorators:**
|
||||
* incorrect annotation to decorator adapter ([b0c735f7](https://github.com/angular/angular/commit/b0c735f7))
|
||||
* fixed decorators ([49777648](https://github.com/angular/angular/commit/49777648))
|
||||
* fixes decorator reflection. ([be7504d4](https://github.com/angular/angular/commit/be7504d4))
|
||||
* updates missing benchmark and fixes typo. ([87dcd5eb](https://github.com/angular/angular/commit/87dcd5eb))
|
||||
* **decorators.es6:** export Directive decorator ([93c331d1](https://github.com/angular/angular/commit/93c331d1), closes [#1688](https://github.com/angular/angular/issues/1688))
|
||||
* **di:** improve error messages for invalid bindings ([ee1b574b](https://github.com/angular/angular/commit/ee1b574b), closes [#1515](https://github.com/angular/angular/issues/1515), [#1573](https://github.com/angular/angular/issues/1573))
|
||||
* **docs:** fix broken docs test after addition of .ts extension to dgeni regex. ([62bf777e](https://github.com/angular/angular/commit/62bf777e))
|
||||
* **exception_handler:** log errors via `console.error` ([ead21c91](https://github.com/angular/angular/commit/ead21c91))
|
||||
* **formatter:** point to the newest clang-format ([51c47792](https://github.com/angular/angular/commit/51c47792))
|
||||
* **router:**
|
||||
* fix for leading slash in dart ([c9cec600](https://github.com/angular/angular/commit/c9cec600))
|
||||
* navigate on popstate event ([2713b787](https://github.com/angular/angular/commit/2713b787))
|
||||
* throw if config does not contain required fields ([259f872c](https://github.com/angular/angular/commit/259f872c))
|
||||
* infer top-level routing from app component ([46ad3552](https://github.com/angular/angular/commit/46ad3552), closes [#1600](https://github.com/angular/angular/issues/1600))
|
||||
* use lists for RouteConfig annotations ([4965226f](https://github.com/angular/angular/commit/4965226f))
|
||||
* **view:** changed view manager to hydrate change detector after creating directives ([c1579222](https://github.com/angular/angular/commit/c1579222))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **benchmark:** added an implementation of the tree benchmark in React ([e4342743](https://github.com/angular/angular/commit/e4342743))
|
||||
* **benchmarks:** Add basic dart transformer benchmarks. ([1864f60a](https://github.com/angular/angular/commit/1864f60a))
|
||||
* **decorators:**
|
||||
* adds decorator versions of DI annotations. ([457c15cd](https://github.com/angular/angular/commit/457c15cd))
|
||||
* adds support for parameter decorators. ([f863ea0d](https://github.com/angular/angular/commit/f863ea0d))
|
||||
* adds decorators to be used by TS and Babel transpiled apps. ([fb67e373](https://github.com/angular/angular/commit/fb67e373))
|
||||
* **dom:** add location and history as DOM-like APIs. ([f356d033](https://github.com/angular/angular/commit/f356d033))
|
||||
* **material:** add prototype dialog component w/ demo. ([f88c4b77](https://github.com/angular/angular/commit/f88c4b77))
|
||||
* **router:**
|
||||
* adds the router to the self-executing bundle. ([8e1d53b5](https://github.com/angular/angular/commit/8e1d53b5))
|
||||
* export decorator version of RouteConfig ([75da6e4c](https://github.com/angular/angular/commit/75da6e4c))
|
||||
* route redirects ([91533313](https://github.com/angular/angular/commit/91533313))
|
||||
* sibling outlets ([9d5c33f9](https://github.com/angular/angular/commit/9d5c33f9))
|
||||
* export routerInjectables ([ef7014fe](https://github.com/angular/angular/commit/ef7014fe))
|
||||
* add location service ([ea546f50](https://github.com/angular/angular/commit/ea546f50))
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* Previously, `Directive` was the abstract base class of several directives.
|
||||
Now, `Directive` is the former `Decorator`, and `Component` inherits from it.
|
||||
|
||||
([f75a50c1](https://github.com/angular/angular/commit/f75a50c1))
|
||||
* A dynamic component is just a component that has no @View annotation…
|
||||
([8faf6364](https://github.com/angular/angular/commit/8faf6364))
|
||||
|
||||
|
||||
<a name="2.0.0-alpha.21"></a>
|
||||
# 2.0.0-alpha.21 (2015-04-27)
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **dart/transform:** Dedup getters, setters, & methods
|
||||
([15376a6d](https://github.com/angular/angular/commit/15376a6d243740c73cf90f55525d1710cdd156f5))
|
||||
- **facade:** add isType method
|
||||
([e617ca63](https://github.com/angular/angular/commit/e617ca6323902bd98c0f1eb990b82f6b8d3c98e3))
|
||||
- **parser:** support === and !== operators
|
||||
([afe0e454](https://github.com/angular/angular/commit/afe0e454537f9252f9cf313647e649cfa464f96f),
|
||||
[#1496](https://github.com/angular/angular/issues/1496), [#1500](https://github.com/angular/angular/issues/1500))
|
||||
- **router:** add initial implementation
|
||||
([1b2754da](https://github.com/angular/angular/commit/1b2754dacdd15e8fea429d56cdacb28eae76d2b1))
|
||||
- **view:** reimplemented property setters using change detection
|
||||
([8ccafb05](https://github.com/angular/angular/commit/8ccafb0524e3ac4c51af34ef88e0fe27482336a6))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **benchmarks:**
|
||||
- benchmark that measure cost of dynamic components
|
||||
([427f0d02](https://github.com/angular/angular/commit/427f0d021c51ea6923edd07574a4cc74a1ef84e6))
|
||||
- benchmark measuring cost of decorators (fixes #1479)
|
||||
([9fc9d535](https://github.com/angular/angular/commit/9fc9d535667c620017367877dbc2a3bc56d358b7))
|
||||
|
||||
|
||||
## Other (malformed commit messages)
|
||||
|
||||
- **other:**
|
||||
- feat: alllow specifying directives as bindings
|
||||
([4bab25b3](https://github.com/angular/angular/commit/4bab25b3666f4247434ad5cb871906fb063fef51),
|
||||
[#1498](https://github.com/angular/angular/issues/1498))
|
||||
- fix: export ShadowDom strategies
|
||||
([6896305e](https://github.com/angular/angular/commit/6896305e34082c246769829e4258631c1d2363d1),
|
||||
[#1510](https://github.com/angular/angular/issues/1510), [#1511](https://github.com/angular/angular/issues/1511))
|
||||
|
151
DEVELOPER.md
151
DEVELOPER.md
@ -3,56 +3,59 @@
|
||||
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)
|
||||
* [Transforming Dart code](#transforming-dart-code)
|
||||
* [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)
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html); note that you can
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html). You can also
|
||||
download both **stable** and **dev** channel versions from the [download
|
||||
archive](https://www.dartlang.org/tools/download-archive).
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **Github app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)): the [Github Guide to Installing
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org) which is used to run a development web server, run tests, and
|
||||
generate distributable files. We also use Node's Package Manager (`npm`). Depending on your
|
||||
system, you can install Node either from source or as a pre-packaged bundle.
|
||||
* [Node.js](http://nodejs.org), which is used to run a development web server, run tests, and
|
||||
generate distributable files. We also use Node's Package Manager, `npm`, which comes with Node.
|
||||
Depending on your system, you can install Node either from source or as a pre-packaged bundle.
|
||||
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||
bleeding edge functionality, built especially for developers (and early adopters).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
Forking and cloning the Angular repository:
|
||||
Fork and clone the Angular repository:
|
||||
|
||||
1. Login to your Github account or create one by following the instructions given
|
||||
1. Login to your GitHub account or create one by following the instructions given
|
||||
[here](https://github.com/signup/free).
|
||||
2. [Fork](http://help.github.com/forking) the [main Angular
|
||||
repository](https://github.com/angular/angular).
|
||||
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
|
||||
the Angular repository that you forked in the first place:
|
||||
the Angular repository that you forked in the first place.
|
||||
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
# Clone your GitHub repository:
|
||||
git clone git@github.com:<github username>/angular.git
|
||||
|
||||
# Go to the Angular directory:
|
||||
@ -90,32 +93,31 @@ PATH+=":$DART_SDK/bin"
|
||||
|
||||
## Installing NPM Modules and Dart Packages
|
||||
|
||||
Next, install the modules and packages needed to build Angular and run tests:
|
||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
||||
|
||||
```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
|
||||
@ -124,42 +126,60 @@ $(npm bin)/gulp build
|
||||
Notes:
|
||||
* Results are put in the `dist` folder.
|
||||
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
|
||||
|
||||
You can selectively build either the JS or Dart versions as follows:
|
||||
|
||||
* `$(npm bin)/gulp build.js`
|
||||
* `$(npm bin)/gulp build.dart`
|
||||
|
||||
To clean out the `dist` folder, run:
|
||||
|
||||
To clean out the `dist` folder use:
|
||||
```shell
|
||||
$(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
|
||||
that 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.
|
||||
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.
|
||||
The task updates the dist folder with transpiled code whenever a source or test file changes, and
|
||||
Karma is run against the new output.
|
||||
|
||||
**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).
|
||||
**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.
|
||||
|
||||
### E2e tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs a local webserver).
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: Dart2JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
|
||||
|
||||
Angular specific command line options when running protractor:
|
||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
@ -167,9 +187,9 @@ Angular specific command line options when running protractor:
|
||||
### Performance tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs a local webserver)
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: Dart2JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
|
||||
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
@ -184,16 +204,20 @@ Angular specific command line options when running protractor (e.g. force gc, ..
|
||||
|
||||
### File suffixes
|
||||
|
||||
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
|
||||
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
|
||||
* `*.es5`: javascript files that don't get transpiled
|
||||
* `*.dart`: dart files that don't get transpiled
|
||||
* `*.js`: JavaScript files that get transpiled to Dart and EcmaScript 5
|
||||
* `*.es6`: JavaScript files that get transpiled only to EcmaScript 5
|
||||
* `*.es5`: JavaScript files that don't get transpiled
|
||||
* `*.dart`: Dart files that don't get transpiled
|
||||
|
||||
## CI using Travis
|
||||
|
||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||
|
||||
## Transforming Dart code
|
||||
|
||||
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
|
||||
|
||||
## Debugging
|
||||
|
||||
### Debug the transpiler
|
||||
@ -219,17 +243,18 @@ Notes:
|
||||
|
||||
If you need to debug the tests:
|
||||
|
||||
- add a `debugger;` statement to the test you want to debug (oe the source code),
|
||||
- add a `debugger;` statement to the test you want to debug (or the source code),
|
||||
- execute karma `$(npm bin)/gulp test.js`,
|
||||
- press the top right "DEBUG" button,
|
||||
- open the dev tools and press F5,
|
||||
- the execution halt at the `debugger;` statement
|
||||
- open the DevTools and press F5,
|
||||
- the execution halts at the `debugger;` statement
|
||||
|
||||
**Note (WebStorm users)**:
|
||||
You can create a Karma run config from WebStorm.
|
||||
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
|
||||
on the `debugger;` statement.
|
||||
You can then step into the code and add watches.
|
||||
|
||||
1. Create a Karma run config from WebStorm.
|
||||
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
|
||||
code on the `debugger;` statement.
|
||||
3. 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.
|
||||
|
||||
|
@ -18,7 +18,7 @@ Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
## Want to help?
|
||||
|
||||
Want to file a bug, or contribute some code or improve documentation? Excellent! Read up on our
|
||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing].
|
||||
|
||||
|
||||
@ -29,13 +29,14 @@ To see the examples, first build the project as described
|
||||
|
||||
### Hello World Example
|
||||
|
||||
This example consists of three basic pieces - a component, a decorator and a
|
||||
This example consists of three basic pieces - a component, a decorator, and a
|
||||
service. They are all constructed via injection. For more information see the
|
||||
comments in the source `modules/examples/src/hello_world/index.js`.
|
||||
|
||||
You can build this example as either JS or Dart app:
|
||||
You can build this example as either a JS or a 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:
|
||||
|
48
docs/angular.io-package/index.js
Normal file
48
docs/angular.io-package/index.js
Normal file
@ -0,0 +1,48 @@
|
||||
var path = require('canonical-path');
|
||||
var Package = require('dgeni').Package;
|
||||
var basePackage = require('../public-docs-package');
|
||||
|
||||
var PARTIAL_PATH = 'partials';
|
||||
var MODULES_DOCS_PATH = PARTIAL_PATH + '/api';
|
||||
|
||||
module.exports = new Package('angular.io', [basePackage])
|
||||
|
||||
.factory(require('./services/renderMarkdown'))
|
||||
.processor(require('./processors/addJadeDataDocsProcessor'))
|
||||
|
||||
// Configure rendering
|
||||
.config(function(templateFinder, templateEngine) {
|
||||
|
||||
templateFinder.templateFolders
|
||||
.unshift(path.resolve(__dirname, 'templates'));
|
||||
})
|
||||
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/angular.io';
|
||||
})
|
||||
|
||||
|
||||
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module'],
|
||||
pathTemplate: '${id}.html',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.jade'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
pathTemplate: '${moduleDoc.id}/${name}-${docType}.html',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${moduleDoc.id}/${name}-${docType}.jade',
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['jade-data'],
|
||||
pathTemplate: '${originalDoc.id}/_data',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${path}.json'
|
||||
});
|
||||
})
|
||||
|
||||
.config(function(getLinkInfo) {
|
||||
getLinkInfo.relativeLinks = true;
|
||||
});
|
@ -0,0 +1,76 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
|
||||
var titleCase = function(text) {
|
||||
return text.replace(/(.)(.*)/, function(_, first, rest) {
|
||||
return first.toUpperCase() + rest;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Create _data.json file for Harp pages
|
||||
*
|
||||
* http://harpjs.com/docs/development/metadata
|
||||
*
|
||||
* This method creates the meta data required for each page
|
||||
* such as the title, description, etc. This meta data is used
|
||||
* in the harp static site generator to create the title for headers
|
||||
* and the navigation used in the API docs
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = function addJadeDataDocsProcessor(EXPORT_DOC_TYPES) {
|
||||
return {
|
||||
$runAfter: ['adding-extra-docs', 'cloneExportedFromDocs'],
|
||||
$runBefore: ['extra-docs-added'],
|
||||
$process: function(docs) {
|
||||
var extraDocs = [];
|
||||
var modules = [];
|
||||
|
||||
|
||||
/*
|
||||
* Create Data for Modules
|
||||
*
|
||||
* Modules must be public and have content
|
||||
*/
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if (doc.docType === 'module' && doc.public && doc.exports.length) {
|
||||
modules.push(doc);
|
||||
|
||||
// GET DATA FOR INDEX PAGE OF MODULE SECTION
|
||||
var indexPageInfo = [{
|
||||
name: 'index',
|
||||
title: _.map(path.basename(doc.fileInfo.baseName).split('_'), function(part) {
|
||||
return titleCase(part);
|
||||
}).join(' '),
|
||||
intro: doc.description.replace('"', '\"').replace(/\r?\n|\r/g,"")
|
||||
}];
|
||||
|
||||
// GET DATA FOR EACH PAGE (CLASS, VARS, FUNCTIONS)
|
||||
var modulePageInfo = _.map(doc.exports, function(exportDoc) {
|
||||
return {
|
||||
name: exportDoc.name + '-' + exportDoc.docType,
|
||||
title: exportDoc.name + ' ' + titleCase(exportDoc.docType)
|
||||
};
|
||||
});
|
||||
|
||||
//COMBINE PAGE DATA
|
||||
var allPageData = indexPageInfo.concat(modulePageInfo);
|
||||
|
||||
// PUSH DATA TO EXTRA DOCS ARRAY
|
||||
extraDocs.push({
|
||||
id: doc.id + "-data",
|
||||
aliases: [doc.id + "-data"],
|
||||
docType: 'jade-data',
|
||||
originalDoc: doc,
|
||||
data: allPageData
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return docs.concat(extraDocs);
|
||||
}
|
||||
};
|
||||
};
|
50
docs/angular.io-package/services/renderMarkdown.js
Normal file
50
docs/angular.io-package/services/renderMarkdown.js
Normal file
@ -0,0 +1,50 @@
|
||||
var marked = require('marked');
|
||||
var Encoder = require('node-html-encoder').Encoder;
|
||||
var html2jade = require('html2jade');
|
||||
var indentString = require('indent-string');
|
||||
var S = require('string');
|
||||
|
||||
// entity type encoder
|
||||
var encoder = new Encoder('entity');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @dgService renderMarkdown
|
||||
* @description
|
||||
* Render the markdown in the given string as HTML.
|
||||
*/
|
||||
module.exports = function renderMarkdown(trimIndentation) {
|
||||
|
||||
var renderer = new marked.Renderer();
|
||||
|
||||
renderer.code = function(code, lang, escaped) {
|
||||
|
||||
var cssClasses = ['prettyprint', 'linenums'];
|
||||
var trimmedCode = trimIndentation(code);
|
||||
|
||||
if(lang) {
|
||||
if(lang=='html') {
|
||||
trimmedCode = encoder.htmlEncode(trimmedCode);
|
||||
}
|
||||
cssClasses.push(this.options.langPrefix + escape(lang, true));
|
||||
}
|
||||
|
||||
return 'pre(class="' + cssClasses.join(' ') + '")\n' + indentString('code.\n', ' ', 2) + trimmedCode;
|
||||
};
|
||||
|
||||
renderer.heading = function (text, level, raw) {
|
||||
var headingText = marked.Renderer.prototype.heading.call(renderer, text, level, raw);
|
||||
var title = 'h2 ' + S(headingText).stripTags().s;
|
||||
|
||||
if (level==2) {
|
||||
title = '.l-main-section\n' + indentString(title, ' ', 2) ;
|
||||
}
|
||||
|
||||
return title;
|
||||
};
|
||||
|
||||
return function(content) {
|
||||
return marked(content, { renderer: renderer });
|
||||
};
|
||||
};
|
49
docs/angular.io-package/templates/class.template.html
Normal file
49
docs/angular.io-package/templates/class.template.html
Normal file
@ -0,0 +1,49 @@
|
||||
{% include "lib/paramList.html" -%}
|
||||
{% extends 'layout/base.template.html' -%}
|
||||
|
||||
{% block body %}
|
||||
p.location-badge.
|
||||
exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a>
|
||||
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>
|
||||
|
||||
:markdown
|
||||
{$ doc.description | indent(2, true) $}
|
||||
|
||||
{%- if doc.constructorDoc or doc.members.length -%}
|
||||
.l-main-section
|
||||
h2 Members
|
||||
|
||||
{%- if doc.constructorDoc %}
|
||||
.l-sub-section
|
||||
h3 {$ doc.constructorDoc.name $}
|
||||
|
||||
{% if doc.constructorDoc.params %}
|
||||
pre.prettyprint
|
||||
code.
|
||||
{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) | indent(4, true) | trim $}
|
||||
{% endif %}
|
||||
:markdown
|
||||
{$ doc.constructorDoc.description | indent(6, true) | replace('## Example', '') | replace('# Example', '') $}
|
||||
|
||||
|
||||
{% endif -%}
|
||||
|
||||
{%- for member in doc.members %}{% if not member.private %}
|
||||
.l-sub-section
|
||||
h3 {$ member.name $}
|
||||
|
||||
{% if member.params %}
|
||||
pre.prettyprint
|
||||
code.
|
||||
{$ member.name $}{$ paramList(member.params) | indent(4, true) | trim $}
|
||||
{% endif %}
|
||||
:markdown
|
||||
|
||||
{$ member.description | indent(6, true) | replace('## Example', '') | replace('# Example', '') $}
|
||||
|
||||
|
||||
|
||||
{% endif %}{% endfor %}
|
||||
{%- endif -%}
|
||||
|
||||
{% endblock %}
|
16
docs/angular.io-package/templates/function.template.html
Normal file
16
docs/angular.io-package/templates/function.template.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% include "lib/paramList.html" -%}
|
||||
{% extends 'layout/base.template.html' -%}
|
||||
|
||||
{% block body %}
|
||||
.l-main-section
|
||||
h2(class="function export") {$ doc.name $}
|
||||
|
||||
p <code>{$ paramList(doc.parameters) $}</code>
|
||||
|
||||
p.location-badge.
|
||||
exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a>
|
||||
|
||||
:markdown
|
||||
{$ doc.description | indent(4, true) $}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
{%- for item in doc.data %}
|
||||
"{$ item.name $}" : {
|
||||
"title" : "{$ item.title $}"{% if item.intro %},
|
||||
"intro" : "{$ item.intro $}"{% endif %}
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor -%}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{% block body %}{% endblock %}
|
7
docs/angular.io-package/templates/lib/paramList.html
Normal file
7
docs/angular.io-package/templates/lib/paramList.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% macro paramList(params) -%}
|
||||
{%- if params -%}
|
||||
({%- for param in params -%}
|
||||
{$ param | escape $}{% if not loop.last %}, {% endif %}
|
||||
{%- endfor %})
|
||||
{%- endif %}
|
||||
{%- endmacro -%}
|
11
docs/angular.io-package/templates/module.template.html
Normal file
11
docs/angular.io-package/templates/module.template.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends 'layout/base.template.html' -%}
|
||||
{% block body -%}
|
||||
ul
|
||||
for page, slug in public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]]._data
|
||||
if slug != 'index'
|
||||
url = "/docs/" + current.path[1] + "/" + current.path[2] + "/" + current.path[3] + "/" + current.path[4] + "/" + slug + ".html"
|
||||
|
||||
li.c8
|
||||
!= partial("../../../../../_includes/_hover-card", {name: page.title, url: url })
|
||||
|
||||
{% endblock %}
|
11
docs/angular.io-package/templates/var.template.html
Normal file
11
docs/angular.io-package/templates/var.template.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
.l-main-section
|
||||
h2 {$ doc.name $} <span class="type">variable</span>
|
||||
p.location-badge.
|
||||
exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a>
|
||||
|
||||
:markdown
|
||||
{$ doc.description | indent(4, true) $}
|
||||
{% endblock %}
|
@ -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>
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
|
||||
<md-content class="md-padding">
|
||||
<ng-include src="nav.currentPage.partial"></ng-include>
|
||||
<ng-include autoscroll src="nav.currentPage.partial"></ng-include>
|
||||
</md-content>
|
||||
|
||||
</section>
|
||||
|
@ -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 = 'warn';
|
||||
})
|
||||
|
||||
|
||||
@ -56,12 +60,23 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{ include: 'modules/*/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/*.es6', basePath: 'modules' },
|
||||
{ include: 'modules/*/src/**/*.es6', basePath: 'modules' },
|
||||
{ include: 'modules/*/*.ts', basePath: 'modules' },
|
||||
{ include: 'modules/*/src/**/*.ts', basePath: 'modules' },
|
||||
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
|
||||
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/public'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/private'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/exportedAs'));
|
||||
})
|
||||
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/docs';
|
||||
@ -96,7 +111,7 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
idTemplate: '${moduleDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
getAliases: function(doc) { return [doc.id, doc.name]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
@ -144,4 +159,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);
|
||||
}
|
||||
};
|
||||
};
|
39
docs/dgeni-package/processors/cloneExportedFromDocs.js
Normal file
39
docs/dgeni-package/processors/cloneExportedFromDocs.js
Normal file
@ -0,0 +1,39 @@
|
||||
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 {
|
||||
|
||||
if(exportedAsModule !== doc.moduleDoc) {
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
};
|
@ -27,13 +27,15 @@ module.exports = function generateNavigationDoc() {
|
||||
modulesDoc.value.sections.push(moduleNavItem);
|
||||
|
||||
_.forEach(doc.exports, function(exportDoc) {
|
||||
var exportNavItem = {
|
||||
path: exportDoc.path,
|
||||
partial: exportDoc.outputPath,
|
||||
name: exportDoc.name,
|
||||
type: exportDoc.docType
|
||||
};
|
||||
moduleNavItem.pages.push(exportNavItem);
|
||||
if (!exportDoc.private) {
|
||||
var exportNavItem = {
|
||||
path: exportDoc.path,
|
||||
partial: exportDoc.outputPath,
|
||||
name: exportDoc.name,
|
||||
type: exportDoc.docType
|
||||
};
|
||||
moduleNavItem.pages.push(exportNavItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -10,9 +10,8 @@ var path = require('canonical-path');
|
||||
module.exports = function atScriptFileReader(log, atParser, modules) {
|
||||
var reader = {
|
||||
name: 'atScriptFileReader',
|
||||
defaultPattern: /\.js$/,
|
||||
defaultPattern: /\.js|\.es6|\.ts$/,
|
||||
getDocs: function(fileInfo) {
|
||||
|
||||
var moduleDoc = atParser.parseModule(fileInfo);
|
||||
moduleDoc.docType = 'module';
|
||||
moduleDoc.id = moduleDoc.moduleTree.moduleName;
|
||||
|
@ -33,7 +33,7 @@ describe('atScript file reader', function() {
|
||||
|
||||
|
||||
it('should provide a default pattern', function() {
|
||||
expect(reader.defaultPattern).toEqual(/\.js$/);
|
||||
expect(reader.defaultPattern).toEqual(/\.js|\.es6|\.ts$/);
|
||||
});
|
||||
|
||||
|
||||
@ -52,4 +52,4 @@ describe('atScript file reader', function() {
|
||||
expect(docs[0].docType).toEqual('module');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -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.silly('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/private.js
Normal file
4
docs/dgeni-package/tag-defs/private.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
name: 'private',
|
||||
transforms: function(doc, tag) { return 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>
|
||||
{% endfor %}
|
||||
|
||||
{%- if doc.constructorDoc %}
|
||||
<section class="member constructor">
|
||||
<h1 id="constructor" class="name">{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h1>
|
||||
{% marked %}
|
||||
{$ doc.constructorDoc.description $}
|
||||
{% endmarked %}
|
||||
</section>
|
||||
{% endif -%}
|
||||
|
||||
{%- for member in doc.members %}{% if not member.private %}
|
||||
<section class="member">
|
||||
<h1 id="{$ member.name $}" class="name">{$ member.name $}{$ paramList(member.params) $}</h1>
|
||||
{% marked %}
|
||||
{$ member.description $}
|
||||
{% endmarked %}
|
||||
</section>
|
||||
|
||||
{% endif %}{% 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,9 @@
|
||||
<h2>Exports</h2>
|
||||
<ul>
|
||||
{%- for exportDoc in doc.exports %}
|
||||
<li><a href="/{$ exportDoc.path $}">{$ exportDoc.name $} {$ exportDoc.docType $}</a></li>
|
||||
{% if not exportDoc.private -%}
|
||||
<li><a href="/{$ exportDoc.path $}"><strong>{$ exportDoc.name $}</strong> {$ exportDoc.docType $}</a></li>
|
||||
{%- endif %}
|
||||
{%- 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 %}
|
12
docs/links-package/index.js
Normal file
12
docs/links-package/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
module.exports = new Package('links', [])
|
||||
|
||||
.factory(require('./inline-tag-defs/link'))
|
||||
.factory(require('dgeni-packages/ngdoc/services/getAliases'))
|
||||
.factory(require('dgeni-packages/ngdoc/services/getDocFromAlias'))
|
||||
.factory(require('./services/getLinkInfo'))
|
||||
|
||||
.config(function(inlineTagProcessor, linkInlineTagDef) {
|
||||
inlineTagProcessor.inlineTagDefinitions.push(linkInlineTagDef);
|
||||
});
|
33
docs/links-package/inline-tag-defs/link.js
Normal file
33
docs/links-package/inline-tag-defs/link.js
Normal file
@ -0,0 +1,33 @@
|
||||
var INLINE_LINK = /(\S+)(?:\s+([\s\S]+))?/;
|
||||
|
||||
/**
|
||||
* @dgService linkInlineTagDef
|
||||
* @description
|
||||
* Process inline link tags (of the form {@link some/uri Some Title}), replacing them with HTML anchors
|
||||
* @kind function
|
||||
* @param {Object} url The url to match
|
||||
* @param {Function} docs error message
|
||||
* @return {String} The html link information
|
||||
*
|
||||
* @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc
|
||||
*/
|
||||
module.exports = function linkInlineTagDef(getLinkInfo, createDocMessage) {
|
||||
return {
|
||||
name: 'link',
|
||||
description: 'Process inline link tags (of the form {@link some/uri Some Title}), replacing them with HTML anchors',
|
||||
handler: function(doc, tagName, tagDescription) {
|
||||
|
||||
// Parse out the uri and title
|
||||
return tagDescription.replace(INLINE_LINK, function(match, uri, title) {
|
||||
|
||||
var linkInfo = getLinkInfo(uri, title, doc);
|
||||
|
||||
if ( !linkInfo.valid ) {
|
||||
throw new Error(createDocMessage(linkInfo.error, doc));
|
||||
}
|
||||
|
||||
return "<a href='" + linkInfo.url + "'>" + linkInfo.title + "</a>";
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
70
docs/links-package/services/getLInkInfo.js
Normal file
70
docs/links-package/services/getLInkInfo.js
Normal file
@ -0,0 +1,70 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @dgService getLinkInfo
|
||||
* @description
|
||||
* Get link information to a document that matches the given url
|
||||
* @kind function
|
||||
* @param {String} url The url to match
|
||||
* @param {String} title An optional title to return in the link information
|
||||
* @return {Object} The link information
|
||||
*
|
||||
* @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc
|
||||
*/
|
||||
module.exports = function getLinkInfo(getDocFromAlias, encodeCodeBlock, log) {
|
||||
|
||||
return function getLinkInfoImpl(url, title, currentDoc) {
|
||||
var linkInfo = {
|
||||
url: url,
|
||||
type: 'url',
|
||||
valid: true,
|
||||
title: title || url
|
||||
};
|
||||
|
||||
if ( !url ) {
|
||||
throw new Error('Invalid url');
|
||||
}
|
||||
|
||||
var docs = getDocFromAlias(url, currentDoc);
|
||||
|
||||
if ( docs.length > 1 ) {
|
||||
|
||||
linkInfo.valid = false;
|
||||
linkInfo.error = 'Ambiguous link: "' + url + '".\n' +
|
||||
docs.reduce(function(msg, doc) { return msg + '\n "' + doc.id + '" ('+ doc.docType + ') : (' + doc.area + ')'; }, 'Matching docs: ');
|
||||
|
||||
} else if ( docs.length === 1 ) {
|
||||
|
||||
linkInfo.url = docs[0].path;
|
||||
linkInfo.title = title || encodeCodeBlock(docs[0].name, true);
|
||||
linkInfo.type = 'doc';
|
||||
|
||||
if ( getLinkInfoImpl.relativeLinks && currentDoc && currentDoc.path ) {
|
||||
var currentFolder = path.dirname(currentDoc.path);
|
||||
var docFolder = path.dirname(linkInfo.url);
|
||||
var relativeFolder = path.relative(path.join('/', currentFolder), path.join('/', docFolder));
|
||||
linkInfo.url = path.join(relativeFolder, path.basename(linkInfo.url));
|
||||
log.debug(currentDoc.path, docs[0].path, linkInfo.url);
|
||||
}
|
||||
|
||||
} else if ( url.indexOf('#') > 0 ) {
|
||||
var pathAndHash = url.split('#');
|
||||
linkInfo = getLinkInfoImpl(pathAndHash[0], title, currentDoc);
|
||||
linkInfo.url = linkInfo.url + '#' + pathAndHash[1];
|
||||
return linkInfo;
|
||||
|
||||
} else if ( url.indexOf('/') === -1 && url.indexOf('#') !== 0 ) {
|
||||
|
||||
linkInfo.valid = false;
|
||||
linkInfo.error = 'Invalid link (does not match any doc): "' + url + '"';
|
||||
|
||||
} else {
|
||||
|
||||
linkInfo.title = title || (( url.indexOf('#') === 0 ) ? url.substring(1) : path.basename(url, '.html'));
|
||||
|
||||
}
|
||||
|
||||
return linkInfo;
|
||||
};
|
||||
};
|
@ -1,21 +1,16 @@
|
||||
var Package = require('dgeni').Package;
|
||||
var basePackage = require('../dgeni-package');
|
||||
var linksPackage = require('../links-package');
|
||||
|
||||
|
||||
module.exports = new Package('angular-public', [basePackage])
|
||||
module.exports = new Package('angular-public', [basePackage, linksPackage])
|
||||
|
||||
.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);
|
||||
|
||||
}
|
||||
};
|
986
gulpfile.js
986
gulpfile.js
File diff suppressed because it is too large
Load Diff
@ -8,14 +8,14 @@ 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: 'tools/transpiler/spec/**/*_spec.js', included: true},
|
||||
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
|
||||
|
||||
// These files are not included, they are imported by the unit tests above.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**/*', included: false},
|
||||
// Karma-dart via the dart-unittest framework generates
|
||||
// `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'dist/dart/**', included: false, watched: false},
|
||||
|
||||
// Dependencies, installed with `pub install`.
|
||||
{pattern: 'packages/**/*.dart', included: false, watched: false},
|
||||
@ -24,58 +24,29 @@ module.exports = function(config) {
|
||||
{pattern: 'test-main.dart', included: true}
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/dart/**/packages/**',
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// TODO(vojta): Remove the localhost:9877 from urls, once the proxy fix is merged:
|
||||
// https://github.com/karma-runner/karma/pull/1207
|
||||
//
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: {
|
||||
// Dependencies installed with `pub install`.
|
||||
'/packages/unittest': 'http://localhost:9877/base/packages/unittest',
|
||||
'/packages/guinness': 'http://localhost:9877/base/packages/guinness',
|
||||
'/packages/matcher': 'http://localhost:9877/base/packages/matcher',
|
||||
'/packages/stack_trace': 'http://localhost:9877/base/packages/stack_trace',
|
||||
'/packages/collection': 'http://localhost:9877/base/packages/collection',
|
||||
'/packages/path': 'http://localhost:9877/base/packages/path',
|
||||
'/packages/unittest': '/base/packages/unittest',
|
||||
'/packages/guinness': '/base/packages/guinness',
|
||||
'/packages/matcher': '/base/packages/matcher',
|
||||
'/packages/stack_trace': '/base/packages/stack_trace',
|
||||
'/packages/collection': '/base/packages/collection',
|
||||
'/packages/path': '/base/packages/path',
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'/packages/angular': 'http://localhost:9877/base/modules/angular',
|
||||
'/packages/benchpress': 'http://localhost:9877/base/modules/benchpress',
|
||||
'/packages/core': 'http://localhost:9877/base/modules/core',
|
||||
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection',
|
||||
'/packages/reflection': 'http://localhost:9877/base/modules/reflection',
|
||||
'/packages/di': 'http://localhost:9877/base/modules/di',
|
||||
'/packages/directives': 'http://localhost:9877/base/modules/directives',
|
||||
'/packages/facade': 'http://localhost:9877/base/modules/facade',
|
||||
'/packages/forms': 'http://localhost:9877/base/modules/forms',
|
||||
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib',
|
||||
'/packages/mock': 'http://localhost:9877/base/modules/mock',
|
||||
},
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'tools/**/*.js': ['traceur']
|
||||
},
|
||||
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
outputLanguage: 'dart',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
modules: 'register',
|
||||
memberVariables: true,
|
||||
types: true,
|
||||
// typeAssertions: true,
|
||||
// typeAssertionModule: 'assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace('.js', '.dart');
|
||||
}
|
||||
'/packages/angular2': '/base/dist/dart/angular2/lib',
|
||||
'/packages/angular2_material': '/base/dist/dart/angular2_material/lib',
|
||||
'/packages/benchpress': '/base/dist/dart/benchpress/lib',
|
||||
'/packages/examples': '/base/dist/dart/examples/lib'
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
@ -87,7 +58,4 @@ module.exports = function(config) {
|
||||
|
||||
port: 9877
|
||||
});
|
||||
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
};
|
||||
|
@ -10,14 +10,16 @@ module.exports = function(config) {
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the es6-module-loader, in `test-main.js`.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**', included: false},
|
||||
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
|
||||
|
||||
'node_modules/traceur/bin/traceur-runtime.js',
|
||||
'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
||||
// 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.js',
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'node_modules/zone.js/zone.js',
|
||||
'node_modules/zone.js/long-stack-trace-zone.js',
|
||||
|
||||
@ -26,43 +28,19 @@ module.exports = function(config) {
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'modules/**/e2e_test/**'
|
||||
'dist/js/dev/es5/**/e2e_test/**',
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'modules/**/*.es6': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.js': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.es6': ['traceur'],
|
||||
},
|
||||
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
outputLanguage: 'es5',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
memberVariables: true,
|
||||
modules: 'instantiate',
|
||||
types: true,
|
||||
typeAssertions: true,
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace(/\.es6$/, '.js');
|
||||
}
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
DartiumWithWebPlatform: {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] }
|
||||
flags: ['--enable-experimental-web-platform-features'] },
|
||||
ChromeNoSandbox: {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox'] }
|
||||
},
|
||||
browsers: ['ChromeCanary'],
|
||||
|
||||
port: 9876
|
||||
});
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
karma-js.conf.js
|
6
modules/angular2/angular2.js
vendored
6
modules/angular2/angular2.js
vendored
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Define public API for Angular here.
|
||||
*/
|
||||
export * from './change_detection';
|
||||
export * from './core';
|
||||
export * from './annotations';
|
||||
export * from './directives';
|
||||
export * from './forms';
|
||||
export {Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
export * from 'angular2/src/render/api';
|
||||
export {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
|
20
modules/angular2/angular2_sfx.es6
Normal file
20
modules/angular2/angular2_sfx.es6
Normal file
@ -0,0 +1,20 @@
|
||||
import * as angular from './angular2';
|
||||
// the router should have its own SFX bundle
|
||||
// But currently the module arithemtic 'angular2/router_sfx - angular2/angular2',
|
||||
// is not support by system builder.
|
||||
import * as router from './router';
|
||||
|
||||
angular.router = router;
|
||||
|
||||
var _prevAngular = window.angular;
|
||||
|
||||
/**
|
||||
* Calling noConflict will restore window.angular to its pre-angular loading state
|
||||
* and return the angular module object.
|
||||
*/
|
||||
angular.noConflict = function() {
|
||||
window.angular = _prevAngular;
|
||||
return angular;
|
||||
};
|
||||
|
||||
window.angular = angular;
|
10
modules/angular2/annotations.js
vendored
10
modules/angular2/annotations.js
vendored
@ -1,4 +1,12 @@
|
||||
/**
|
||||
* 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 {@link Component}, {@link Directive}, and {@link View} annotations, as well as {@link Parent} and {@link Ancestor} annotations that are
|
||||
* used by Angular to resolve dependencies.
|
||||
*
|
||||
*/
|
||||
export * from './src/core/annotations/annotations';
|
||||
export * from './src/core/annotations/decorators';
|
||||
|
95
modules/angular2/change_detection.js
vendored
95
modules/angular2/change_detection.js
vendored
@ -1,72 +1,29 @@
|
||||
export {AST} from './src/change_detection/parser/ast';
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Change detection enables data binding in Angular.
|
||||
*/
|
||||
|
||||
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}
|
||||
from './src/change_detection/proto_change_detector';
|
||||
export {DynamicChangeDetector}
|
||||
from './src/change_detection/dynamic_change_detector';
|
||||
export * from './src/change_detection/pipes/pipe_registry';
|
||||
export {Locals} from './src/change_detection/parser/locals';
|
||||
|
||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions';
|
||||
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 {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
|
||||
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
|
||||
export {ChangeDetectorRef} from './src/change_detection/change_detector_ref';
|
||||
export {PipeRegistry} 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}
|
||||
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 {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;
|
||||
}
|
||||
}
|
||||
|
||||
export var defaultPipes = {
|
||||
"iterableDiff" : [
|
||||
new ArrayChangesFactory(),
|
||||
new NullPipeFactory()
|
||||
],
|
||||
"keyValDiff" : [
|
||||
new KeyValueChangesFactory(),
|
||||
new NullPipeFactory()
|
||||
]
|
||||
};
|
||||
|
||||
export class DynamicChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
constructor(registry:PipeRegistry) {
|
||||
super();
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||
return new DynamicProtoChangeDetector(this.registry);
|
||||
}
|
||||
}
|
||||
|
||||
export class JitChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
constructor(registry:PipeRegistry) {
|
||||
super();
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||
return new JitProtoChangeDetector(this.registry);
|
||||
}
|
||||
}
|
||||
|
||||
var _registry = new PipeRegistry(defaultPipes);
|
||||
|
||||
export var dynamicChangeDetection = new DynamicChangeDetection(_registry);
|
||||
export var jitChangeDetection = new JitChangeDetection(_registry);
|
||||
export {WrappedValue, Pipe} from './src/change_detection/pipes/pipe';
|
||||
export {
|
||||
defaultPipes, DynamicChangeDetection, JitChangeDetection, defaultPipeRegistry
|
||||
} from './src/change_detection/change_detection';
|
||||
|
26
modules/angular2/core.js
vendored
26
modules/angular2/core.js
vendored
@ -1,13 +1,27 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Define angular core API 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/query_list';
|
||||
|
||||
export * from './src/core/compiler/compiler';
|
||||
|
||||
export * from './src/core/compiler/template_loader';
|
||||
export * from './src/core/compiler/view';
|
||||
export * from './src/core/compiler/view_container';
|
||||
export * from './src/core/compiler/binding_propagation_config';
|
||||
// TODO(tbosch): remove this once render migration is complete
|
||||
export * from './src/render/dom/compiler/template_loader';
|
||||
export * from './src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
export * from './src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
export * from './src/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy';
|
||||
export * from './src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
export * from './src/core/compiler/dynamic_component_loader';
|
||||
export {ViewRef, ProtoViewRef} from './src/core/compiler/view_ref';
|
||||
export {ViewContainerRef} from './src/core/compiler/view_container_ref';
|
||||
export {ElementRef} from './src/core/compiler/element_ref';
|
||||
|
||||
export * from './src/core/dom/element';
|
||||
|
7
modules/angular2/di.js
vendored
7
modules/angular2/di.js
vendored
@ -1,7 +0,0 @@
|
||||
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 {Key, KeyRegistry} from './src/di/key';
|
||||
export {KeyMetadataError, NoProviderError, ProviderError, AsyncBindingError, CyclicDependencyError,
|
||||
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions';
|
||||
export {OpaqueToken} from './src/di/opaque_token';
|
27
modules/angular2/di.ts
Normal file
27
modules/angular2/di.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* The `di` module provides dependency injection container services.
|
||||
*/
|
||||
|
||||
export * from './src/di/annotations';
|
||||
export * from './src/di/decorators';
|
||||
export {Injector} from './src/di/injector';
|
||||
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
|
||||
export {Key, KeyRegistry} from './src/di/key';
|
||||
export {
|
||||
NoBindingError,
|
||||
AbstractBindingError,
|
||||
AsyncBindingError,
|
||||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidBindingError,
|
||||
NoAnnotationError
|
||||
} from './src/di/exceptions';
|
||||
export {OpaqueToken} from './src/di/opaque_token';
|
||||
|
||||
// HACK: workaround for Traceur behavior.
|
||||
// It expects all transpiled modules to contain this marker.
|
||||
// TODO: remove this when we no longer use traceur
|
||||
export var __esModule = true;
|
7
modules/angular2/di_annotations.js
vendored
Normal file
7
modules/angular2/di_annotations.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Annotations which control how the dependencies are resolved by the {@link Injector}.
|
||||
*/
|
||||
|
7
modules/angular2/di_errors.js
vendored
Normal file
7
modules/angular2/di_errors.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Errors thrown by the {@link Injector}.
|
||||
*/
|
||||
|
10
modules/angular2/directives.js
vendored
10
modules/angular2/directives.js
vendored
@ -1,4 +1,12 @@
|
||||
export * from './src/directives/foreach';
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Common directives shipped with Angualr.
|
||||
*/
|
||||
|
||||
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';
|
||||
|
@ -5,7 +5,7 @@ projected to DOM as well as which DOM events should invoke which methods on the
|
||||
syntax which is core to Angular and allows for data-binding, event-binding, template-instantiation.
|
||||
|
||||
The design of the template syntax has these properties:
|
||||
|
||||
|
||||
|
||||
* All data-binding expressions are easily identifiable. (i.e. there is never an ambiguity whether the value should be
|
||||
interpreted as string literal or as an expression.)
|
||||
@ -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>
|
||||
```
|
||||
@ -282,7 +282,7 @@ Key points:
|
||||
* The binding is to the element property not the element attribute.
|
||||
* To prevent custom element from accidentally reading the literal `expression` on the title element, the attribute name
|
||||
is escaped. In our case the `title` is escaped to `[title]` through the addition of square brackets `[]`.
|
||||
* A binding value (in this case `user.firstName` will always be an expression, never a string literal)
|
||||
* A binding value (in this case `user.firstName` will always be an expression, never a string literal).
|
||||
|
||||
NOTE: Unlike Angular v1, Angular v2 binds to properties of elements rather than attributes of elements. This is
|
||||
done to better support custom elements, and to allow binding for values other than strings.
|
||||
@ -372,8 +372,8 @@ Where:
|
||||
inserted. The template can be defined implicitly with `template` attribute, which turns the current element into
|
||||
a template, or explicitly with `<template>` element. Explicit declaration is longer, but it allows for having
|
||||
templates which have more than one root DOM node.
|
||||
* `viewport` is required for templates. The Viewport directive is responsible for deciding when
|
||||
and in which order should child views be inserted into this location. An Viewport directive usually has one or
|
||||
* `viewport` is required for templates. The directive is responsible for deciding when
|
||||
and in which order should child views be inserted into this location. Such a directive usually has one or
|
||||
more bindings and can be represented as either `viewport-directive-bindings` or
|
||||
`viewport-directive-microsyntax` on `template` element or attribute. See template microsyntax for more details.
|
||||
|
||||
@ -387,7 +387,7 @@ Hello {{user}}!
|
||||
</div>
|
||||
```
|
||||
|
||||
In the above example the `if` Viewport determines whether the child view (an instance of the child template) should be
|
||||
In the above example the `if` directive determines whether the child view (an instance of the child template) should be
|
||||
inserted into the root view. The `if` makes this decision based on if the `isAdministrator` binding is true.
|
||||
|
||||
The above example is in the short form, for better clarity let's rewrite it in the canonical form, which is functionally
|
||||
@ -402,28 +402,26 @@ Hello {{user}}!
|
||||
</template>
|
||||
```
|
||||
|
||||
NOTE: Only Viewport directives can be placed on the template element. (Decorators and Components are not allowed.)
|
||||
|
||||
|
||||
### 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 +429,7 @@ syntax is preferable.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="foreach; #person; in=people; #i=index;">{{i}}. {{person}}<li>
|
||||
<li template="for; #person; of=people; #i=index;">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -441,19 +439,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.
|
||||
|
||||
@ -480,7 +487,7 @@ Where
|
||||
|
||||
|
||||
NOTE: the `template` attribute must be present to make it clear to the user that a sub-template is being created. This
|
||||
goes along the philosophy that the developer should be able to reason about the template without understanding the
|
||||
goes along with the philosophy that the developer should be able to reason about the template without understanding the
|
||||
semantics of the instantiator directive.
|
||||
|
||||
|
||||
@ -503,13 +510,14 @@ Binding events allows wiring events from DOM (or other components) to the Angula
|
||||
|
||||
Where:
|
||||
* `some-element` Any element which can generate DOM events (or has an angular directive which generates the event).
|
||||
* `some-event` (escaped with `()` or `bind-`) is the name of the event `some-event`. In this case the
|
||||
* `some-event` (escaped with `()` or `on-`) is the name of the event `some-event`. In this case the
|
||||
dash-case is converted into camel-case `someEvent`.
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
If the execution of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
||||
|
||||
By default, angular only listens to the element on the event, and ignores events which bubble. To listen to bubbled
|
||||
events (as in the case of clicking on any child) use the bubble option (`(^event)` or `on-bubble-event`) as shown
|
||||
bellow.
|
||||
events (as in the case of clicking on any child) use the bubble option (`(event)` or `on-bubble-event`) as shown
|
||||
below.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -6,17 +6,12 @@ Directives are the cornerstone of an Angular application. We use Directives to b
|
||||
|
||||
Angular applications do not have a main method. Instead they have a root Component. Dependency Injection then assembles the directives into a working Angular application.
|
||||
|
||||
There are three different kinds of directives (described in more detail in later sections).
|
||||
|
||||
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)
|
||||
|
||||
Directives with an encapsulated view and an optional injector are called *Components*.
|
||||
|
||||
|
||||
## CSS Selectors
|
||||
|
||||
Decorators are instantiated whenever the decorator CSS selector matches the DOM structure.
|
||||
Directives are instantiated whenever the CSS selector matches the DOM structure.
|
||||
|
||||
Angular supports these CSS selector constructs:
|
||||
* Element name: `name`
|
||||
@ -29,7 +24,7 @@ Angular supports these CSS selector constructs:
|
||||
|
||||
Angular does not support these (and any CSS selector which crosses element boundaries):
|
||||
* Descendant: `body div`
|
||||
* Direct descendant: `body > div`
|
||||
* Direct descendant: `body > div`
|
||||
* Adjacent: `div + table`
|
||||
* Sibling: `div ~ table`
|
||||
* Wildcard: `*`
|
||||
@ -50,33 +45,33 @@ These CSS selectors will match:
|
||||
|
||||
CSS Selectors can be combined:
|
||||
* `input[type=text]`: Triggers on element name `input` which is of `type` `text`.
|
||||
* `input[type=text], textarea`: triggers on element name `input` which is of `type` `text` or element name `textarea`
|
||||
* `input[type=text], textarea`: triggers on element name `input` which is of `type` `text` or element name `textarea`.
|
||||
|
||||
|
||||
|
||||
## Decorators
|
||||
## Directives
|
||||
|
||||
The simplest kind of directive is a decorator. Directives are usefull for encapsulating behavior.
|
||||
|
||||
* Multiple decorators can be placed on a single element.
|
||||
* Decorators do not introduce new evaluation context.
|
||||
* Decorators are registered through the `@Decorator` meta-data annotation.
|
||||
* Directives do not introduce new evaluation context.
|
||||
* Directives are registered through the `@Directive` meta-data annotation.
|
||||
|
||||
Here is a trivial example of a tooltip decorator. The directive will log a tooltip into the console on every time mouse enters a region:
|
||||
|
||||
```
|
||||
@Decorator({
|
||||
@Directive({
|
||||
selector: '[tooltip]', // CSS Selector which triggers the decorator
|
||||
bind: { // List which properties need to be bound
|
||||
text: 'tooltip' // - DOM element tooltip property should be
|
||||
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.
|
||||
mouseover: 'show' // - Invoke the show() method every time
|
||||
hostListeners: { // List which events need to be mapped.
|
||||
mouseover: 'show' // - Invoke the show() method every time
|
||||
} // the mouseover event is fired.
|
||||
})
|
||||
class Form { // Directive controller class, instantiated
|
||||
// when CSS matches.
|
||||
text:string; // text property on the Decorator Controller.
|
||||
text:string; // text property on the Directive Controller.
|
||||
|
||||
show(event) { // Show method which implements the show action.
|
||||
console.log(this.text);
|
||||
@ -112,23 +107,23 @@ 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
|
||||
title:string; | - title property
|
||||
open:boolean;
|
||||
|
||||
|
||||
constructor() {
|
||||
this.title = '';
|
||||
this.open = true;
|
||||
}
|
||||
|
||||
|
||||
// Public API
|
||||
toggle() => this.open = !this.open;
|
||||
open() => this.open = true;
|
||||
@ -140,7 +135,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>
|
||||
@ -163,39 +158,39 @@ Example of usage:
|
||||
|
||||
|
||||
|
||||
## Viewport
|
||||
## Directives that use a ViewContainer
|
||||
|
||||
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `foreach`.)
|
||||
Directives that use a ViewContainer 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.
|
||||
* The viewport is created over the `template` element. This is known as the `ViewContainer`.
|
||||
* Viewport can insert child views into the `ViewContainer`. The child views show up as siblings of the `Viewport` in the DOM.
|
||||
* Every `template` element creates a `ProtoView` which can be used to create Views via the ViewContainer.
|
||||
* The child views show up as siblings of the directive in the DOM.
|
||||
|
||||
>> TODO(misko): Relationship with Injection
|
||||
>> TODO(misko): Instantiator can not be injected into child Views
|
||||
|
||||
|
||||
```
|
||||
@Viewport({
|
||||
@Directive({
|
||||
selector: '[if]',
|
||||
bind: {
|
||||
properties: {
|
||||
'condition': 'if'
|
||||
}
|
||||
})
|
||||
export class If {
|
||||
viewContainer: ViewContainer;
|
||||
viewContainer: ViewContainerRef;
|
||||
protoViewRef: ProtoViewRef;
|
||||
view: View;
|
||||
|
||||
constructor(viewContainer: ViewContainer) {
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
this.viewContainer = viewContainer;
|
||||
this.protoViewRef = protoViewRef;
|
||||
this.view = null;
|
||||
}
|
||||
|
||||
set condition(value) {
|
||||
if (value) {
|
||||
if (this.view === null) {
|
||||
this.view = this.viewContainer.create();
|
||||
this.view = this.viewContainer.create(protoViewRef);
|
||||
}
|
||||
} else {
|
||||
if (this.view !== null) {
|
||||
@ -213,37 +208,37 @@ Dependency Injection (DI) is a key aspect of directives. DI allows directives to
|
||||
|
||||
When Angular directives are instantiated, the directive can ask for other related directives to be injected into it. By assembling the directives in different order and subtypes the application behavior can be controlled. A good mental model is that the DOM structure controls the directive instantiation graph.
|
||||
|
||||
Directive instantiation is triggered by the directive CSS selector matching the DOM structure. The directive in its constructor can ask for other directives or application services. When asking for directives the dependency is locating by following the DOM hierarchy and if not found using the application level injector.
|
||||
Directive instantiation is triggered by the directive CSS selector matching the DOM structure. In a directive's constructor, it can ask for other directives or application services. When asking for directives, the dependency is attempted to be located by its DOM hierarchy first, then if not found, by using the application level injector.
|
||||
|
||||
To better understand the kinds of injections which are supported in Angular we have broken them down into use case examples.
|
||||
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
```
|
||||
class MyService {} | Assume a service which needs to be injected
|
||||
class MyService {} | Assume a service which needs to be injected
|
||||
| into a directive.
|
||||
|
|
||||
@Component({ | Assume a top level application component which
|
||||
@Component({ | Assume a top level application component which
|
||||
selector: 'my-app', | configures the services to be injected.
|
||||
services: [MyService] |
|
||||
injectables: [MyService] |
|
||||
}) |
|
||||
@Template({ | Assume we have a template that needs to be
|
||||
url: 'my_app.html', | configured with directives to be injected.
|
||||
directives: [House] |
|
||||
@View({ | Assume we have a template that needs to be
|
||||
templateUrl: 'my_app.html', | configured with directives to be injected.
|
||||
directives: [House] |
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@Decorator({ | This is the directive into which we would like
|
||||
@Directive({ | This is the directive into which we would like
|
||||
selector: '[house]' | to inject the MyService.
|
||||
}) |
|
||||
class House { |
|
||||
constructor(myService:MyService) { | Notice that in the constructor we can simply
|
||||
constructor(myService:MyService) { | Notice that in the constructor we can simply
|
||||
} | ask for MyService.
|
||||
} |
|
||||
|
||||
@ -252,7 +247,7 @@ class House { |
|
||||
|
||||
Assume the following DOM structure for `my_app.html`:
|
||||
```
|
||||
<div house> | The house attribute triggers the creation of the House directive.
|
||||
<div house> | The house attribute triggers the creation of the House directive.
|
||||
</div> | This is equivalent to:
|
||||
| new House(injector.get(MyService));
|
||||
```
|
||||
@ -260,58 +255,58 @@ Assume the following DOM structure for `my_app.html`:
|
||||
|
||||
### Injecting other Directives
|
||||
|
||||
Injecting other directives into directives follows a similar mechanism as injecting services, but with added constraint of visibility governed by DOM structure.
|
||||
Injecting other directives into directives follows a similar mechanism as injecting services into directives, but with added constraint of visibility governed by DOM structure.
|
||||
|
||||
There are five kinds of visibilities:
|
||||
|
||||
* (no annotation): Inject dependant directives only if they are on the current element.
|
||||
* (no annotation): Inject dependant directives only if they are on the current element.
|
||||
* `@ancestor`: Inject a directive if it is at any element above the current element.
|
||||
* `@parent`: Inject a directive which is direct parent of the current element.
|
||||
* `@parent`: Inject a directive which is a direct parent of the current element.
|
||||
* `@child`: Inject a list of direct children which match a given type. (Used with `Query`)
|
||||
* `@descendant`: Inject a list of any children which match a given type. (Used with `Query`)
|
||||
|
||||
NOTE: if the injection constraint can not be satisfied by the current visibility constraint, then it is forward to the normal injector which may provide a default value for the directive or it may throw an error.
|
||||
NOTE: if the injection constraint can not be satisfied by the current visibility constraint, then it is forwarded to the normal injector which either provides a default value for the directive or throws an error.
|
||||
|
||||
Here is an example of the kinds of injections which can be achieved:
|
||||
|
||||
|
||||
```
|
||||
@Component({ |
|
||||
selector: 'my-app', |
|
||||
template: new TemplateConfig({ |
|
||||
url: 'my_app.html', |
|
||||
directives: [Form, FieldSet, |
|
||||
Field, Primary] |
|
||||
}) |
|
||||
selector: 'my-app' |
|
||||
}) |
|
||||
@View({ |
|
||||
templateUrl: 'my_app.html', |
|
||||
directives: [Form, FieldSet, |
|
||||
Field, Primary] |
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@Decorator({ selector: 'form' }) |
|
||||
@Directive({ selector: 'form' }) |
|
||||
class Form { |
|
||||
constructor( |
|
||||
@descendant sets:Query<FieldSet> |
|
||||
) { |
|
||||
} |
|
||||
) { |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Decorator({ selector: 'fieldset' }) |
|
||||
@Directive({ selector: 'fieldset' }) |
|
||||
class FieldSet { |
|
||||
constructor( |
|
||||
@child sets:Query<Field> |
|
||||
) { ... } |
|
||||
) { ... } |
|
||||
} |
|
||||
|
|
||||
@Decorator({ selector: 'field' }) |
|
||||
@Directive({ selector: 'field' }) |
|
||||
class Field { |
|
||||
constructor( |
|
||||
@ancestor field:Form, |
|
||||
@parent field:FieldSet, |
|
||||
) { ... } |
|
||||
) { ... } |
|
||||
} |
|
||||
|
|
||||
@Decorator({ selector: '[primary]'}) |
|
||||
@Directive({ selector: '[primary]'}) |
|
||||
class Primary { |
|
||||
constructor(field:Field ) { ... } |
|
||||
constructor(field:Field ) { ... } |
|
||||
} |
|
||||
```
|
||||
|
||||
@ -322,17 +317,75 @@ Assume the following DOM structure for `my_app.html`:
|
||||
<fieldset> |
|
||||
<field primary></field> |
|
||||
<field></field> |
|
||||
</div> |
|
||||
</fieldset> |
|
||||
</form> |
|
||||
</fieldset> |
|
||||
</div> |
|
||||
</form> |
|
||||
```
|
||||
|
||||
#### Shadow DOM effects on Directive DI
|
||||
|
||||
### Shadow DOM effects on Dependency Injection
|
||||
Shadow DOM provides an encapsulation for components, so as a general rule it does not allow directive injections to cross the shadow DOM boundaries. To remedy this, declaritively specify the required component as an injectable.
|
||||
|
||||
Shadow DOM provides an encapsulation for components, so as a general rule it does not allow directive injections to cross the shadow DOM boundaries.
|
||||
```
|
||||
@Component({
|
||||
selector: '[kid]',
|
||||
injectables: []
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'kid.html',
|
||||
directives: []
|
||||
})
|
||||
class Kid {
|
||||
constructor(
|
||||
@Parent() dad:Dad,
|
||||
@Optional() grandpa:Grandpa
|
||||
) {
|
||||
this.name = 'Billy';
|
||||
this.dad = dad.name;
|
||||
this.grandpa = grandpa.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[dad]',
|
||||
injectables: [Grandpa]
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'dad.html',
|
||||
directives: [Kid]
|
||||
})
|
||||
class Dad {
|
||||
constructor(@Parent() dad:Grandpa) {
|
||||
this.name = 'Joe Jr';
|
||||
this.dad = dad.name;
|
||||
console.log(dad)
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[grandpa]',
|
||||
injectables: []
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'grandpa.html',
|
||||
directives: [Dad]
|
||||
})
|
||||
class Grandpa {
|
||||
constructor() {
|
||||
this.name = 'Joe';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `grandpa.html`: The Dad has access to the Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Children: <div dad></div>
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `dad.html`: Here the rendered Kid will also have access to Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Dad: {{dad}} <br> Children: <div kid></div>
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
|
@ -2,24 +2,24 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This document explains the concept of a View.
|
||||
A View is a core primitive used by angular to render the DOM tree.
|
||||
A ViewPort is location in a View which can accept child Views.
|
||||
Every ViewPort has an associated ViewContainer than can contain any number of child Views.
|
||||
This document explains the concept of a View.
|
||||
A View is a core primitive used by angular to render the DOM tree.
|
||||
A ViewContainer is location in a View which can accept child Views.
|
||||
Every ViewContainer has an associated ViewContainerRef than can contain any number of child Views.
|
||||
Views form a tree structure which mimics the DOM tree.
|
||||
|
||||
* View is a core rendering construct. A running application is just a collection of Views which are
|
||||
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
|
||||
* View is a core rendering construct. A running application is just a collection of Views which are
|
||||
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
|
||||
can change properties, they can not change structurally. (Structural changes such as, adding or
|
||||
* 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
|
||||
* View can have zero or more ViewContainers. A ViewContainer 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
|
||||
* View contains a context object. The context represents the object instance against which all
|
||||
expressions are evaluated.
|
||||
* View contains a ChangeDetector for looking for detecting changes to the model.
|
||||
* View contains ElementInjector for creating Directives.
|
||||
@ -40,7 +40,7 @@ class Greeter {
|
||||
}
|
||||
```
|
||||
|
||||
And assume following HTML Template:
|
||||
And assume following HTML View:
|
||||
|
||||
```
|
||||
<div>
|
||||
@ -51,9 +51,9 @@ And assume following HTML Template:
|
||||
</div>
|
||||
```
|
||||
|
||||
The above template is compiled by the Compiler to create a ProtoView. The ProtoView is then used to
|
||||
create an instance of the View. The instantiation process involves cloning the above template and
|
||||
locating all of the elements which contain bindings and finally instantiating the Directives
|
||||
The above template is compiled by the Compiler to create a ProtoView. The ProtoView is then used to
|
||||
create an instance of the View. The instantiation process involves cloning the above template and
|
||||
locating all of the elements which contain bindings and finally instantiating the Directives
|
||||
associated with the template. (See compilation for more details.)
|
||||
|
||||
```
|
||||
@ -81,16 +81,16 @@ Note:
|
||||
* View knows which expressions need to be watched.
|
||||
* View knows what needs to be updated if the watched expression changes.
|
||||
* All DOM elements are owned by single instance of the view.
|
||||
* The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need
|
||||
* The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need
|
||||
to understand Composed View.
|
||||
|
||||
|
||||
## Composed View
|
||||
|
||||
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.
|
||||
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 ViewContainer.
|
||||
|
||||
Let's start with a Template such as:
|
||||
Let's start with a View such as:
|
||||
|
||||
```
|
||||
<ul>
|
||||
@ -108,7 +108,7 @@ and
|
||||
|
||||
```
|
||||
<ul> | protoViewA(someContext)
|
||||
<template></template> | protoViewA(someContext): new ProtoViewPort(protoViewB)
|
||||
<template></template> | protoViewA(someContext): protoViewB
|
||||
</ul> | protoViewA(someContext)
|
||||
```
|
||||
|
||||
@ -119,49 +119,49 @@ The next step is to compose these two ProtoViews into an actual view which is re
|
||||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewContainer(protoViewB))
|
||||
</ul> | viewA(someContext)
|
||||
```
|
||||
|
||||
*Step2:* Instantiate `Foreach` directive which will receive the `ViewContainer`. (The ViewContainer
|
||||
*Step2:* Instantiate `Foreach` directive which will receive the `ViewContainerRef`. (The ViewContainerRef
|
||||
has a reference to `protoViewA`).
|
||||
|
||||
|
||||
*Step3:* As the `Foreach` directive unrolls it asks the `ViewContainer` to instantiate `protoViewB` and insert
|
||||
it after the `ViewPort` anchor. This is repeated for each `person` in `people`. Notice that
|
||||
*Step3:* As the `Foreach` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert
|
||||
it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that
|
||||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewContainer(protoViewB))
|
||||
<li>{{person}}</li> | viewB0(locals0(someContext))
|
||||
<li>{{person}}</li> | viewB1(locals0(someContext))
|
||||
</ul> | viewA(someContext)
|
||||
```
|
||||
|
||||
*Step4:* All of the bindings in the child Views are updated. Notice that in the case of `Foreach`
|
||||
the evaluation context for the `viewB0` and `viewB1` are `locals0` and `locals1` respectively.
|
||||
Locals allow the introduction of new local variables visible only within the scope of the View, and
|
||||
*Step4:* All of the bindings in the child Views are updated. Notice that in the case of `Foreach`
|
||||
the evaluation context for the `viewB0` and `viewB1` are `locals0` and `locals1` respectively.
|
||||
Locals allow the introduction of new local variables visible only within the scope of the View, and
|
||||
delegate any unknown references to the parent context.
|
||||
|
||||
```
|
||||
<ul> | viewA
|
||||
<template></template> | viewA: new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA: new Foreach(new ViewContainer(protoViewB))
|
||||
<li>Alice</li> | viewB0
|
||||
<li>Bob</li> | viewB1
|
||||
</ul> | viewA
|
||||
```
|
||||
|
||||
Each View can have zero or more ViewPorts. By inserting and removing child Views to and from the
|
||||
ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain
|
||||
individual nodes or a complex DOM structure. The insertion points for the child Views, known as
|
||||
ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or
|
||||
a `script` element depending on your browser. It is used to identify where the child Views will be
|
||||
Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the
|
||||
ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain
|
||||
individual nodes or a complex DOM structure. The insertion points for the child Views, known as
|
||||
ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or
|
||||
a `script` element depending on your browser. It is used to identify where the child Views will be
|
||||
inserted.
|
||||
|
||||
## Component Views
|
||||
|
||||
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
|
||||
rendering state. Unlike ViewPorts which can contain zero or more Views, the Component always contains
|
||||
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
|
||||
rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains
|
||||
exactly one Shadow View.
|
||||
|
||||
```
|
||||
@ -194,7 +194,7 @@ class Greeter {
|
||||
}
|
||||
```
|
||||
|
||||
And assume the following HTML Template:
|
||||
And assume the following HTML View:
|
||||
|
||||
```
|
||||
<div> | viewA(greeter)
|
||||
@ -205,7 +205,7 @@ And assume the following HTML Template:
|
||||
</div> | viewA(greeter)
|
||||
```
|
||||
|
||||
The above UI is built using a single View, and hence a single context `greeter`. It can be expressed
|
||||
The above UI is built using a single View, and hence a single context `greeter`. It can be expressed
|
||||
in this pseudo-code.
|
||||
|
||||
```
|
||||
@ -215,15 +215,15 @@ var greeter = new Greeter();
|
||||
The View contains two bindings:
|
||||
|
||||
1. `greeting`: This is bound to the `greeting` property on the `Greeter` instance.
|
||||
2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve
|
||||
2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve
|
||||
this we wrap the `Greeter` instance in the `Local` instance like so:
|
||||
```
|
||||
var greeter = new Locals(new Greeter(), {name: ref_to_input_element })
|
||||
```
|
||||
|
||||
|
||||
By wrapping the `Greeter` instance into the `Locals` we allow the view to introduce variables which
|
||||
are in addition to the `Greeter` instance. During the resolution of the expressions we first check
|
||||
By wrapping the `Greeter` instance into the `Locals` we allow the view to introduce variables which
|
||||
are in addition to the `Greeter` instance. During the resolution of the expressions we first check
|
||||
the locals, and then the `Greeter` instance.
|
||||
|
||||
|
||||
@ -233,14 +233,14 @@ the locals, and then the `Greeter` instance.
|
||||
Views transition through a particular set of states:
|
||||
|
||||
1. View is created from the ProtoView.
|
||||
2. View can be attached to an existing ViewContainer.
|
||||
3. Upon attaching View to the ViewContainer the View needs to be hydrated. The hydration process
|
||||
2. View can be attached to an existing ViewContainerRef.
|
||||
3. Upon attaching View to the ViewContainerRef the View needs to be hydrated. The hydration process
|
||||
involves instantiating all of the Directives associated with the current View.
|
||||
4. At this point the view is ready and renderable. Multiple changes can be delivered to the
|
||||
4. At this point the view is ready and renderable. Multiple changes can be delivered to the
|
||||
Directives from the ChangeDetection.
|
||||
5. At some point the View can be removed. At this point all of the directives are destroyed during
|
||||
5. At some point the View can be removed. At this point all of the directives are destroyed during
|
||||
the dehydration process and the view becomes inactive.
|
||||
6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused
|
||||
6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused
|
||||
because an animation is animating the view away.
|
||||
7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the
|
||||
7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the
|
||||
application to be faster in subsequent renderings.
|
||||
|
@ -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
|
||||
]);
|
||||
@ -316,8 +316,8 @@ ctrlPromise.then((ctrl) {
|
||||
expect(ctrl).toBeAnInstanceOf(UserController);
|
||||
expect(ctrl.ul).toBeAnInstanceOf(UserList);
|
||||
});
|
||||
// No synchronous provider for UserList, results in a NoProviderError.
|
||||
expect(() => inj.get(UserController)).toThrow(new NoProviderError(...));
|
||||
// No synchronous provider for UserList, results in a NoBindingError.
|
||||
expect(() => inj.get(UserController)).toThrow(new NoBindingError(...));
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||
|
14
modules/angular2/forms.js
vendored
14
modules/angular2/forms.js
vendored
@ -1,5 +1,17 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This module is used for handling user input, by defining and building a {@link ControlGroup} that consists of
|
||||
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used to read information
|
||||
* from the form DOM elements.
|
||||
*
|
||||
* This module is not included in the `angular2` module; you must import the forms module explicitly.
|
||||
*
|
||||
*/
|
||||
|
||||
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';
|
||||
|
3
modules/angular2/globals.dart
Normal file
3
modules/angular2/globals.dart
Normal file
@ -0,0 +1,3 @@
|
||||
// Globals are provided by lang.dart in Dart.
|
||||
// This file exists to prevent global.ts from being transpiled.
|
||||
library angular2.globals;
|
23
modules/angular2/globals.ts
Normal file
23
modules/angular2/globals.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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;
|
||||
gc(): void;
|
||||
Reflect: any;
|
||||
}
|
@ -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.
|
||||
*/
|
@ -7,13 +7,20 @@ authors:
|
||||
description: Angular 2 for Dart - a web framework for modern web apps
|
||||
homepage: <%= packageJson.homepage %>
|
||||
environment:
|
||||
sdk: '>=1.9.0-dev.8.0'
|
||||
sdk: '>=1.10.0-dev.1.10 <2.0.0'
|
||||
dependencies:
|
||||
analyzer: '^0.22.4'
|
||||
analyzer: '>=0.24.4 <0.26.0'
|
||||
barback: '^0.15.2+2'
|
||||
code_transformers: '^0.2.5'
|
||||
code_transformers: '^0.2.8'
|
||||
dart_style: '^0.1.3'
|
||||
html5lib: '^0.12.0'
|
||||
html: '^0.12.0'
|
||||
logging: '>=0.9.0 <0.11.0'
|
||||
source_span: '^1.0.0'
|
||||
stack_trace: '^1.1.1'
|
||||
dev_dependencies:
|
||||
guinness: "^0.1.17"
|
||||
transformers:
|
||||
- angular2
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --show-package-warnings
|
||||
|
30
modules/angular2/router.js
vendored
Normal file
30
modules/angular2/router.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Maps application URLs into application states, to support deep-linking and navigation.
|
||||
*/
|
||||
|
||||
|
||||
export {Router} from './src/router/router';
|
||||
export {RouterOutlet} from './src/router/router_outlet';
|
||||
export {RouterLink} from './src/router/router_link';
|
||||
export {RouteParams} from './src/router/instruction';
|
||||
export * from './src/router/route_config_annotation';
|
||||
export * from './src/router/route_config_decorator';
|
||||
|
||||
import {Router, RootRouter} from './src/router/router';
|
||||
import {RouteRegistry} from './src/router/route_registry';
|
||||
import {Pipeline} from './src/router/pipeline';
|
||||
import {Location} from './src/router/location';
|
||||
import {appComponentAnnotatedTypeToken} from './src/core/application_tokens';
|
||||
import {bind} from './di';
|
||||
|
||||
export var routerInjectables:List = [
|
||||
RouteRegistry,
|
||||
Pipeline,
|
||||
Location,
|
||||
bind(Router).toFactory((registry, pipeline, location, meta) => {
|
||||
return new RootRouter(registry, pipeline, location, meta.type);
|
||||
}, [RouteRegistry, Pipeline, Location, appComponentAnnotatedTypeToken])
|
||||
];
|
@ -1,25 +1,40 @@
|
||||
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 {ChangeDetectorRef} from './change_detector_ref';
|
||||
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;
|
||||
ref:ChangeDetectorRef;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.children = [];
|
||||
this.mode = CHECK_ALWAYS;
|
||||
this.lightDomChildren = [];
|
||||
this.shadowDomChildren = [];
|
||||
this.ref = new ChangeDetectorRef(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;
|
||||
}
|
||||
|
||||
removeShadowDomChild(cd:ChangeDetector) {
|
||||
ListWrapper.remove(this.shadowDomChildren, cd);
|
||||
}
|
||||
|
||||
remove() {
|
||||
@ -38,20 +53,37 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
markAsCheckOnce() {
|
||||
this.mode = CHECK_ONCE;
|
||||
}
|
||||
|
||||
markPathToRootAsCheckOnce() {
|
||||
var c = this;
|
||||
while(isPresent(c) && c.mode != DETACHED) {
|
||||
|
68
modules/angular2/src/change_detection/binding_record.js
vendored
Normal file
68
modules/angular2/src/change_detection/binding_record.js
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {SetterFn} from 'angular2/src/reflection/types';
|
||||
import {AST} from './parser/ast';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
|
||||
const DIRECTIVE="directive";
|
||||
const ELEMENT="element";
|
||||
const TEXT_NODE="textNode";
|
||||
|
||||
export class BindingRecord {
|
||||
mode:string;
|
||||
ast:AST;
|
||||
|
||||
implicitReceiver:any; //number | DirectiveIndex
|
||||
elementIndex:number;
|
||||
propertyName:string;
|
||||
setter:SetterFn;
|
||||
|
||||
directiveRecord:DirectiveRecord;
|
||||
|
||||
constructor(mode:string, implicitReceiver:any, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
this.mode = mode;
|
||||
this.implicitReceiver = implicitReceiver;
|
||||
this.ast = ast;
|
||||
|
||||
this.elementIndex = elementIndex;
|
||||
this.propertyName = propertyName;
|
||||
this.setter = setter;
|
||||
|
||||
this.directiveRecord = directiveRecord;
|
||||
}
|
||||
|
||||
callOnChange() {
|
||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
|
||||
}
|
||||
|
||||
isOnPushChangeDetection() {
|
||||
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
|
||||
}
|
||||
|
||||
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, 0, ast, 0, propertyName, setter, directiveRecord);
|
||||
}
|
||||
|
||||
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
|
||||
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
|
||||
}
|
||||
|
||||
static createForHostProperty(directiveIndex:DirectiveIndex, ast:AST, propertyName:string) {
|
||||
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex, propertyName, null, null);
|
||||
}
|
||||
|
||||
static createForTextNode(ast:AST, elementIndex:number) {
|
||||
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
|
||||
}
|
||||
}
|
96
modules/angular2/src/change_detection/change_detection.js
vendored
Normal file
96
modules/angular2/src/change_detection/change_detection.js
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
|
||||
import {PipeFactory} from './pipes/pipe';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {IterableChangesFactory} from './pipes/iterable_changes';
|
||||
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
|
||||
import {AsyncPipeFactory} from './pipes/async_pipe';
|
||||
import {NullPipeFactory} from './pipes/null_pipe';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {DEFAULT} from './constants';
|
||||
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* Structural diffing for `Object`s and `Map`s.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export var keyValDiff:List<PipeFactory> = [
|
||||
new KeyValueChangesFactory(),
|
||||
new NullPipeFactory()
|
||||
];
|
||||
|
||||
/**
|
||||
* Structural diffing for `Iterable` types such as `Array`s.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export var iterableDiff:List<PipeFactory> = [
|
||||
new IterableChangesFactory(),
|
||||
new NullPipeFactory()
|
||||
];
|
||||
|
||||
/**
|
||||
* Async binding to such types as Observable.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export var async:List<PipeFactory> = [
|
||||
new AsyncPipeFactory(),
|
||||
new NullPipeFactory()
|
||||
];
|
||||
|
||||
export var defaultPipes:Map<String, List<PipeFactory>> = {
|
||||
"iterableDiff" : iterableDiff,
|
||||
"keyValDiff" : keyValDiff,
|
||||
"async" : async
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Implements change detection that does not require `eval()`.
|
||||
*
|
||||
* This is slower than {@link JitChangeDetection}.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
@Injectable()
|
||||
export class DynamicChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
constructor(registry:PipeRegistry) {
|
||||
super();
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
|
||||
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
||||
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements faster change detection, by generating source code.
|
||||
*
|
||||
* This requires `eval()`. For change detection that does not require `eval()`, see {@link DynamicChangeDetection}.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
@Injectable()
|
||||
export class JitChangeDetection extends ChangeDetection {
|
||||
registry:PipeRegistry;
|
||||
|
||||
constructor(registry:PipeRegistry) {
|
||||
super();
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
|
||||
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
|
||||
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
export var defaultPipeRegistry:PipeRegistry = new PipeRegistry(defaultPipes);
|
@ -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 {DirectiveIndex, 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 IS_CHANGED_LOCAL = "isChanged";
|
||||
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,29 @@ 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>, detectorFieldNames:List<String>):string {
|
||||
var directiveInit = "";
|
||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
var detectorInit = "";
|
||||
for(var i = 0; i < detectorFieldNames.length; ++i) {
|
||||
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\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}
|
||||
${detectorInit}
|
||||
}
|
||||
${type}.prototype.dehydrate = function() {
|
||||
${pipeOnDestroy}
|
||||
${fieldsDefinitions}
|
||||
${fieldDefinitions}
|
||||
${LOCALS_ACCESSOR} = null;
|
||||
}
|
||||
${type}.prototype.hydrated = function() {
|
||||
@ -156,13 +116,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 ${IS_CHANGED_LOCAL} = false;
|
||||
var ${CURRENT_PROTO};
|
||||
var ${CHANGES_LOCAL} = null;
|
||||
|
||||
context = ${CONTEXT_ACCESSOR};
|
||||
@ -170,45 +143,42 @@ ${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 (${oldValue} !== ${newValue}) {
|
||||
${newValue} = ${UTIL}.unwrapValue(${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 +207,66 @@ 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};
|
||||
${IS_CHANGED_LOCAL} = true;
|
||||
`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function notifyOnPushDetectorsTemplate(detector:string):string{
|
||||
return `
|
||||
if(${IS_CHANGED_LOCAL}) {
|
||||
${detector}.markAsCheckOnce();
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function lastInDirectiveTemplate(notifyOnChanges:string, notifyOnPush:string):string{
|
||||
return `
|
||||
${notifyOnChanges}
|
||||
${notifyOnPush}
|
||||
${IS_CHANGED_LOCAL} = false;
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
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 +274,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 +283,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 +307,41 @@ 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(), this.getDetectorFieldNames());
|
||||
}
|
||||
|
||||
getDirectiveFieldNames():List<string> {
|
||||
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDetectorFieldNames():List<string> {
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDirective(d:DirectiveIndex) {
|
||||
return `this.directive_${d.name}`;
|
||||
}
|
||||
|
||||
getDetector(d:DirectiveIndex) {
|
||||
return `this.detector_${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());
|
||||
fields = fields.concat(this.getDetectorFieldNames());
|
||||
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 +349,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.directiveIndex.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 +382,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 +391,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 cdRef = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.ref" : "null";
|
||||
|
||||
return pipeCheckTemplate(context, pipe, r.name, newValue, change, addRecord, notify);
|
||||
var update = this.genUpdateDirectiveOrElement(r);
|
||||
var addToChanges = this.genAddToChanges(r);
|
||||
var lastInDirective = this.genLastInDirective(r);
|
||||
|
||||
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, 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.genLastInDirective(r);
|
||||
|
||||
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
|
||||
update, addToChanges, lastInDirective);
|
||||
if (r.isPureFunction()) {
|
||||
return this.ifChangedGuard(r, check);
|
||||
} else {
|
||||
@ -369,7 +426,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
genUpdateCurrentValue(r:ProtoRecord):string {
|
||||
var context = this.localNames[r.contextIndex];
|
||||
var context = this.getContext(r);
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var args = this.genArgs(r);
|
||||
|
||||
@ -407,6 +464,14 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
getContext(r:ProtoRecord):string {
|
||||
if (r.contextIndex == -1) {
|
||||
return this.getDirective(r.directiveIndex);
|
||||
} else {
|
||||
return this.localNames[r.contextIndex];
|
||||
}
|
||||
}
|
||||
|
||||
ifChangedGuard(r:ProtoRecord, body:string):string {
|
||||
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
||||
}
|
||||
@ -427,8 +492,49 @@ 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.directiveIndex)}.${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) : "";
|
||||
}
|
||||
|
||||
genLastInDirective(r:ProtoRecord):string{
|
||||
var onChanges = this.genNotifyOnChanges(r);
|
||||
var onPush = this.genNotifyOnPushDetectors(r);
|
||||
return lastInDirectiveTemplate(onChanges, onPush);
|
||||
}
|
||||
|
||||
genNotifyOnChanges(r:ProtoRecord):string{
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.callOnChange()) {
|
||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
genNotifyOnPushDetectors(r:ProtoRecord):string{
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
genArgs(r:ProtoRecord):string {
|
||||
|
@ -2,8 +2,8 @@ import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang'
|
||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
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 {WrappedValue} from './pipes/pipe';
|
||||
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;
|
||||
@ -143,37 +109,31 @@ export class ChangeDetectionUtil {
|
||||
return obj[args[0]];
|
||||
}
|
||||
|
||||
static noChangeMarker(value):boolean {
|
||||
return value === NO_CHANGE;
|
||||
static unwrapValue(value:any):any {
|
||||
if (value instanceof WrappedValue) {
|
||||
return value.wrapped;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static throwOnChange(proto:ProtoRecord, change) {
|
||||
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;
|
||||
}
|
||||
}
|
45
modules/angular2/src/change_detection/change_detector_ref.js
vendored
Normal file
45
modules/angular2/src/change_detection/change_detector_ref.js
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
import {ChangeDetector} from './interfaces';
|
||||
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
|
||||
|
||||
/**
|
||||
* Controls change detection.
|
||||
*
|
||||
* {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It also allows detaching and
|
||||
* attaching change detector subtrees.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class ChangeDetectorRef {
|
||||
_cd:ChangeDetector;
|
||||
|
||||
constructor(cd:ChangeDetector) {
|
||||
this._cd = cd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to check all ON_PUSH ancestors.
|
||||
*/
|
||||
requestCheck() {
|
||||
this._cd.markPathToRootAsCheckOnce();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the change detector from the change detector tree.
|
||||
*
|
||||
* The detached change detector will not be checked until it is reattached.
|
||||
*/
|
||||
detach() {
|
||||
this._cd.mode = DETACHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reattach the change detector to the change detector tree.
|
||||
*
|
||||
* This also requests a check of this change detector. This reattached change detector will be checked during the
|
||||
* next change detection run.
|
||||
*/
|
||||
reattach() {
|
||||
this._cd.mode = CHECK_ALWAYS;
|
||||
this.requestCheck();
|
||||
}
|
||||
}
|
@ -45,9 +45,9 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
|
||||
[],
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
selfIndex,
|
||||
r.bindingMemento,
|
||||
r.directiveMemento,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
r.lastInBinding,
|
||||
r.lastInDirective
|
||||
@ -73,9 +73,9 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
|
||||
args,
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
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";
|
34
modules/angular2/src/change_detection/directive_record.js
vendored
Normal file
34
modules/angular2/src/change_detection/directive_record.js
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
import {ON_PUSH} from './constants';
|
||||
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
export class DirectiveIndex {
|
||||
elementIndex:number;
|
||||
directiveIndex:number;
|
||||
|
||||
constructor(elementIndex:number, directiveIndex:number) {
|
||||
this.elementIndex = elementIndex;
|
||||
this.directiveIndex = directiveIndex;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return `${this.elementIndex}_${this.directiveIndex}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveRecord {
|
||||
directiveIndex:DirectiveIndex;
|
||||
callOnAllChangesDone:boolean;
|
||||
callOnChange:boolean;
|
||||
changeDetection:string;
|
||||
|
||||
constructor(directiveIndex:DirectiveIndex, callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callOnAllChangesDone = callOnAllChangesDone;
|
||||
this.callOnChange = callOnChange;
|
||||
this.changeDetection = changeDetection;
|
||||
}
|
||||
|
||||
isOnPushChangeDetection():boolean {
|
||||
return StringWrapper.equals(this.changeDetection, ON_PUSH);
|
||||
}
|
||||
}
|
@ -2,8 +2,9 @@ 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 {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
||||
|
||||
|
||||
import {
|
||||
@ -17,6 +18,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 +35,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 +55,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 +93,74 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
detectChangesInRecords(throwOnChange:boolean) {
|
||||
var protos:List<ProtoRecord> = this.protos;
|
||||
|
||||
var updatedRecords = null;
|
||||
var changes = null;
|
||||
var isChanged = false;
|
||||
for (var i = 0; i < protos.length; ++i) {
|
||||
var proto:ProtoRecord = protos[i];
|
||||
var change = this._check(proto);
|
||||
var bindingRecord = proto.bindingRecord;
|
||||
var directiveRecord = bindingRecord.directiveRecord;
|
||||
|
||||
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, bindingRecord);
|
||||
isChanged = true;
|
||||
changes = this._addChange(bindingRecord, change, changes);
|
||||
}
|
||||
|
||||
if (proto.lastInDirective && isPresent(updatedRecords)) {
|
||||
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, updatedRecords[0]);
|
||||
if (proto.lastInDirective) {
|
||||
if (isPresent(changes)) {
|
||||
this._getDirectiveFor(directiveRecord.directiveIndex).onChange(changes);
|
||||
changes = null;
|
||||
}
|
||||
|
||||
this.dispatcher.onRecordChange(proto.directiveMemento, updatedRecords);
|
||||
updatedRecords = null;
|
||||
if (isChanged && bindingRecord.isOnPushChangeDetection()) {
|
||||
this._getDetectorFor(directiveRecord.directiveIndex).markAsCheckOnce();
|
||||
}
|
||||
|
||||
isChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callOnAllChangesDone() {
|
||||
var dirs = this.directiveRecords;
|
||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||
var dir = dirs[i];
|
||||
if (dir.callOnAllChangesDone) {
|
||||
this._getDirectiveFor(dir.directiveIndex).onAllChangesDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_updateDirectiveOrElement(change, bindingRecord) {
|
||||
if (isBlank(bindingRecord.directiveRecord)) {
|
||||
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
||||
} else {
|
||||
var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
|
||||
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
_addChange(bindingRecord:BindingRecord, change, changes) {
|
||||
if (bindingRecord.callOnChange()) {
|
||||
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
|
||||
} else {
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
_getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
|
||||
_getDetectorFor(directiveIndex) {
|
||||
return this.directives.getDetectorFor(directiveIndex);
|
||||
}
|
||||
|
||||
_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);
|
||||
@ -176,10 +233,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
_pipeCheck(proto:ProtoRecord) {
|
||||
var context = this._readContext(proto);
|
||||
var pipe = this._pipeFor(proto, context);
|
||||
var prevValue = this._readSelf(proto);
|
||||
|
||||
var newValue = pipe.transform(context);
|
||||
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
|
||||
var prevValue = this._readSelf(proto);
|
||||
|
||||
if (!isSame(prevValue, newValue)) {
|
||||
newValue = ChangeDetectionUtil.unwrapValue(newValue);
|
||||
|
||||
this._writeSelf(proto, newValue);
|
||||
this._setChanged(proto, true);
|
||||
|
||||
@ -202,12 +262,25 @@ 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 changeDetectorRef of the encompassing component.
|
||||
//
|
||||
// In the future, pipes declared in the bind configuration should
|
||||
// be able to access the changeDetectorRef of that component.
|
||||
var cdr = proto.mode === RECORD_TYPE_BINDING_PIPE ? this.ref : null;
|
||||
var pipe = this.pipeRegistry.get(proto.name, context, cdr);
|
||||
this._writePipe(proto, pipe);
|
||||
return pipe;
|
||||
}
|
||||
|
||||
_readContext(proto:ProtoRecord) {
|
||||
if (proto.contextIndex == -1) {
|
||||
return this._getDirectiveFor(proto.directiveIndex);
|
||||
} else {
|
||||
return this.values[proto.contextIndex];
|
||||
}
|
||||
|
||||
return this.values[proto.contextIndex];
|
||||
}
|
||||
|
||||
@ -255,11 +328,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,6 +1,7 @@
|
||||
import {ProtoRecord} from './proto_record';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
|
||||
export class ExpressionChangedAfterItHasBeenChecked extends Error {
|
||||
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
|
||||
message:string;
|
||||
|
||||
constructor(proto:ProtoRecord, change:any) {
|
||||
@ -14,7 +15,7 @@ export class ExpressionChangedAfterItHasBeenChecked extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeDetectionError extends Error {
|
||||
export class ChangeDetectionError extends BaseException {
|
||||
message:string;
|
||||
originalException:any;
|
||||
location:string;
|
||||
|
@ -1,52 +1,46 @@
|
||||
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):ChangeDetector{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
|
||||
* will become CHECKED.
|
||||
* Interface used by Angular to control the change detection strategy for an application.
|
||||
*
|
||||
* Angular implements the following change detection strategies by default:
|
||||
*
|
||||
* - {@link DynamicChangeDetection}: slower, but does not require `eval()`.
|
||||
* - {@link JitChangeDetection}: faster, but requires `eval()`.
|
||||
*
|
||||
* In JavaScript, you should always use `JitChangeDetection`, unless you are in an environment that has
|
||||
* [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP), such as a Chrome Extension.
|
||||
*
|
||||
* In Dart, use `DynamicChangeDetection` during development. The Angular transformer generates an analog to the
|
||||
* `JitChangeDetection` strategy at compile time.
|
||||
*
|
||||
*
|
||||
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection}
|
||||
*
|
||||
* # Example
|
||||
* ```javascript
|
||||
* bootstrap(MyApp, [bind(ChangeDetection).toClass(DynamicChangeDetection)]);
|
||||
* ```
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
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, bindingRecords:List, variableBindings:List, directiveRecords:List,
|
||||
changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeDispatcher {
|
||||
onRecordChange(directiveMemento, records:List<ChangeRecord>) {}
|
||||
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
|
||||
}
|
||||
|
||||
export class ChangeDetector {
|
||||
@ -54,9 +48,11 @@ export class ChangeDetector {
|
||||
mode:string;
|
||||
|
||||
addChild(cd:ChangeDetector) {}
|
||||
addShadowDomChild(cd:ChangeDetector) {}
|
||||
removeChild(cd:ChangeDetector) {}
|
||||
removeShadowDomChild(cd:ChangeDetector) {}
|
||||
remove() {}
|
||||
hydrate(context:any, locals:Locals) {}
|
||||
hydrate(context:any, locals:Locals, directives:any) {}
|
||||
dehydrate() {}
|
||||
markPathToRootAsCheckOnce() {}
|
||||
|
||||
|
@ -6,7 +6,7 @@ export class AST {
|
||||
throw new BaseException("Not supported");
|
||||
}
|
||||
|
||||
get isAssignable() {
|
||||
get isAssignable():boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ export class AccessMember extends AST {
|
||||
}
|
||||
}
|
||||
|
||||
get isAssignable() {
|
||||
get isAssignable():boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ export class KeyedAccess extends AST {
|
||||
return obj[key];
|
||||
}
|
||||
|
||||
get isAssignable() {
|
||||
get isAssignable():boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
@ -279,6 +281,8 @@ export class Binary extends AST {
|
||||
case '%' : return left % right;
|
||||
case '==' : return left == right;
|
||||
case '!=' : return left != right;
|
||||
case '===' : return left === right;
|
||||
case '!==' : return left !== right;
|
||||
case '<' : return left < right;
|
||||
case '>' : return left > right;
|
||||
case '<=' : return left <= right;
|
||||
@ -395,7 +399,7 @@ export class ASTWithSource extends AST {
|
||||
return this.ast.eval(context, locals);
|
||||
}
|
||||
|
||||
get isAssignable() {
|
||||
get isAssignable():boolean {
|
||||
return this.ast.isAssignable;
|
||||
}
|
||||
|
||||
@ -418,7 +422,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 +448,72 @@ export class AstVisitor {
|
||||
visitPrefixNot(ast:PrefixNot) {}
|
||||
}
|
||||
|
||||
export class AstTransformer {
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||
return ast;
|
||||
}
|
||||
|
||||
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]];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
|
||||
import {int, NumberWrapper, StringJoiner, StringWrapper} from "angular2/src/facade/lang";
|
||||
import {int, NumberWrapper, StringJoiner, StringWrapper, BaseException} from "angular2/src/facade/lang";
|
||||
|
||||
export const TOKEN_TYPE_CHARACTER = 1;
|
||||
export const TOKEN_TYPE_IDENTIFIER = 2;
|
||||
@ -160,10 +160,7 @@ export const $QUESTION = 63;
|
||||
const $0 = 48;
|
||||
const $9 = 57;
|
||||
|
||||
const $A = 65, $B = 66, $C = 67, $D = 68, $E = 69, $F = 70, $G = 71, $H = 72,
|
||||
$I = 73, $J = 74, $K = 75, $L = 76, $M = 77, $N = 78, $O = 79, $P = 80,
|
||||
$Q = 81, $R = 82, $S = 83, $T = 84, $U = 85, $V = 86, $W = 87, $X = 88,
|
||||
$Y = 89, $Z = 90;
|
||||
const $A = 65, $E = 69, $Z = 90;
|
||||
|
||||
export const $LBRACKET = 91;
|
||||
export const $BACKSLASH = 92;
|
||||
@ -171,19 +168,15 @@ export const $RBRACKET = 93;
|
||||
const $CARET = 94;
|
||||
const $_ = 95;
|
||||
|
||||
const $a = 97, $b = 98, $c = 99, $d = 100, $e = 101, $f = 102, $g = 103,
|
||||
$h = 104, $i = 105, $j = 106, $k = 107, $l = 108, $m = 109, $n = 110,
|
||||
$o = 111, $p = 112, $q = 113, $r = 114, $s = 115, $t = 116, $u = 117,
|
||||
$v = 118, $w = 119, $x = 120, $y = 121, $z = 122;
|
||||
const $a = 97, $e = 101, $f = 102, $n = 110, $r = 114, $t = 116, $u = 117, $v = 118, $z = 122;
|
||||
|
||||
export const $LBRACE = 123;
|
||||
export const $BAR = 124;
|
||||
export const $RBRACE = 125;
|
||||
const $TILDE = 126;
|
||||
const $NBSP = 160;
|
||||
|
||||
|
||||
export class ScannerError extends Error {
|
||||
export class ScannerError extends BaseException {
|
||||
message:string;
|
||||
constructor(message) {
|
||||
super();
|
||||
@ -275,8 +268,6 @@ class _Scanner {
|
||||
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
|
||||
case $BAR:
|
||||
return this.scanComplexOperator(start, $BAR, '|', '|');
|
||||
case $TILDE:
|
||||
return this.scanComplexOperator(start, $SLASH, '~', '/');
|
||||
case $NBSP:
|
||||
while (isWhitespace(this.peek)) this.advance();
|
||||
return this.scanToken();
|
||||
@ -304,7 +295,7 @@ class _Scanner {
|
||||
assert(this.peek == StringWrapper.charCodeAt(one, 0));
|
||||
this.advance();
|
||||
var str:string = one;
|
||||
if (this.peek == code) {
|
||||
while (this.peek == code) {
|
||||
this.advance();
|
||||
str += two;
|
||||
}
|
||||
@ -455,12 +446,13 @@ var OPERATORS = SetWrapper.createFromList([
|
||||
'-',
|
||||
'*',
|
||||
'/',
|
||||
'~/',
|
||||
'%',
|
||||
'^',
|
||||
'=',
|
||||
'==',
|
||||
'!=',
|
||||
'===',
|
||||
'!==',
|
||||
'<',
|
||||
'>',
|
||||
'<=',
|
||||
@ -480,5 +472,5 @@ var KEYWORDS = SetWrapper.createFromList([
|
||||
'null',
|
||||
'undefined',
|
||||
'true',
|
||||
'false',
|
||||
'false'
|
||||
]);
|
||||
|
@ -34,7 +34,7 @@ export class Locals {
|
||||
throw new BaseException(`Cannot find '${name}'`);
|
||||
}
|
||||
|
||||
set(name:string, value) {
|
||||
set(name:string, value):void {
|
||||
// TODO(rado): consider removing this check if we can guarantee this is not
|
||||
// exposed to the public API.
|
||||
// TODO: vsavkin maybe it should check only the local map
|
||||
@ -45,7 +45,7 @@ export class Locals {
|
||||
}
|
||||
}
|
||||
|
||||
clearValues() {
|
||||
clearValues():void {
|
||||
MapWrapper.clearValues(this.current);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
|
||||
@ -31,7 +31,6 @@ import {
|
||||
var _implicitReceiver = new ImplicitReceiver();
|
||||
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
||||
var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
||||
var QUOTE_REGEXP = RegExpWrapper.create("'");
|
||||
|
||||
@Injectable()
|
||||
export class Parser {
|
||||
@ -58,7 +57,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);
|
||||
}
|
||||
@ -211,18 +210,11 @@ class _ParseAST {
|
||||
|
||||
parsePipe() {
|
||||
var result = this.parseExpression();
|
||||
while (this.optionalOperator("|")) {
|
||||
if (this.parseAction) {
|
||||
this.error("Cannot have a pipe in an action expression");
|
||||
}
|
||||
var name = this.expectIdentifierOrKeyword();
|
||||
var args = ListWrapper.create();
|
||||
while (this.optionalCharacter($COLON)) {
|
||||
ListWrapper.push(args, this.parseExpression());
|
||||
}
|
||||
result = new Pipe(result, name, args);
|
||||
if (this.optionalOperator("|")) {
|
||||
return this.parseInlinedPipe(result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseExpression() {
|
||||
@ -284,13 +276,17 @@ class _ParseAST {
|
||||
}
|
||||
|
||||
parseEquality() {
|
||||
// '==','!='
|
||||
// '==','!=','===','!=='
|
||||
var result = this.parseRelational();
|
||||
while (true) {
|
||||
if (this.optionalOperator('==')) {
|
||||
result = new Binary('==', result, this.parseRelational());
|
||||
} else if (this.optionalOperator('===')) {
|
||||
result = new Binary('===', result, this.parseRelational());
|
||||
} else if (this.optionalOperator('!=')) {
|
||||
result = new Binary('!=', result, this.parseRelational());
|
||||
} else if (this.optionalOperator('!==')) {
|
||||
result = new Binary('!==', result, this.parseRelational());
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
@ -464,16 +460,38 @@ class _ParseAST {
|
||||
} else {
|
||||
var getter = this.reflector.getter(id);
|
||||
var setter = this.reflector.setter(id);
|
||||
return new AccessMember(receiver, id, getter, setter);
|
||||
var am = new AccessMember(receiver, id, getter, setter);
|
||||
|
||||
if (this.optionalOperator("|")) {
|
||||
return this.parseInlinedPipe(am);
|
||||
} else {
|
||||
return am;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseInlinedPipe(result) {
|
||||
do {
|
||||
if (this.parseAction) {
|
||||
this.error("Cannot have a pipe in an action expression");
|
||||
}
|
||||
var name = this.expectIdentifierOrKeyword();
|
||||
var args = ListWrapper.create();
|
||||
while (this.optionalCharacter($COLON)) {
|
||||
ListWrapper.push(args, this.parseExpression());
|
||||
}
|
||||
result = new Pipe(result, name, args, true);
|
||||
} while(this.optionalOperator("|"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
parseCallArguments() {
|
||||
if (this.next.isCharacter($RPAREN)) return [];
|
||||
var positionals = [];
|
||||
do {
|
||||
ListWrapper.push(positionals, this.parseExpression());
|
||||
} while (this.optionalCharacter($COMMA))
|
||||
} while (this.optionalCharacter($COMMA));
|
||||
return positionals;
|
||||
}
|
||||
|
||||
@ -519,7 +537,7 @@ class _ParseAST {
|
||||
ListWrapper.push(bindings, new TemplateBinding(key, keyIsVar, name, expression));
|
||||
if (!this.optionalCharacter($SEMICOLON)) {
|
||||
this.optionalCharacter($COMMA);
|
||||
};
|
||||
}
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
117
modules/angular2/src/change_detection/pipes/async_pipe.js
vendored
Normal file
117
modules/angular2/src/change_detection/pipes/async_pipe.js
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
import {Observable, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
|
||||
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
/**
|
||||
* Implements async bindings to Observable.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example we bind the description observable to the DOM. The async pipe will convert an observable to the
|
||||
* latest value it emitted. It will also request a change detection check when a new value is emitted.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: "task-cmp",
|
||||
* changeDetection: ON_PUSH
|
||||
* })
|
||||
* @View({
|
||||
* inline: "Task Description {{description|async}}"
|
||||
* })
|
||||
* class Task {
|
||||
* description:Observable<string>;
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class AsyncPipe extends Pipe {
|
||||
_ref:ChangeDetectorRef;
|
||||
|
||||
_latestValue:Object;
|
||||
_latestReturnedValue:Object;
|
||||
|
||||
_subscription:Object;
|
||||
_observable:Observable;
|
||||
|
||||
constructor(ref:ChangeDetectorRef) {
|
||||
super();
|
||||
this._ref = ref;
|
||||
this._latestValue = null;
|
||||
this._latestReturnedValue = null;
|
||||
this._subscription = null;
|
||||
this._observable = null;
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return ObservableWrapper.isObservable(obs);
|
||||
}
|
||||
|
||||
onDestroy():void {
|
||||
if (isPresent(this._subscription)) {
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
transform(obs:Observable):any {
|
||||
if (isBlank(this._subscription)) {
|
||||
this._subscribe(obs);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (obs !== this._observable) {
|
||||
this._dispose();
|
||||
return this.transform(obs);
|
||||
}
|
||||
|
||||
if (this._latestValue === this._latestReturnedValue) {
|
||||
return this._latestReturnedValue;
|
||||
} else {
|
||||
this._latestReturnedValue = this._latestValue;
|
||||
return WrappedValue.wrap(this._latestValue);
|
||||
}
|
||||
}
|
||||
|
||||
_subscribe(obs:Observable):void {
|
||||
this._observable = obs;
|
||||
this._subscription = ObservableWrapper.subscribe(obs,
|
||||
value => this._updateLatestValue(value),
|
||||
e => {throw e;}
|
||||
);
|
||||
}
|
||||
|
||||
_dispose():void {
|
||||
ObservableWrapper.dispose(this._subscription);
|
||||
this._latestValue = null;
|
||||
this._latestReturnedValue = null;
|
||||
this._subscription = null;
|
||||
this._observable = null;
|
||||
}
|
||||
|
||||
_updateLatestValue(value:Object) {
|
||||
this._latestValue = value;
|
||||
this._ref.requestCheck();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a factory for [AsyncPipe].
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class AsyncPipeFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return ObservableWrapper.isObservable(obs);
|
||||
}
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return new AsyncPipe(cdRef);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
isListLikeIterable,
|
||||
iterateListLike,
|
||||
@ -14,19 +15,27 @@ import {
|
||||
looseIdentical,
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {NO_CHANGE, Pipe} from './pipe';
|
||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
||||
|
||||
export class ArrayChangesFactory {
|
||||
supports(obj):boolean {
|
||||
return ArrayChanges.supportsObj(obj);
|
||||
export class IterableChangesFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
return new ArrayChanges();
|
||||
supports(obj):boolean {
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return new IterableChanges();
|
||||
}
|
||||
}
|
||||
|
||||
export class ArrayChanges extends Pipe {
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class IterableChanges extends Pipe {
|
||||
_collection;
|
||||
_length:int;
|
||||
_linkedRecords:_DuplicateMap;
|
||||
@ -66,7 +75,7 @@ export class ArrayChanges extends Pipe {
|
||||
}
|
||||
|
||||
supports(obj):boolean {
|
||||
return ArrayChanges.supportsObj(obj);
|
||||
return IterableChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
get collection() {
|
||||
@ -114,9 +123,9 @@ export class ArrayChanges extends Pipe {
|
||||
|
||||
transform(collection){
|
||||
if (this.check(collection)) {
|
||||
return this;
|
||||
return WrappedValue.wrap(this);
|
||||
} else {
|
||||
return NO_CHANGE;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +135,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;
|
||||
@ -206,10 +216,10 @@ export class ArrayChanges extends Pipe {
|
||||
/**
|
||||
* This is the core function which handles differences between collections.
|
||||
*
|
||||
* - [record] is the record which we saw at this position last time. If null then it is a new
|
||||
* - `record` is the record which we saw at this position last time. If null then it is a new
|
||||
* item.
|
||||
* - [item] is the current item in the collection
|
||||
* - [index] is the position of the item in the collection
|
||||
* - `item` is the current item in the collection
|
||||
* - `index` is the position of the item in the collection
|
||||
*/
|
||||
_mismatch(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
|
||||
// The previous record after which we will append the current one.
|
||||
@ -280,9 +290,9 @@ export class ArrayChanges extends Pipe {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rid of any excess [CollectionChangeRecord]s from the previous collection
|
||||
* Get rid of any excess {@link CollectionChangeRecord}s from the previous collection
|
||||
*
|
||||
* - [record] The first excess [CollectionChangeRecord].
|
||||
* - `record` The first excess {@link CollectionChangeRecord}.
|
||||
*/
|
||||
_truncate(record:CollectionChangeRecord) {
|
||||
// Anything after that needs to be removed;
|
||||
@ -500,6 +510,9 @@ export class ArrayChanges extends Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class CollectionChangeRecord {
|
||||
currentIndex:int;
|
||||
previousIndex:int;
|
||||
@ -581,7 +594,7 @@ class _DuplicateItemRecordList {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one [CollectionChangeRecord] from the list of duplicates.
|
||||
* Remove one {@link CollectionChangeRecord} from the list of duplicates.
|
||||
*
|
||||
* Returns whether the list of duplicates is empty.
|
||||
*/
|
||||
@ -644,7 +657,7 @@ class _DuplicateMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an [CollectionChangeRecord] from the list of duplicates.
|
||||
* Removes a {@link CollectionChangeRecord} from the list of duplicates.
|
||||
*
|
||||
* The list of duplicates also is removed from the map if it gets empty.
|
||||
*/
|
@ -1,18 +1,29 @@
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {stringify, looseIdentical, isJsObject} from 'angular2/src/facade/lang';
|
||||
import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang';
|
||||
|
||||
import {NO_CHANGE, Pipe} from './pipe';
|
||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KeyValueChangesFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
export class KeyValueChangesFactory {
|
||||
supports(obj):boolean {
|
||||
return KeyValueChanges.supportsObj(obj);
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
create(cdRef):Pipe {
|
||||
return new KeyValueChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KeyValueChanges extends Pipe {
|
||||
_records:Map;
|
||||
|
||||
@ -48,9 +59,9 @@ export class KeyValueChanges extends Pipe {
|
||||
|
||||
transform(map){
|
||||
if (this.check(map)) {
|
||||
return this;
|
||||
return WrappedValue.wrap(this);
|
||||
} else {
|
||||
return NO_CHANGE;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,9 +118,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 +135,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 +169,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 +226,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 +360,13 @@ export class KeyValueChanges extends Pipe {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class KVChangeRecord {
|
||||
key;
|
||||
_previousValue;
|
||||
_currentValue;
|
||||
previousValue;
|
||||
currentValue;
|
||||
|
||||
_nextPrevious:KVChangeRecord;
|
||||
_next:KVChangeRecord;
|
||||
@ -363,8 +377,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 +389,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,27 @@
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Pipe, NO_CHANGE} from './pipe';
|
||||
import {isBlank, CONST} from 'angular2/src/facade/lang';
|
||||
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class NullPipeFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
export class NullPipeFactory {
|
||||
supports(obj):boolean {
|
||||
return NullPipe.supportsObj(obj);
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
create(cdRef):Pipe {
|
||||
return new NullPipe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class NullPipe extends Pipe {
|
||||
called:boolean;
|
||||
constructor() {
|
||||
@ -29,9 +40,9 @@ export class NullPipe extends Pipe {
|
||||
transform(value) {
|
||||
if (! this.called) {
|
||||
this.called = true;
|
||||
return null;
|
||||
return WrappedValue.wrap(null);
|
||||
} else {
|
||||
return NO_CHANGE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,78 @@
|
||||
export var NO_CHANGE = new Object();
|
||||
import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Indicates that the result of a {@link Pipe} transformation has changed even though the reference has not changed.
|
||||
*
|
||||
* The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored.
|
||||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class WrappedValue {
|
||||
wrapped:any;
|
||||
|
||||
constructor(wrapped:any) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
static wrap(value:any):WrappedValue {
|
||||
var w = _wrappedValues[_wrappedIndex++ % 5];
|
||||
w.wrapped = value;
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
var _wrappedValues = [
|
||||
new WrappedValue(null),
|
||||
new WrappedValue(null),
|
||||
new WrappedValue(null),
|
||||
new WrappedValue(null),
|
||||
new WrappedValue(null)
|
||||
];
|
||||
|
||||
var _wrappedIndex = 0;
|
||||
|
||||
/**
|
||||
* An interface for extending the list of pipes known to Angular.
|
||||
*
|
||||
* If you are writing a custom {@link 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;}
|
||||
}
|
||||
}
|
||||
|
||||
@ABSTRACT()
|
||||
export class PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return _abstract();
|
||||
}
|
||||
|
||||
create(cdRef):Pipe {
|
||||
return _abstract();
|
||||
}
|
||||
}
|
||||
|
||||
function _abstract() {
|
||||
return new BaseException('This method is abstract');
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||
import {Pipe} from './pipe';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
@Injectable()
|
||||
export class PipeRegistry {
|
||||
config;
|
||||
|
||||
@ -9,19 +12,19 @@ export class PipeRegistry {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
get(type:string, obj):Pipe {
|
||||
get(type:string, obj, cdRef:ChangeDetectorRef):Pipe {
|
||||
var listOfConfigs = this.config[type];
|
||||
if (isBlank(listOfConfigs)) {
|
||||
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||
}
|
||||
|
||||
var matchingConfig = ListWrapper.find(listOfConfigs,
|
||||
(pipeConfig) => pipeConfig.supports(obj));
|
||||
|
||||
if (isBlank(matchingConfig)) {
|
||||
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||
}
|
||||
|
||||
return matchingConfig.create();
|
||||
return matchingConfig.create(cdRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,13 @@ 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 {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
@ -41,47 +43,38 @@ 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>;
|
||||
_bindingRecords:List<BindingRecord>;
|
||||
_variableBindings:List<string>;
|
||||
_directiveRecords:List<DirectiveRecord>;
|
||||
_changeControlStrategy:string;
|
||||
|
||||
constructor(pipeRegistry:PipeRegistry) {
|
||||
constructor(pipeRegistry:PipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
|
||||
super();
|
||||
this._pipeRegistry = pipeRegistry;
|
||||
this._bindingRecords = bindingRecords;
|
||||
this._variableBindings = variableBindings;
|
||||
this._directiveRecords = directiveRecords;
|
||||
this._changeControlStrategy = changeControlStrategy;
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||
this._createRecordsIfNecessary(bindingRecords, variableBindings);
|
||||
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
||||
instantiate(dispatcher:any) {
|
||||
this._createRecordsIfNecessary();
|
||||
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
|
||||
this._pipeRegistry, this._records, this._directiveRecords);
|
||||
}
|
||||
|
||||
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||
_createRecordsIfNecessary() {
|
||||
if (isBlank(this._records)) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(bindingRecords, (r) => {
|
||||
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||
ListWrapper.forEach(this._bindingRecords, (b) => {
|
||||
recordBuilder.addAst(b, this._variableBindings);
|
||||
});
|
||||
this._records = coalesce(recordBuilder.records);
|
||||
}
|
||||
@ -92,28 +85,37 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
|
||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||
_factory:Function;
|
||||
_pipeRegistry;
|
||||
_bindingRecords:List<BindingRecord>;
|
||||
_variableBindings:List<string>;
|
||||
_directiveRecords:List<DirectiveRecord>;
|
||||
_changeControlStrategy:string;
|
||||
|
||||
constructor(pipeRegistry) {
|
||||
constructor(pipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
|
||||
super();
|
||||
this._pipeRegistry = pipeRegistry;
|
||||
this._factory = null;
|
||||
this._bindingRecords = bindingRecords;
|
||||
this._variableBindings = variableBindings;
|
||||
this._directiveRecords = directiveRecords;
|
||||
this._changeControlStrategy = changeControlStrategy;
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
||||
this._createFactoryIfNecessary(bindingRecords, variableBindings);
|
||||
instantiate(dispatcher:any) {
|
||||
this._createFactoryIfNecessary();
|
||||
return this._factory(dispatcher, this._pipeRegistry);
|
||||
}
|
||||
|
||||
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||
_createFactoryIfNecessary() {
|
||||
if (isBlank(this._factory)) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(bindingRecords, (r) => {
|
||||
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
|
||||
ListWrapper.forEach(this._bindingRecords, (b) => {
|
||||
recordBuilder.addAst(b, this._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,
|
||||
this._directiveRecords).generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,13 +127,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,29 +146,27 @@ 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;
|
||||
}
|
||||
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||
return 0;
|
||||
return this.bindingRecord.implicitReceiver;
|
||||
}
|
||||
|
||||
visitInterpolation(ast:Interpolation) {
|
||||
@ -181,7 +181,9 @@ class _ConvertAstIntoProtoRecords {
|
||||
|
||||
visitAccessMember(ast:AccessMember) {
|
||||
var receiver = ast.receiver.visit(this);
|
||||
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
|
||||
if (isPresent(this.variableBindings) &&
|
||||
ListWrapper.contains(this.variableBindings, ast.name) &&
|
||||
ast.receiver instanceof ImplicitReceiver) {
|
||||
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
|
||||
} else {
|
||||
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
||||
@ -239,7 +241,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) {
|
||||
@ -259,9 +262,15 @@ class _ConvertAstIntoProtoRecords {
|
||||
|
||||
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
||||
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));
|
||||
if (context instanceof DirectiveIndex) {
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
} else {
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
}
|
||||
return selfIndex;
|
||||
}
|
||||
}
|
||||
@ -356,4 +365,4 @@ function _interpolationFn(strings:List) {
|
||||
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8 + s(a9) + c9;
|
||||
default: throw new BaseException(`Does not support more than 9 expressions`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
|
||||
export const RECORD_TYPE_SELF = 0;
|
||||
export const RECORD_TYPE_CONST = 1;
|
||||
@ -9,7 +11,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;
|
||||
@ -17,10 +20,12 @@ export class ProtoRecord {
|
||||
funcOrValue:any;
|
||||
args:List;
|
||||
fixedArgs:List;
|
||||
|
||||
contextIndex:number;
|
||||
directiveIndex:DirectiveIndex;
|
||||
|
||||
selfIndex:number;
|
||||
bindingMemento:any;
|
||||
directiveMemento:any;
|
||||
bindingRecord:BindingRecord;
|
||||
lastInBinding:boolean;
|
||||
lastInDirective:boolean;
|
||||
expressionAsString:string;
|
||||
@ -31,9 +36,9 @@ export class ProtoRecord {
|
||||
args:List,
|
||||
fixedArgs:List,
|
||||
contextIndex:number,
|
||||
directiveIndex:DirectiveIndex,
|
||||
selfIndex:number,
|
||||
bindingMemento:any,
|
||||
directiveMemento:any,
|
||||
bindingRecord:BindingRecord,
|
||||
expressionAsString:string,
|
||||
lastInBinding:boolean,
|
||||
lastInDirective:boolean) {
|
||||
@ -43,10 +48,12 @@ export class ProtoRecord {
|
||||
this.funcOrValue = funcOrValue;
|
||||
this.args = args;
|
||||
this.fixedArgs = fixedArgs;
|
||||
|
||||
this.contextIndex = contextIndex;
|
||||
this.directiveIndex = directiveIndex;
|
||||
|
||||
this.selfIndex = selfIndex;
|
||||
this.bindingMemento = bindingMemento;
|
||||
this.directiveMemento = directiveMemento;
|
||||
this.bindingRecord = bindingRecord;
|
||||
this.lastInBinding = lastInBinding;
|
||||
this.lastInDirective = lastInDirective;
|
||||
this.expressionAsString = expressionAsString;
|
||||
|
8
modules/angular2/src/core/annotations/annotations.dart
Normal file
8
modules/angular2/src/core/annotations/annotations.dart
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* This indirection is needed for TS compilation path.
|
||||
* See comment in annotations.es6.
|
||||
*/
|
||||
|
||||
library angular2.core.annotations.annotations;
|
||||
|
||||
export "../annotations_impl/annotations.dart";
|
10
modules/angular2/src/core/annotations/annotations.es6
Normal file
10
modules/angular2/src/core/annotations/annotations.es6
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This indirection is needed to free up Component, etc symbols in the public API
|
||||
* to be used by the decorator versions of these annotations.
|
||||
*/
|
||||
|
||||
export {
|
||||
Component as ComponentAnnotation,
|
||||
Directive as DirectiveAnnotation,
|
||||
onDestroy, onChange, onAllChangesDone
|
||||
} from '../annotations_impl/annotations';
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user