Compare commits

..

219 Commits

Author SHA1 Message Date
7d29636087 chore(packaging): bump version to 2.0.0-alpha.19 2015-04-13 16:15:12 -07:00
70433e6b73 chore(build): use a Filter plugin to write the dest folder.
This lets broccoli keep the dest folder up-to-date in 'watch' mode,
so we should be able to use that for Karma.
2015-04-13 15:51:33 -07:00
3667854a8f refactor(di): move all binding resolution logic into injector.js 2015-04-13 15:06:44 -07:00
c5c1c9e38e feat(docs): more docs on binding resolution 2015-04-13 15:06:44 -07:00
308823b6ea perf(view): use pre-resolved bindings for child injector init
Creating a child injector from pre-resolved bindings (if any) is an
order of magnitude faster.
2015-04-13 15:06:44 -07:00
c05bad381c perf(benchmark): measure Injector init from resolved bindings 2015-04-13 15:06:44 -07:00
4a961f4ecb feat(di): provide two ways to create an injector, resolved and unresolved
Add two factory static functions to Injector: resolveAndCreate and
fromResolvedBindings.

We want to avoid resolution and flattening every time we create a new
injector. This commit allows the user to cache resolved bindings and
reuse them.
2015-04-13 15:06:43 -07:00
6c8398df9b fix(di): refactor bindings to support Dart annotations 2015-04-13 15:06:43 -07:00
ff6e7754ae chore(build): gulp test.unit.cjs broken the second run
Fixes #1311

Closes #1333
2015-04-13 21:44:54 +00:00
28ba179e31 feat: intiial commit for angular 2 dart analysis
Initial commit for analysis support to Angular 2 Dart
2015-04-13 13:50:29 -07:00
b96e560c8d feat(events): add support for global events
Fixes #1098
Closes #1255
2015-04-13 22:35:36 +02:00
7c95cea3a8 chore(bundle): avoid imports in "global" format
If an "empty" file (like angular2/template.js) is imported
it is auto-detected as the one using "global" format by the
system builder. This is incorrect as the entire angular2 build
output is in the ES6 format.

Removing empty import till it has some content.

Closes #1329
2015-04-13 21:24:56 +02:00
34501aaae6 chore(build): move more broccoli support inside the TypeScript boundary 2015-04-13 12:24:28 -07:00
dbfc4c1c16 chore(build): inline Traceur options just like we do everywhere 2015-04-13 11:37:18 -07:00
301863b105 chore(build): transpile only e2e test code into cjs 2015-04-13 11:31:14 -07:00
ef8dc40492 chore(build): correct comment 2015-04-13 11:31:14 -07:00
6dbd4d969b chore(build): create es5build.js only for files that will become es6 npm packages 2015-04-13 11:31:14 -07:00
3dd0ac1f0a chore(build): move dart broccoli tree to make-broccoli-tree 2015-04-13 11:20:59 -07:00
5b42272365 docs(DEVELOPER.md): add bower install info
Closes #1310
2015-04-13 20:15:29 +02:00
1f6c6dbf2f chore(build): refactor broccoli trees to generate them in order to reduce duplication 2015-04-13 10:07:31 -07:00
0012caa4d5 perf(benchmarks): measure cost of Injector init with a variety of bindings 2015-04-13 10:02:13 -07:00
8499cf84c3 fix(shadow_dom): redistribute light dom when a dynamic component is attached.
Fixes #1077
Closes #1315
2015-04-13 09:23:52 -07:00
daf0f472b3 feat(build): enforce formatting of some files.
Our style guide includes formatting conventions. Instead of wasting time in reviewing PRs discussing things like indenting, and to avoid later deltas to fix bad formatting in earlier commits, we want to enforce these in the build.
The intent in this change is to fail the build as quickly as possible in travis, so those sending a PR immediately know they should run clang-format and update their commit. When running locally, we want users to know about formatting, but they may not want to act on it immediately, until they are done working. For this reason, it is only a warning outside of the continuous build.
This is done by having a check-format task which should run on most local builds, and an enforce-format task only run by travis.
2015-04-11 18:39:28 -07:00
a3decad4c2 feat(build): Use broccoli for ts2dart transpilation. 2015-04-11 16:26:44 -07:00
7b790a3369 chore(build): fix check-format 2015-04-11 12:40:05 -07:00
e18920884e chore(DEVELOPER.md): Update Dart SDK Version
Package angular requires SDK version >=1.9.0 <2.0.0

Closes #1318
2015-04-11 12:06:50 +02:00
6f8fef4f13 feat(bootstrap): changed bootstrap to return ComponentRef 2015-04-10 18:14:59 -07:00
e295940833 cleanup(docs): Edited API docs 2015-04-10 18:02:10 -07:00
2ed7622239 chore(build): compile the .ts broccoli tools.
This avoids having to check in the compiled .js files.
2015-04-10 17:29:32 -07:00
6ce085a21a feat(benchmark): make view cache a parameter to the tree benchmark 2015-04-10 16:57:46 -07:00
e34146fc14 fix(view_factory): fix caching of views
Previous implementation had bugs, and did not cache per ProtoView.
2015-04-10 16:57:45 -07:00
4e2316c742 feat(build): Add rudimentary TS typings for broccoli. 2015-04-10 16:37:24 -07:00
785900f722 DEVELOPER.md copyedit 2015-04-10 23:34:06 +00:00
5ce5a87abe style: add more type info to Dart BrowserAdapter 2015-04-10 23:34:06 +00:00
afe5465862 add return types for indexOf and lastIndexOf
closes #1277
2015-04-10 23:34:05 +00:00
678d541da7 refactor(compiler): add control.ignoreCurrentElement() to skip the current element
relates to #808
2015-04-10 23:34:05 +00:00
f0477e164a chore(build): add typescript to the cjs build.
Refactor the file extension logic in traceur plugin to simplify
2015-04-10 15:22:03 -07:00
b5002fb46b docs(test_lib/test_injector): fix invalid jsdoc type
chore(doc-gen): capture docs for modules from comments

Closes #1258

docs(*): add module description jsdoc tags
docs(*): add @public tag to public modules
chore(doc-gen): fix overview-dump template
The template was referencing an invalid property
chore(doc-gen): use `@exportedAs` and `@public` rather than `@publicModule`

This commit refactors how we describe components that are re-exported in another
module. For example the "public" modules like `angular/angular` and `angular/annotations`
are public but they only re-export components from "private" modules.

Previously, you must apply the `@publicModule` tag to a component that was to be
re-exported. Applying this tag caused the destination module to become public.

Now, you specify that a module is public by applying the `@public` tag and then
you can "re-export" components to other modules by applying the `@exportedAs`
giving the name of the module from which the component will be re-exported.
tag. This tag can be used multiple times on a single component, allowing the
component to be exported on multiple modules.

docs(*): rename `@publicModule` to `@exportedAs`

The `@publicModule` dgeni tag has been replaced by the `@exportedAs`
dgeni tag on components that are to be re-exported on another module.

Closes #1290
2015-04-10 22:00:41 +00:00
82127571b5 feat(dart/transform): Use the Dart transformer for benchmarks
Remove explicit generation of reflection information in benchmark code
and generate it with the transformer.
2015-04-10 14:01:55 -07:00
f6e9d1f857 feat(dart/transform): Fix handling of Dart keywords
Use `package:analyzer`'s list of Dart keywords to ensure we are properly
reporting usages of Dart keywords as runtime errors.
2015-04-10 13:43:11 -07:00
2cab7c79c3 feat(dart/transform): Allow multiple transformer entry points
- Allow the user to specify multiple entry points to an app.
- Allow the Angular 2 transformer to run without explicit entry points to
generate necessary setters & getters on built-in directives like `For`
and `If`.

Closes #1246
2015-04-10 13:41:26 -07:00
bba849909c fix(dart/transform): Gracefully handle log calls before init
- Lazily create and use a logger that prints instead of `throw`ing.
- Use this logger in unit tests.
2015-04-10 13:41:26 -07:00
cac74c73e1 feat(dart/transform): Add stub implementations to Html5LibAdapter
Stub out some methods used in the `CompilerPipeline`.
2015-04-10 13:41:26 -07:00
f375dbd013 feat(dart/transform): Mark Compiler as Injectable
Necessary to allow runtime access via the `Injector`
2015-04-10 13:41:26 -07:00
ea58ef85fc chore(build): move the js.prod build over to broccoli 2015-04-10 13:11:58 -07:00
bf7933714a chore(rename): rename View and Template concepts for #1244 2015-04-10 12:00:37 -07:00
564477b8a0 chore(build): migrate build.js.cjs to broccoli.
This doesn't do the typescript part of the build yet. Also there is a bit
of hackiness left to resolve in a follow-up change.
2015-04-10 11:39:48 -07:00
7e2c04e805 feat: add class directive to a list of directives
Closes #1292
2015-04-10 18:33:51 +02:00
8fa1539bac feat(keyEvents): support for <div (keyup.enter)="callback()">
This commit adds a plugin for the event manager, to allow a key name to
be appended to the event name (for keyup and keydown events), so that
the callback is only called for that key.

Here are some examples:
 (keydown.shift.enter)
 (keyup.space)
 (keydown.control.shift.a)
 (keyup.f1)

Key names mostly follow the DOM Level 3 event key values:
http://www.w3.org/TR/DOM-Level-3-Events-key/#key-value-tables

There are some limitations to be worked on (cf details
in https://github.com/angular/angular/pull/1136) but for now, this
implementation is reliable for the following keys (by "reliable" I mean
compatible with Chrome and Firefox and not depending on the keyboard
layout):
- alt, control, shift, meta (those keys can be combined with other keys)
- tab, enter, backspace, pause, scrolllock, capslock, numlock
- insert, delete, home, end, pageup, pagedown
- arrowup, arrowdown, arrowleft, arrowright
- latin letters (a-z), function keys (f1-f12)
- numbers on the numeric keypad (but those keys are not correctly simulated
by Chromedriver)

There is a sample to play with in examples/src/key_events/.

close #523
close #1136
2015-04-10 13:29:27 +02:00
f45281a10a feat(view): generalized loading of dynamic components 2015-04-09 22:15:42 -07:00
e9f70293ac feat(query): adds initial implementation of the query api.
Queries allow a directive to inject a live list of directives of a given
type from its LightDom. The injected list is Iterable (in JS and Dart).
It will be Observable when Observables are support in JS, for now it
maintains a simple list of onChange callbacks API.

To support queries, element injectors now maintain a list of
child injectors in the correct DOM order (dynamically updated by
viewports).

For performance reasons we allow only 3 active queries in an injector
subtree. The feature adds no overhead to the application when not
used. Queries walk the injector tree only during dynamic view
addition/removal as triggered by viewport directives.

Syncs changes between viewContainer on the render and logic sides.

Closes #792
2015-04-09 19:07:19 -07:00
61cb99ea42 refactor(change_detection): removed directive and binding mementos 2015-04-09 18:56:19 -07:00
5408a9a72d cleanup(change_detection): removed dead code 2015-04-09 18:56:19 -07:00
22c1a0d030 fix(benchmarks): Stop working around a Traceur bug. 2015-04-09 18:03:27 -07:00
8c3007e4b5 fix(build): Remove unused done function arguments.
gulp only requires receiving a done argument if a task is not returning
a stream. Doing both is unnecessary and confusing.
2015-04-09 18:03:27 -07:00
838aa2aaa9 fix(ts2dart): Adjust to new ts2dart API. 2015-04-09 18:03:27 -07:00
3285ffba16 fix(traceur): Fix a couple of unsupported or incorrect tests. 2015-04-09 18:03:27 -07:00
17e8857efc feat(dart): Use ts2dart for transpilation in Karma Dart. 2015-04-09 18:03:27 -07:00
226cbc7db3 feat(dart): Use ts2dart for transpilation.
This switches all transpilation over from using Traceur to using ts2dart, based
on the TypeScript tool chain. Transpilation is a bit slow due to issues with
the gulp integration, but that should be easily fixable once we move to
broccoli.
2015-04-09 18:03:27 -07:00
cc7c7b3321 fix(build): Don't include rtts in the dart build.
The module name actually does not include a trailing slash, so the
folder would show up and be included in the dart pubbuild.
2015-04-09 18:03:26 -07:00
70cea03b4b fix(build): Only return directories from subDirs() 2015-04-09 18:03:26 -07:00
a027912891 cleanup(change_detection): fixed ChangeDetector interface 2015-04-09 17:30:04 -07:00
3bdf669ddf cleanup(change_detection): removed dead code 2015-04-09 17:30:04 -07:00
b94b04c074 chore(build): Migrate remaining tasks under build.js.dev to broccoli. 2015-04-09 14:09:38 -07:00
cfc5dd830c perf(build): use patched broccoli-funnel version
see: https://github.com/broccolijs/broccoli-funnel/pull/22
2015-04-09 12:48:51 -07:00
a3097aaf05 chore(build): Migrate build.js.dev fully to broccoli.
The previous change did the ES6 transpile, now we add ES5.
The sourcemaps are broken, but were also broken previously. We'll address that separately.
2015-04-09 11:00:47 -07:00
69c3bff086 feat(change_detection): updated change detection to update directive directly, without the dispatcher 2015-04-08 22:14:50 -07:00
50098767fc refactor(render): use render layer fully
Introduces angular2/src/core/compiler/ViewFactory which
extracts ProtoView.instantiate and replaces ViewPool.

Note: This is a work in progress commit to unblock other commits.
There will be follow ups to add unit tests, remove TODOs, …
2015-04-08 20:51:31 -07:00
de581ea8b3 chore(build): Move broccoli support to own module.
Add support for multiple pipelines in different Brocfile's.
2015-04-08 10:41:42 -07:00
9f8a9c6fc7 chore(doc-gen): ensure all public exports are rendered in public_docs
Closes #1222
2015-04-08 18:58:44 +02:00
ad083ed28f fix(repo): .gitignore the broccoli tmp dir 2015-04-08 09:57:24 -07:00
105ba30ce9 chore(doc-gen): improve method signature formatting
Re-use the preformatting from the source code to layout method signatures
more cleanly.
2015-04-08 17:11:34 +02:00
ee8bf0b3c0 chore(doc-gen): HTML escape method signatures
Closes #1249
Closes #1257
2015-04-08 17:11:34 +02:00
41262f4265 feat(Ruler): introduce Ruler service
Closes #1089

Closes #1253
2015-04-08 11:04:42 +02:00
c349eb4fa4 fix(bundles): remove work-around rx.js module detection.
Updates rx to the newest version, because a fix is needed.

Closes #1245
2015-04-08 11:01:01 +02:00
ca958464c4 refactor(render): create and store render ProtoViewRef in every app ProtoView
Needed to change Renderer.mergeChildComponentProtoViews to not create
new ProtoViews to be able to deal with cyclic references.

This commit is part of using the new render layer in Angular.
2015-04-07 20:27:25 -07:00
d6003ee0ab chore(build): Add traceur transpiler for broccoli.
This exactly reproduces the output tree from one of the gulp tasks, which is now removed.
Next step is to migrate another sibling task to broccoli.
2015-04-07 16:38:02 -07:00
bc248e9a15 fix(build) use relative path in file property inside sourcemap 2015-04-07 12:53:06 -07:00
42c0171b40 chore(dart/transform): Create targets for serving transformed Dart code
- Allow pub (build|serve) to specify mode
- Update pubbuild.js & pubserve.js to allow the caller to provide a `mode` value.
- Update settings to allow the di benchmark to be transformed to run statically.
2015-04-07 10:57:01 -07:00
1a99090b45 chore(doc-gen): don't show Members heading if no members
Closes #1248
2015-04-07 09:23:19 +02:00
b7eea4f577 docs(directives): add # to for directive microsyntax example 2015-04-07 08:44:32 +02:00
9c62b5867e benchmark(change_detection): added a new set of benchmarks measuring updating properties 2015-04-06 18:13:20 -07:00
2560af731a refactor(dart): use Map instead of HashMap
Closes #1202
2015-04-06 17:16:54 +00:00
86211eb5f0 doc(directives): add inline documentation
Closes #1240
2015-04-06 17:11:23 +00:00
a3387b7f48 fix(di): allow injecting static attrs without type annotations
Closes #1226
2015-04-06 12:33:37 +02:00
94a48e8640 test(VmTurnZone): provide a stub VmTurnZone for CJS tests 2015-04-06 10:30:17 +02:00
d8aeb40b49 reafactor(XHR): rename XHRMock to MockXHR for consistency 2015-04-06 10:30:16 +02:00
52c55d0ee8 test: convert to using TestBed 2015-04-06 10:30:16 +02:00
438c2b31e4 test(TestBed): initial implementation 2015-04-06 10:30:16 +02:00
57e308dd46 test(MockTemplateResolver): allow directive overriding 2015-04-06 10:07:50 +02:00
c922b5a112 docs(annotations): fix some typos, align docs with code
Closes #1227
2015-04-04 12:23:01 +02:00
d552303cd5 docs(02_directives.md): foreach -> for
Closes #1235
2015-04-04 10:32:53 +02:00
1d4d18d9db refactor(render): user render compiler 2015-04-03 23:41:00 -07:00
069bbf3ed0 docs(02_directives.md): Fixes variable name for visibility in Components example 2015-04-03 14:18:15 -07:00
a4a2d4e56d chore: allow latest stack_track package 2015-04-03 14:18:14 -07:00
d77f409093 chore: analyzer fixes for Dart transformer 2015-04-03 14:18:14 -07:00
25c709c58e fix(angular2): export PrivateComponent{Loader,Location} in angular2/core 2015-04-03 14:18:14 -07:00
bc909d1d0f refactor(dart/transform): Minor renames
Rename `setupReflection` => `initReflector`
Rename `TemplateComplier` => `TemplateCompiler`

Closes #1180
2015-04-03 13:16:24 -07:00
a6736ff9f2 perf(change detection): Assign this.locals in change detector ctor
Set `this.locals = null;` in the ctor of generated change detector
classes to prevent the class "shape" from changing on `hydrate`.
2015-04-03 12:23:47 -07:00
894a0f0ee5 chore(ts): duplicate the .es6 files in the facade directory to TypeScript.
Adds a gulp task which builds the .ts files (in the cjs build only).
The new files have extension .ts since they are now valid typescript.
Unfortunately until Typescript can emit System.require, we have to keep the old .es6 version
so traceur works inside the Karma preprocessor. This should be fixed soon.
2015-04-03 09:35:06 -07:00
abea92af59 refactor(change_detection): call onChange from the change detector 2015-04-02 21:22:42 -07:00
bcbed2812d feat(bundle): work-around rx.all.js bundle issue.
Adds long-stack-trace-zone into the dev build. Turn off source maps
until proper concatination of them is added.
2015-04-02 19:54:07 -07:00
c0b04ca0bc feat(gulp): adds System.register bundle task. 2015-04-02 19:54:06 -07:00
86dc3e5b07 docs: create public API surface
Closes #1181
2015-04-02 23:23:39 +00:00
c1aa65239e refactor(render): move services to render folder
property_setter_factory
selector
style_inliner
style_url_resolver
shadow_css
2015-04-02 14:40:49 -07:00
be5ccf6957 refactor(render): delete copies files so we add them via moves 2015-04-02 14:24:55 -07:00
09067ebdc5 fix(build): Require gulp-ts2dart at least at 1.0.6.
This fixes the build by pulling in a later version that correctly ignores the .es6 files.
2015-04-02 13:13:47 -07:00
08697e71fa chore(package.json): update madge to v0.5.0
Closes #1211
2015-04-02 22:08:04 +02:00
90d9a1df3f fix(IE11): first fixes
Closes #1179
2015-04-02 22:06:21 +02:00
a96c149793 chore(gulp): Stop copying .cjs files to the dist folder
They're already transpiled by the build/transpile.js.cjs task
2015-04-02 20:51:16 +02:00
1037cef22e refactor(render): misc minor fixes 2015-04-02 20:50:05 +02:00
09948f4403 feat(dart/transform): Add a di transformer
Add a transformer for `di` which generates `.ng_deps.dart` files for all
`.dart` files it is run on. These `.ng_deps.dart` files register
metadata for any `@Injectable` classes.

Fix unit tests for changes introduced by the di transformer.

When using `pub (build|serve) --mode=ngstatic`, we will also generate
getters and setters, parse templates, and remove import of `dart:mirrors`
in the Angular transform. Because this is still relatively immature, we
use the mode to keep it opt-in for now.

Closes #700
2015-04-02 11:06:33 -07:00
788461b7e2 feat(di): Mark objects @Injectable
Allow `PrivateComponentLoader`, `Testability`, and `TestabilityRegistry` to be injected.
2015-04-02 11:02:26 -07:00
4f56628566 refactor(render): move services to right location
core/compiler/events -> render/dom/events
core/compiler/url_resolver -> services/url_resolver
core/compiler/xhr/* -> services/*
2015-04-02 10:35:27 -07:00
bcbf1ccc68 refactor(render): remove duplicate files to prepare for move
Remove first so Github shows the files as being moved instead of copied and deleted.
2015-04-02 10:35:27 -07:00
ae30d7ba40 fix(di): allow injecting event emitter fns without specifying type annotation
Fixes #965

Closes #1155
2015-04-02 19:07:49 +02:00
9adf41ca2d fix(build) Add a .tsdrc file for github rate limiting.
See https://github.com/DefinitelyTyped/tsd#tsdrc
2015-04-02 10:05:37 -07:00
1d79d534d9 test(selector): add tests with multiple attributes
Fixes #1025
Closes #1117
2015-04-02 18:06:44 +02:00
60e4197026 feat(tooling): Add a .clang-format for automated JavaScript formatting. 2015-04-02 08:44:34 -07:00
2fabca77b9 test(Travis): use test.dart for Dart tests 2015-04-02 13:12:59 +02:00
47542b0cb0 fix(build): don’t read out chrome perflogs during e2e tests
We do this as we are seeing flakes in Chrome with ECONNREFUSED.

Also reuses the same browser window.

Also reenables the infinite scroll benchmark

Closes #1137
2015-04-01 17:09:26 -07:00
6c60c3e547 feat(render): add initial implementation of render layer 2015-04-01 16:50:22 -07:00
814d389b6e chore(gulp): add typescript transpilation tasks 2015-04-01 15:01:27 -07:00
e81e5fb2b9 feat(testability): add an initial scaffold for the testability api
Make each application component register itself onto the testability
API and exports the API onto the window object.
2015-04-01 13:54:06 -07:00
f68cdf3878 chore(ts): introduce some TypeScript type declarations.
This uses tsd to fetch the typings from another git repo. I've forked the DefinitelyTyped repo because some typings we use are not available upstream.
We should probably fork it in the Angular org, so everyone on the team has commit access to our DefinitelyTyped fork.
2015-04-01 12:01:45 -07:00
91e0e9e1dd chore(ts): Patch traceur's type module only when targetting es6 output. 2015-04-01 10:39:06 -07:00
59c1299168 fix(tests): add missing ;s 2015-04-01 15:30:46 +02:00
27c6afbeb4 chore(doc-gen): add temporary dump of all API docs
Remove unwanted < character
2015-04-01 10:24:33 +01:00
514ba54282 feat(change_detection): added changeDetection to Component 2015-03-31 20:54:44 -07:00
a11f683e7b chore(ts): Don't mask the Regexp builtin.
Doing so makes it impossible to compile with TypeScript, since it conflicts with the shape of the Regexp global var defined in the standard lib.
2015-03-31 19:20:21 -07:00
b65b145122 refactor(view): refactored DirectiveMemento to expose properties in a consistent way 2015-03-31 18:26:58 -07:00
982bb8b01d fix(forms): fixed a directive selector 2015-03-31 17:45:38 -07:00
eb7b7581ca fix(build): Actually code in the subset of JS that Traceur-Dart supports. 2015-03-31 16:08:49 -07:00
adab6c0728 chore(doc-gen): add temporary dump of all API docs
Accessible at `angular/dist/public_docs/overview-dump.html`
2015-03-31 22:12:41 +01:00
609201e109 chore(doc-gen): add method signatures to members and functions
Closes https://github.com/angular/dgeni/issues/124
2015-03-31 22:12:41 +01:00
54a4e4a67c fix(dart): The Traceur dart transpiler doesn't support shorthand syntax. 2015-03-31 13:17:56 -07:00
aca4604879 feat(CSSClass): support binding to classList
Closes #876
2015-03-31 21:53:24 +02:00
48811cd805 doc(lifecycle): minor fixes 2015-03-31 21:12:37 +02:00
136f64f4ac fix(dart): don't instantiate abstract directive.
Directive is an abstract class, so it should not
be instantiated directly in tests.
2015-03-31 11:38:59 -07:00
123ee8e06f feat(dom): add replaceChild to DOM adapter 2015-03-31 09:54:41 -07:00
a55efbd8b8 feat(perf): add Angular2 implementation of largetable benchmark from AngularJS 1.x 2015-03-31 09:54:41 -07:00
7bf9525353 fix(benchmark_util): remove strict equality check from getStringParameter
Transpiled dart code was using identical() method to compare, which checks reference
equality, even for strings.
2015-03-31 09:54:41 -07:00
3915e1b242 docs(annotations): Added new text 2015-03-30 17:19:58 -07:00
ed5975d3e5 test(dart/transform): Add unit tests for url-linked templates
Test expression and method generation from url-linked templates.
2015-03-30 14:49:31 -07:00
1a788e6b0d feat(dart/transform): Parse url values in Templates
When a `Template` annotation declares a `url` value, parse it to
generate `getter`s, `setter`s, and `method`s which will it needs to
access reflectively.
2015-03-30 14:49:31 -07:00
d822793229 fix(test): add a test for @PropertySetter on a class with a dash
Closes #1113
Fixes #1099
2015-03-28 16:17:43 +01:00
b46d0bc48c docs(annotations): fix typo, align docs with code
Closes #1045
2015-03-28 15:39:50 +01:00
65320126c2 docs(directives): fix HTML in an example
Closes #1115
2015-03-28 15:37:11 +01:00
c63b3164bd fix(build): add package.json again to the copy files for js 2015-03-27 17:34:26 -07:00
dbffa88dc2 chore(release): bump version to 2.0.0-alpha.18
For docs
2015-03-27 17:16:22 -07:00
8c5d9d372f fix(build): publish docs as well and correct bench press docs 2015-03-27 16:47:52 -07:00
50f8892c6b chore(release): bump version
Somehow the version bump from alpha.16 was not submitted to master…
2015-03-27 16:21:41 -07:00
3bfbfa8ae0 chore(release): bump version 2015-03-27 16:18:36 -07:00
8598c87ef4 docs(bench press): add initial docs 2015-03-27 16:16:35 -07:00
33bfc4c24a feat(bench press): replace microIterations with microMetrics 2015-03-27 16:16:35 -07:00
3afb744e77 chore(ci): reorganize e2e/perf test running
Now, running protractor configs by default only runs e2e tests. If
the --benchmark flag is added, it runs only the perf tests, and always
restarts the browser in between tests. If the --dryrun test is added,
the perf tests are run only once.

This should make it easier to run perf tests versus example e2e tests,
and help stabilize the travis build because perf tests always
run with a clean browser.
2015-03-27 13:29:21 -07:00
e92918bbfe feat(change_detector): split light dom and shadow dom children 2015-03-27 13:26:37 -07:00
723e8fde93 feat(change_detection): added a directive lifecycle hook that is called after children are checked 2015-03-27 13:26:36 -07:00
507f7ea70a chore(package.json): upgrade zone.js to v0.4.2
Closes #1142
2015-03-27 16:24:07 -04:00
6b985d56a5 cleanup(forms): added missing type annotations
Closes #1054
2015-03-27 11:30:39 -07:00
c8385ad998 refactor(cd): remove dead code 2015-03-27 16:59:23 +01:00
9d21a6f40d chore(package.json): upgrade traceur to v0.0.87
Fix in source-map test to follow through the sourcemap chain.
2015-03-26 18:37:03 -07:00
d304f41197 docs(core): improved docs on directive lifecycle 2015-03-26 18:18:25 -07:00
8d85b839b6 feat(change_detection): pass binding propagation config to pipe registry 2015-03-26 16:57:04 -07:00
dd235f38a3 fix(build): try to eliminate build flakes by running dartstyle:format sequentially 2015-03-26 16:23:09 -07:00
5306b6dd0c fix(change_detection): expose values when detecting changes in key-value pairs
Fixes #1118

Closes #1123
2015-03-26 21:18:14 +01:00
b09624024b example(forms): added a example of using forms 2015-03-26 11:36:14 -07:00
edc3709451 fix(ElementBinderBuilder): properly bind CSS classes with "-" in their names
Fixes #1057

Closes #1059
2015-03-26 19:25:31 +01:00
e706f3477b Remove invalid super() call
Unless I'm missing something?
2015-03-26 11:10:39 -07:00
6298cb3999 chore(ci): upgrade to new version of protractor and selenium-webdriver
Protractor 2.0.0
selenium-webdriver 2.45.1
2015-03-26 10:00:46 -07:00
878fce6482 fix(ts): ts doesn't like ";;" 2015-03-26 17:32:48 +01:00
b02bd65871 feat(forms): made forms works with single controls 2015-03-26 07:48:17 -07:00
ee36aaf163 fix(tests): fixed a broken test 2015-03-26 07:46:26 -07:00
ff84506bd5 feat(forms): added support for arrays of controls 2015-03-26 07:43:25 -07:00
0ae33b7e3c refactor(compiler): factorize common util code dash <-> camel
Closes #1114
Fixes #1097
2015-03-26 15:22:35 +01:00
b1dc6239ef feat(core): @Attribute annotation
Closes #1091
Fixes #622
2015-03-26 10:51:44 +01:00
3ce0f1146f chore(dgeni): set log level to 'warning' 2015-03-26 09:31:36 +01:00
3ec837bfdb test(di): Add a test for sync binding + resolved async dependency 2015-03-26 08:38:29 +01:00
18ff2be9bb feat(ts2dart): include srcFolderInsertion in ts2dart step. 2015-03-25 21:31:40 -07:00
c0d296334c feature(ts2dart): ts2dart runs on all .js files.
Update the experimental ts2dart task to also read the .es6 files, which are the next step.
2015-03-25 17:14:06 -07:00
9a0a2e319c chore(ts2dart) remove extra semi
This breaks our ts2dart transpilation.
2015-03-25 17:06:47 -07:00
a0d86ac2bb chore(ts2dart): ts2dart doesn't support mixed typed/untyped var decl lists. 2015-03-25 16:41:33 -07:00
99045b2f6a refactor: update Dart package dependencies 2015-03-25 15:54:12 -07:00
c34ca36778 chore: build the stable branch of Dart
Now that Dart 1.9 is stable
2015-03-25 21:17:07 +01:00
58dd75a1c8 feat(compiler): Add support for setting attributes to Component host element
Fixes #1008
Fixes #1009
Closes #1052
2015-03-25 17:32:07 +01:00
f995b07876 docs: annotations edits 2015-03-24 23:28:24 +00:00
101a4aa3cf feat(PrivateComponentLoader): Explicit error message when loading a non-component
fixes #1062
2015-03-24 22:11:41 +01:00
65d759316b fix(PrivateComponentLoader): add the loader to the app injector
fixes #1063
2015-03-24 22:10:26 +01:00
19c1773133 feat(forms): added an observable of value changes to Control 2015-03-24 13:45:47 -07:00
9b3b3d325f feat(facade): added support for observables 2015-03-24 13:45:39 -07:00
43f4374944 feature(build): Include ts2dart transpile step in the Angular build.
This only transpiles one package to start with: di/
It ensures that package transpiles without errors, so no one can
introduce non-TypeScript syntax.
Next step is to widen the task inputs to cover additional packages.

See design doc for the migration:
https://docs.google.com/document/d/14RJLhu6uuv7NchFkAb6PKzOOO0L7l3Z507eKWzkEUhQ/edit

A convenience task 'ts2dart' is added for developing ts2dart, and
it runs all of the angular code through the transpiler to collect errors.
2015-03-24 10:34:46 -07:00
81e6d13241 chore: bump up the version to 2.0.0-alpha.15 2015-03-24 07:50:39 -07:00
f8e7a37c0d fix(view): fixed view instantiation to use the component template's change detector when creating BindingPropagationConfig 2015-03-24 07:49:28 -07:00
c686e7ea30 chore(doc-gen): ignore non-jsdoc style comments
Now the visitor will find the last jsdoc style comment (e.g. `/** jsdoc comment */`)
before the current code item, ignoring any inline style comments (e.g. `// inline comment`)
in between.

Closes #1072
2015-03-24 11:25:58 +00:00
7e89af8190 chore(doc-gen): move @publicModule tag-def to base package
This prevents unwanted "unknown tag" warnings when generating the non-public docs.
2015-03-24 10:57:42 +00:00
539e8e2cce chore(doc-gen): add specific template for displaying variable exports
Closes #1071
2015-03-24 10:57:42 +00:00
aab084866c doc(test): add a comment on why tests are disabled 2015-03-24 09:52:41 +01:00
0e61a86763 docs: annotations 2015-03-24 00:42:58 +00:00
1c9938ed98 chore(packaging): bump version to alpha.14 2015-03-23 17:14:55 -07:00
47c1a0f381 feat(forms): added value accessor for input=text 2015-03-23 08:53:27 -07:00
514529b5d9 refactor(formed): changed forms to use event and property setters instead of NgElement 2015-03-23 08:52:54 -07:00
a12dc7d75a refactor(forms): wrapped all validators into the Validator class 2015-03-23 08:50:56 -07:00
41b53e71e1 feat(selector): support , for multiple targets
Fixes #867
Closes #1019
2015-03-23 10:06:33 +01:00
0fb9f3bd6c fix(ElementBinderBuilder): properly bind to web component properties
Fixes #776

Closes #1024
2015-03-22 14:14:36 +01:00
81f3f32217 refactor(DirectiveParser): remove checks for missing directives
Based on the discussion in #776 we can't reliably check if a given
element has a particular property at the compilation time. As such
the existing algorithm detecting "missing" directives can't be used.

We need to see if there is a different / better algorithm or maybe
those checks need to be moved later in the process (runtime). Leaving
integration tests in place (disabled) so we can come back to the
topic after unblocking the situation.

This commit effectivelly reverts 94e203b9df
2015-03-22 14:14:36 +01:00
b35f288794 refactor(dart/transform): Use package:guinness in tests
`guinness` is a Dart port of Jasmine. Since the rest of Angular 2 uses
Jasmine, use it for the transformer too.

Closes #8

Closes #1037

Closes #1000
2015-03-21 15:18:15 -07:00
4e82cc0861 refactor(dart/transform): Test directive_linker as a unit
Formerly, it was tested only as a piece of the transformer pipeline. Add
its own directory and test the linker on its own.
2015-03-21 15:18:15 -07:00
c735644c57 refactor(dart/transform): Minor logging changes
Enable easier testing by providing a null log implementation and a way
to use it.
2015-03-21 15:18:15 -07:00
5d479fa0ae refactor(dart/transform): Remove ngData
Now that we have `Parser`, `ngData` is redundant & unnecessary.
2015-03-21 15:18:15 -07:00
8baedca972 style(dart/transform): Remove src from library directives
Conform to Angular 2 style by removing `src` from library directives.
Completed with:
```
find -name "*.dart" | xargs sed -i -e 's!library\(.*\)src\.\(.*\)!library \1\2!'
```

Closes #1005

Closes #1038
2015-03-21 14:55:11 -07:00
02aa8e7945 feat(compiler): support bindings for any attribute
Closes #1029
2015-03-21 14:55:11 -07:00
ee523efcb4 feat(ShadowCss): Support the new deep combinator syntax >>>
fixes #990

ref http://dev.w3.org/csswg/css-scoping-1/#deep-combinator

Closes #1028
2015-03-21 14:55:11 -07:00
eef5f7e06d README - don't forget to build app before start
Don't forget to build app before start HTTP server

Closes #1014
2015-03-21 14:55:11 -07:00
83402930f2 chore(install+test): single cmd to full install/test & test JS w/o Dart
* `npm install` now does a full install; auxiliary installation steps
have been integrated into the `postinstall` script.
* Updated developer docs `DEVELOPER.md` accordingly; also added
instructions to dev docs for performing full tests (via `npm test`) --
same as those run on Travis.
* Reorg in tests so that JS tests can run without a Dart env.

Partly fixes #945 **under the assumption that when running JS tests
locally, `ChromeCanary` is the desired browser to use**. Note that CI
tests (Travis) still uses `DartiumWithWebPlatform` across the board
(Maybe because ChromeCanary isn't being installed?)

Fixes #1012.

Closes #1010
2015-03-21 14:55:11 -07:00
bd48c927d0 fix(ViewContainer) removeChild called with null parent
In view_container.js, templateElement.parentNode can be null
when two template tags are nested in one another.
Accessing the parent node through view.nodes[0].parentNode fixes
the problem.

closes #997

Closes #999
2015-03-21 14:55:10 -07:00
b61b8d60b7 refactor(forEach): change to for-of with iterable
rename: foreach -> for
rename: array -> iterable
update: DartParseTreeWriter
update: naive_infinite_scroll
update: todo
fix: tests in foreach_spec

Closes #919
2015-03-21 14:19:21 -07:00
f1fca5abb6 (docs) decorator events property
As from what i understand shouldn't the event property rather be events: https://github.com/angular/angular/blob/master/modules/angular2/src/core/annotations/annotations.js#L161

Closes #1018
2015-03-21 18:26:13 +00:00
045ce3c77a Fix which dependency is injected w/ current elem.
Docs for the "Injecting a directive from the current element" indicate that having a dependency of `dependency: Dependency` should cause the current element's dependency to be injected, but then uses the ID value from the parent element in the example.

Closes #1032
2015-03-21 18:14:43 +00:00
f822066e2a docs: annotations 2015-03-21 18:05:12 +00:00
465 changed files with 19019 additions and 11300 deletions

3
.clang-format Normal file
View File

@ -0,0 +1,3 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 100

9
.gitignore vendored
View File

@ -7,6 +7,9 @@ bower_components
.pub
.DS_STORE
# Or broccoli working directory
tmp
# Or the files created by dart2js.
*.dart.js
*.dart.precompiled.js
@ -14,6 +17,9 @@ bower_components
*.js.deps
*.js.map
# Or type definitions we mirror from github
**/typings/**/*.d.ts
# Include when developing application packages.
pubspec.lock
.c9
@ -23,4 +29,7 @@ pubspec.lock
# Don't check in secret files
*secret.js
# Ignore npm debug log
npm-debug.log
/docs/bower_components/

View File

@ -8,13 +8,20 @@ env:
- E2E_BROWSERS=Dartium
- LOGS_DIR=/tmp/angular-build/logs
- ARCH=linux-x64
# Token for tsd to increase github rate limit
# See https://github.com/DefinitelyTyped/tsd#tsdrc
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# because those are not visible for pull requests, and those should also be reliable.
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
# (password is in Valentine)
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
matrix:
- MODE=js DART_CHANNEL=dev
# Dissabled until Dart v1.9 hits stable
# - MODE=dart DART_CHANNEL=stable
- MODE=dart DART_CHANNEL=stable
- MODE=dart DART_CHANNEL=dev
before_install:
- echo ${TSDRC} > .tsdrc
- export DISPLAY=:99.0
- export GIT_SHA=$(git rev-parse HEAD)
- ./scripts/ci/init_android.sh

View File

@ -3,24 +3,24 @@
This document describes how to set up your development environment to build and test Angular, both
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
See the [contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
for how to contribute your own code to
* [Prerequisite Software](#prerequisite-software)
* [Getting the Sources](#getting-the-sources)
* [Environment Variable Setup](#environment-variable-setup)
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
* [Running Tests Locally](#running-tests-locally)
* [Project Information](#project-information)
* [CI using Travis](#ci-using-travis)
* [Debugging](#debugging)
1. [Prerequisite Software](#prerequisite-software)
2. [Getting the Sources](#getting-the-sources)
3. [Environment Variable Setup](#environment-variable-setup)
4. [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
5. [Running Tests Locally](#running-tests-locally)
6. [Project Information](#project-information)
7. [CI using Travis](#ci-using-travis)
8. [Debugging](#debugging)
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
## Prerequisite Software
Before you can build and test Angular, you must install and configure the
following products on your development machine:
* [Dart](https://www.dartlang.org) (version `>=1.9.0-dev.8.0`), specifically the Dart-SDK and
* [Dart](https://www.dartlang.org) (version ` >=1.9.0 <2.0.0`), specifically the Dart-SDK and
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
@ -39,6 +39,8 @@ following products on your development machine:
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
bleeding edge functionality, built especially for developers (and early adopters).
* [Bower](http://bower.io/).
## Getting the Sources
@ -95,27 +97,26 @@ Next, install the modules and packages needed to build Angular and run tests:
```shell
# Install Angular project dependencies (package.json)
npm install
# Ensure protractor has the latest webdriver
$(npm bin)/webdriver-manager update
# Install Dart packages
pub get
```
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by
globally installing these two packages as follows:
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
*Option 1*: globally installing these two packages as follows:
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
Since global installs can become stale, we avoid their use in these instructions.
Since global installs can become stale, and required versions can vary by project, we avoid their
use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Build commands
To build Angular and prepare tests run
To build Angular and prepare tests run:
```shell
$(npm bin)/gulp build
@ -133,24 +134,37 @@ $(npm bin)/gulp clean
## Running Tests Locally
### Basic tests
### Full test suite
1. `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
watches the test files for changes and re-runs tests when files are updated).
2. `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests as
those run on Travis.
If you prefer running tests in "single-run" mode rather than watch mode use
You can selectively run either the JS or Dart versions as follows:
* `$(npm bin)/gulp test.all.js`
* `$(npm bin)/gulp test.all.dart`
### Unit tests
You can run just the unit tests as follows:
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
watches the test files for changes and re-runs tests when files are updated).
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
If you prefer running tests in "single-run" mode rather than watch mode use:
* `$(npm bin)/gulp test.unit.js/ci`
* `$(npm bin)/gulp test.unit.cjs/ci`
* `$(npm bin)/gulp test.unit.dart/ci`
**Note**: If you want to only run a single test you can alter the test you wish
to run by changing `it` to `iit` or `describe` to `ddescribe`. This will only
run that individual test and make it much easier to debug. `xit` and `xdescribe`
can also be useful to exclude a test and a group of tests respectively.
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
tests respectively.
**Note** for transpiler tests: The karma preprocessor is setup in a way so that after every test
**Note for transpiler tests**: The karma preprocessor is setup in a way so that after every test
run the transpiler is reloaded. With that it is possible to make changes to the preprocessor and
run the tests without exiting karma (just touch a test file that you would like to run).
@ -232,4 +246,3 @@ on the `debugger;` statement.
You can then step into the code and add watches.
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
the original source files are not supported at the moment.

View File

@ -36,6 +36,7 @@ comments in the source `modules/examples/src/hello_world/index.js`.
You can build this example as either JS or Dart app:
* JS:
* `$(npm bin)/gulp build.js.dev`, and
* `$(npm bin)/gulp serve.js.dev`, and
* open `localhost:8000/examples/src/hello_world/` in Chrome.
* Dart:

View File

@ -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;
}

View File

@ -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>

View File

@ -37,15 +37,19 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
// Register the processors
.processor(require('./processors/generateDocsFromComments'))
.processor(require('./processors/processModuleDocs'))
.processor(require('./processors/processClassDocs'))
.processor(require('./processors/captureModuleExports'))
.processor(require('./processors/captureClassMembers'))
.processor(require('./processors/captureModuleDocs'))
.processor(require('./processors/attachModuleDocs'))
.processor(require('./processors/cloneExportedFromDocs'))
.processor(require('./processors/generateNavigationDoc'))
.processor(require('./processors/extractTitleFromGuides'))
.processor(require('./processors/createOverviewDump'))
// Configure the log service
.config(function(log) {
log.level = 'info';
log.level = 'warning';
})
@ -62,6 +66,12 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
})
.config(function(parseTagsProcessor, getInjectables) {
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/public'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/exportedAs'));
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/docs';
@ -144,4 +154,4 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
pathTemplate: '${id}',
outputPathTemplate: GUIDES_PATH + '/${id}.html'
});
});
});

View 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;
});
}
};
};

View File

@ -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);
}

View 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);
}
});
}
};
};

View File

@ -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);
}
};
};

View File

@ -0,0 +1,37 @@
var _ = require('lodash');
module.exports = function cloneExportedFromDocs(modules, EXPORT_DOC_TYPES) {
return {
$runAfter: ['tags-parsed', 'attachModuleDocs'],
$runBefore: ['computing-ids'],
$process: function(docs) {
var extraPublicDocs = [];
_.forEach(docs, function(doc) {
if (EXPORT_DOC_TYPES.indexOf(doc.docType) === -1 || !doc.exportedAs) return;
_.forEach(doc.exportedAs, function(exportedAs) {
var exportedAsModule = modules[exportedAs];
if (!exportedAsModule) {
throw new Error('Missing module definition: "' + doc.exportedAs + '"\n' +
'Referenced in "@exportedAs" tag on class: "' + doc.moduleDoc.id + '/' + doc.name + '"');
} else {
// Add a clone of export to its "exportedAs" module
var clonedDoc = _.clone(doc);
clonedDoc.moduleDoc = exportedAsModule;
exportedAsModule.exports.push(clonedDoc);
extraPublicDocs.push(clonedDoc);
}
});
});
docs = docs.concat(extraPublicDocs);
return docs;
}
};
};

View 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);
}
};
};

View File

@ -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);
}
};
};

View File

@ -30,11 +30,18 @@ module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
while (this.currentComment &&
this.currentComment.range.end.offset < tree.location.start.offset) {
log.silly('comment: ' + this.currentComment.range.start.line + ' - ' +
this.currentComment.range.end.line + ' : ' +
this.currentComment.range.toString());
tree.commentBefore = this.currentComment;
this.currentComment.treeAfter = tree;
var commentText = this.currentComment.range.toString();
// Only store the comment if it is JSDOC style (e.g. /** some comment */)
if (/^\/\*\*([\w\W]*)\*\/$/.test(commentText)) {
log.info('comment: ' + this.currentComment.range.start.line + ' - ' +
this.currentComment.range.end.line + ' : ' +
commentText);
tree.commentBefore = this.currentComment;
this.currentComment.treeAfter = tree;
}
this.index++;
this.currentComment = this.comments[this.index];
}

View File

@ -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';

View File

@ -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

View File

@ -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();
};

View File

@ -0,0 +1,4 @@
module.exports = {
name: 'exportedAs',
multi: true
};

View File

@ -0,0 +1,4 @@
module.exports = {
name: 'public',
transforms: function(doc, tag) { return true; }
};

View File

@ -1,14 +1,33 @@
{% extends 'layout/base.template.html' %}
{% include "lib/paramList.html" -%}
{% extends 'layout/base.template.html' -%}
{% block body %}
<h1>{$ doc.name $} <span class="type">class</span></h1>
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
<h1 class="class export">{$ doc.name $} <span class="type">class</span></h1>
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a><br/>
defined in <a href="https://github.com/angular/angular/tree/master/modules/{$ doc.location.start.source.name $}.js#L{$ doc.location.start.line $}">{$ doc.location.start.source.name $}.js (line {$ doc.location.start.line $})</a></p>
<p>{$ doc.description | marked $}</p>
{%- if doc.constructorDoc or doc.members.length -%}
<h2>Members</h2>
{% for member in doc.members %}
<h3>{$ member.name $}</h3>
<p>{$ member.description | marked $}</p>
{%- if doc.constructorDoc %}
<section class="member constructor">
<h1 class="name">{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) $}</h1>
{% marked %}
{$ doc.constructorDoc.description $}
{% endmarked %}
</section>
{% endif -%}
{%- for member in doc.members %}
<section class="member">
<h1 class="name">{$ member.name $}{$ paramList(member.params) $}</h1>
{% marked %}
{$ member.description $}
{% endmarked %}
</section>
{% endfor %}
{%- endif -%}
{% endblock %}

View 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 %}

View 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 -%}

View File

@ -9,7 +9,7 @@
<h2>Exports</h2>
<ul>
{%- for exportDoc in doc.exports %}
<li><a href="/{$ exportDoc.path $}">{$ exportDoc.name $} {$ exportDoc.docType $}</a></li>
<li><a href="/{$ exportDoc.path $}"><strong>{$ exportDoc.name $}</strong> {$ exportDoc.docType $}</a></li>
{%- endfor %}
</ul>
{% endif %}

View 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>

View 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 %}

View File

@ -6,16 +6,11 @@ module.exports = new Package('angular-public', [basePackage])
.processor(require('./processors/filterPublicDocs'))
.config(function(parseTagsProcessor) {
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
})
.config(function(processClassDocs, filterPublicDocs, EXPORT_DOC_TYPES) {
processClassDocs.ignorePrivateMembers = true;
filterPublicDocs.docTypes = EXPORT_DOC_TYPES;
.config(function(captureClassMembers) {
captureClassMembers.ignorePrivateMembers = true;
})
// Configure file writing
.config(function(writeFilesProcessor) {
writeFilesProcessor.outputFolder = 'dist/public_docs';
});
});

View File

@ -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);
}
};

View File

@ -1,12 +1,13 @@
var format = require('gulp-clang-format');
var gulp = require('gulp');
var gulpPlugins = require('gulp-load-plugins')();
var shell = require('gulp-shell');
var runSequence = require('run-sequence');
var madge = require('madge');
var merge = require('merge');
var path = require('path');
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
var clean = require('./tools/build/clean');
var transpile = require('./tools/build/transpile');
var html = require('./tools/build/html');
@ -23,11 +24,24 @@ var karma = require('karma').server;
var minimist = require('minimist');
var es5build = require('./tools/build/es5build');
var runServerDartTests = require('./tools/build/run_server_dart_tests');
var sourcemaps = require('gulp-sourcemaps');
var transformCJSTests = require('./tools/build/transformCJSTests');
var ts2dart = require('gulp-ts2dart');
var tsc = require('gulp-typescript');
var util = require('./tools/build/util');
var bundler = require('./tools/build/bundle');
var replace = require('gulp-replace');
var insert = require('gulp-insert');
// dynamic require in build.broccoli.tools so we can bootstrap TypeScript compilation
function missingDynamicBroccoli() {
throw new Error('ERROR: build.broccoli.tools task should have been run before using broccoli');
}
var makeBroccoliTree = missingDynamicBroccoli, broccoliBuild = missingDynamicBroccoli;
// Note: when DART_SDK is not found, all gulp tasks ending with `.dart` will be skipped.
var DART_SDK = require('./tools/build/dartdetect')(gulp);
// -----------------------
// configuration
@ -48,6 +62,8 @@ var _HTML_DEFAULT_SCRIPTS_JS = [
{src: 'node_modules/zone.js/long-stack-trace-zone.js', mimeType: 'text/javascript', copy: true},
{src: 'node_modules/systemjs/dist/system.src.js', mimeType: 'text/javascript', copy: true},
{src: 'node_modules/systemjs/lib/extension-register.js', mimeType: 'text/javascript', copy: true},
{src: 'node_modules/systemjs/lib/extension-cjs.js', mimeType: 'text/javascript', copy: true},
{src: 'node_modules/rx/dist/rx.all.js', mimeType: 'text/javascript', copy: true},
{src: 'tools/build/snippets/runtime_paths.js', mimeType: 'text/javascript', copy: true},
{
inline: 'System.import(\'$MODULENAME$\').then(function(m) { m.main(); }, console.error.bind(console))',
@ -116,10 +132,8 @@ var CONFIG = {
transpile: {
src: {
js: ['modules/**/*.js', 'modules/**/*.es6'],
dart: ['modules/**/*.js'],
// Migrating to TypeScript, one package at a time.
// See https://docs.google.com/document/d/14RJLhu6uuv7NchFkAb6PKzOOO0L7l3Z507eKWzkEUhQ/edit
ts2dart: ['modules/angular2/src/di/*.js', 'modules/angular2/test/di/*.js', 'modules/angular2/src/test_lib/*.js']
ts: ['modules/**/*.ts'],
dart: ['modules/**/*.js']
},
options: {
js: {
@ -134,26 +148,21 @@ var CONFIG = {
}),
cjs: merge(true, _COMPILER_CONFIG_JS_DEFAULT, {
typeAssertionModule: 'rtts_assert/rtts_assert',
typeAssertions: true,
// Don't use type assertions since this is partly transpiled by typescript
typeAssertions: false,
modules: 'commonjs'
})
},
dart: {
sourceMaps: true,
annotations: true, // parse annotations
types: true, // parse types
script: false, // parse as a module
memberVariables: true, // parse class fields
outputLanguage: 'dart'
}
}
},
copy: {
js: {
cjs: {
src: ['modules/**/README.js.md', 'modules/**/package.json', 'modules/**/*.cjs'],
src: [
'modules/**/*.md', '!modules/**/*.dart.md', 'modules/**/*.png',
'modules/**/package.json'
],
pipes: {
'**/*.cjs': gulpPlugins.rename({extname: '.js'}),
'**/*.js.md': gulpPlugins.rename(function(file) {
file.basename = file.basename.substring(0, file.basename.lastIndexOf('.'));
}),
@ -170,7 +179,10 @@ var CONFIG = {
}
},
dart: {
src: ['modules/**/README.dart.md', 'modules/**/*.dart', 'modules/*/pubspec.yaml', 'modules/**/*.css', '!modules/**/e2e_test/**'],
src: [
'modules/**/*.md', '!modules/**/*.js.md', 'modules/**/*.png', 'modules/**/*.html',
'modules/**/*.dart', 'modules/*/pubspec.yaml', 'modules/**/*.css', '!modules/**/e2e_test/**'
],
pipes: {
'**/*.dart': util.insertSrcFolder(gulpPlugins, SRC_FOLDER_INSERTION.dart),
'**/*.dart.md': gulpPlugins.rename(function(file) {
@ -203,7 +215,7 @@ var CONFIG = {
},
dart: {
src: ['LICENSE'],
exclude: ['rtts_assert/'],
exclude: ['rtts_assert'],
pipes: {}
}
},
@ -282,13 +294,42 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, {
// ------------
// transpile
gulp.task('build/transpile.js.dev.es6', transpile(gulp, gulpPlugins, {
src: CONFIG.transpile.src.js,
dest: CONFIG.dest.js.dev.es6,
outputExt: 'es6',
options: CONFIG.transpile.options.js.dev,
srcFolderInsertion: CONFIG.srcFolderInsertion.js
}));
gulp.task('build/transpile.ts.cjs', function() {
var tsResult = gulp.src(CONFIG.transpile.src.ts)
.pipe(sourcemaps.init())
.pipe(tsc({
target: 'ES5',
module: /*system.js*/'commonjs',
allowNonTsExtensions: false,
typescript: require('typescript'),
//declarationFiles: true,
noEmitOnError: true
}));
var dest = gulp.dest(CONFIG.dest.js.cjs);
return merge([
// Write external sourcemap next to the js file
tsResult.js.pipe(sourcemaps.write('.')).pipe(dest),
tsResult.js.pipe(dest),
tsResult.dts.pipe(dest),
]);
});
gulp.task('build/transpile.ts.dev.es5', function() {
var tsResult = gulp.src(CONFIG.transpile.src.ts)
.pipe(sourcemaps.init())
.pipe(tsc({
target: 'ES5',
module: 'commonjs',
typescript: require('typescript'),
noEmitOnError: true
}));
return merge([
tsResult.js.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(CONFIG.dest.js.dev.es5)),
tsResult.js.pipe(gulp.dest(CONFIG.dest.js.dev.es5))
]);
});
gulp.task('build/transpile.js.dev.es5', function() {
return es5build({
@ -298,14 +339,6 @@ gulp.task('build/transpile.js.dev.es5', function() {
});
});
gulp.task('build/transpile.js.dev', function(done) {
runSequence(
'build/transpile.js.dev.es6',
'build/transpile.js.dev.es5',
done
);
});
gulp.task('build/transpile.js.prod.es6', transpile(gulp, gulpPlugins, {
src: CONFIG.transpile.src.js,
dest: CONFIG.dest.js.prod.es6,
@ -338,36 +371,13 @@ gulp.task('build/transpile.js.cjs', transpile(gulp, gulpPlugins, {
srcFolderInsertion: CONFIG.srcFolderInsertion.js
}));
gulp.task('build/transformCJSTests', function() {
return gulp.src(CONFIG.dest.js.cjs + '/angular2/test/**/*_spec.js').pipe(transformCJSTests()).pipe(gulp.dest(CONFIG.dest.js.cjs + '/angular2/test/'));
return gulp.src(CONFIG.dest.js.cjs + '/angular2/test/**/*_spec.js')
.pipe(transformCJSTests())
.pipe(gulp.dest(CONFIG.dest.js.cjs + '/angular2/test/'));
});
gulp.task('build/transpile.dart', transpile(gulp, gulpPlugins, {
src: CONFIG.transpile.src.dart,
dest: CONFIG.dest.dart,
outputExt: 'dart',
options: CONFIG.transpile.options.dart,
srcFolderInsertion: CONFIG.srcFolderInsertion.dart
}));
gulp.task('build/transpile.dart.ts2dart', function() {
return gulp.src(CONFIG.transpile.src.ts2dart)
.pipe(ts2dart.transpile())
.pipe(gulp.dest('dist/dart.ts2dart'))
});
gulp.task('build/format.dart.ts2dart', rundartpackage(gulp, gulpPlugins, {
pub: DART_SDK.PUB,
packageName: CONFIG.formatDart.packageName,
args: ['dart_style:format', '-w', 'dist/dart.ts2dart']
}));
// Temporary tasks for development on ts2dart. Will likely fail.
gulp.task('build/transpile.dart.ts2dart.all', function() {
return gulp.src(CONFIG.transpile.src.dart)
.pipe(ts2dart.transpile())
.pipe(gulp.dest('dist/dart.ts2dart'));
});
gulp.task('ts2dart', function(done) {
runSequence('build/transpile.dart.ts2dart.all', 'build/format.dart.ts2dart', done);
gulp.task('build/transpile.dart', ['build.broccoli.tools'], function() {
return broccoliBuild(makeBroccoliTree('dart'), 'dart');
});
// ------------
@ -456,7 +466,11 @@ gulp.task('build/multicopy.dart', copy.multicopy(gulp, gulpPlugins, {
// ------------
// pubspec
gulp.task('build/pubspec.dart', pubget(gulp, gulpPlugins, {
// Run a top-level `pub get` for this project.
gulp.task('pubget.dart', pubget.dir(gulp, gulpPlugins, { dir: '.', command: DART_SDK.PUB }));
// Run `pub get` over CONFIG.dest.dart
gulp.task('build/pubspec.dart', pubget.subDir(gulp, gulpPlugins, {
dir: CONFIG.dest.dart,
command: DART_SDK.PUB
}));
@ -486,7 +500,7 @@ gulp.task('build/pubbuild.dart', pubbuild(gulp, gulpPlugins, {
}));
// ------------
// format dart
// formatting
gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
pub: DART_SDK.PUB,
@ -494,6 +508,24 @@ gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
args: CONFIG.formatDart.args
}));
function doCheckFormat() {
return gulp.src(['Brocfile*.js', 'modules/**/*.ts', 'tools/**/*.ts', '!**/typings/**/*.d.ts'])
.pipe(format.checkFormat('file'));
}
gulp.task('check-format', function() {
return doCheckFormat().on('warning', function(e) {
console.log("NOTE: this will be promoted to an ERROR in the continuous build");
});
});
gulp.task('enforce-format', function() {
return doCheckFormat().on('warning', function(e) {
console.log("ERROR: Some files need formatting");
process.exit(1);
});
});
// ------------
// check circular dependencies in Node.js context
gulp.task('build/checkCircularDependencies', function (done) {
@ -545,6 +577,24 @@ gulp.task('serve/benchmarks_external.dart', pubserve(gulp, gulpPlugins, {
path: CONFIG.dest.dart + '/benchmarks_external'
}));
gulp.task('serve/examples.dart.static', pubserve(gulp, gulpPlugins, {
command: DART_SDK.PUB,
mode: 'ngstatic',
path: CONFIG.dest.dart + '/examples'
}));
gulp.task('serve/benchmarks.dart.static', pubserve(gulp, gulpPlugins, {
command: DART_SDK.PUB,
mode: 'ngstatic',
path: CONFIG.dest.dart + '/benchmarks'
}));
gulp.task('serve/benchmarks_external.dart.static', pubserve(gulp, gulpPlugins, {
command: DART_SDK.PUB,
mode: 'ngstatic',
path: CONFIG.dest.dart + '/benchmarks_external'
}));
// --------------
// doc generation
var Dgeni = require('dgeni');
@ -613,6 +663,22 @@ createDocsTasks(true);
createDocsTasks(false);
// ------------------
// CI tests suites
gulp.task('test.js', function(done) {
runSequence('test.transpiler.unittest', 'docs/test', 'test.unit.js/ci',
'test.unit.cjs/ci', done);
});
gulp.task('test.dart', function(done) {
runSequence('test.transpiler.unittest', 'docs/test', 'test.unit.dart/ci', done);
});
// Reuse the Travis scripts
// TODO: rename test_*.sh to test_all_*.sh
gulp.task('test.all.js', shell.task(['./scripts/ci/test_js.sh']))
gulp.task('test.all.dart', shell.task(['./scripts/ci/test_dart.sh']))
// karma tests
// These tests run in the browser and are allowed to access
// HTML DOM APIs.
@ -640,6 +706,7 @@ gulp.task('test.unit.cjs/ci', function () {
gulp.task('test.unit.cjs', ['build.js.cjs'], function () {
//Run tests once
runSequence('test.unit.cjs/ci', function() {});
//Watcher to transpile file changed
gulp.watch(CONFIG.transpile.src.js.concat(['modules/**/*.cjs']), function(event) {
var relPath = path.relative(__dirname, event.path).replace(/\\/g, "/");
@ -652,14 +719,15 @@ gulp.task('test.unit.cjs', ['build.js.cjs'], function () {
});
//Watcher to run tests when dist/js/cjs/angular2 is updated by the first watcher (after clearing the node cache)
gulp.watch(CONFIG.dest.js.cjs + '/angular2/**/*.js', function(event) {
for (var id in require.cache) {
for (var id in require.cache) {
if (id.replace(/\\/g, "/").indexOf(CONFIG.dest.js.cjs) > -1) {
delete require.cache[id];
delete require.cache[id];
}
}
global.assert = undefined; // https://github.com/angular/angular/issues/1340
runSequence('test.unit.cjs/ci', function() {});
});
});
// ------------------
@ -672,31 +740,22 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {
// -----------------
// test builders
gulp.task('test.transpiler.unittest', function (done) {
gulp.task('test.transpiler.unittest', function() {
return gulp.src('tools/transpiler/unittest/**/*.js')
.pipe(jasmine({
includeStackTrace: true
}))
});
// Copy test resources to dist
gulp.task('tests/transform.dart', function() {
return gulp.src('modules/angular2/test/transform/**')
.pipe(gulp.dest('dist/dart/angular2/test/transform'));
});
// -----------------
// orchestrated targets
// Builds all Dart packages, but does not compile them
gulp.task('build/packages.dart', function(done) {
runSequence(
['build/transpile.dart.ts2dart', 'build/transpile.dart', 'build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
'tests/transform.dart',
['build/format.dart.ts2dart', 'build/format.dart'],
'build/pubspec.dart',
'build/transpile.dart', // Creates the folder structure needed by subsequent tasks.
['build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
'build/format.dart',
done
);
});
@ -705,36 +764,129 @@ gulp.task('build/packages.dart', function(done) {
gulp.task('build.dart', function(done) {
runSequence(
'build/packages.dart',
'build/pubspec.dart',
'build/analyze.dart',
'build/pubbuild.dart',
done
);
});
gulp.task('build.broccoli.tools', function() {
var tsResult = gulp.src('tools/broccoli/**/*.ts')
.pipe(tsc({ target: 'ES5', module: 'commonjs' }));
return tsResult.js.pipe(gulp.dest('dist/broccoli'))
.on('end', function() {
makeBroccoliTree = require('./dist/broccoli/make-broccoli-tree');
broccoliBuild = require('./dist/broccoli/gulp');
});
});
gulp.task('broccoli.js.dev', ['build.broccoli.tools'], function() {
return broccoliBuild(makeBroccoliTree('dev'), path.join('js', 'dev'));
});
gulp.task('broccoli.js.prod', ['build.broccoli.tools'], function() {
return broccoliBuild(makeBroccoliTree('prod'), path.join('js', 'prod'));
});
gulp.task('build.js.dev', function(done) {
runSequence(
['build/transpile.js.dev', 'build/html.js.dev', 'build/copy.js.dev', 'build/multicopy.js.dev.es6'],
'broccoli.js.dev',
'build/checkCircularDependencies',
'check-format',
done
);
});
gulp.task('build.js.prod', function(done) {
runSequence(
['build/transpile.js.prod', 'build/html.js.prod', 'build/copy.js.prod', 'build/multicopy.js.prod.es6'],
done
);
});
gulp.task('build.js.prod', ['broccoli.js.prod']);
gulp.task('broccoli.js.cjs', ['build.broccoli.tools'], function() {
return broccoliBuild(makeBroccoliTree('cjs'), path.join('js', 'cjs'));
});
gulp.task('build.js.cjs', function(done) {
runSequence(
['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'],
'broccoli.js.cjs',
['build/linknodemodules.js.cjs'],
'build/transformCJSTests',
done
);
});
var bundleConfig = {
paths: {
"*": "dist/js/prod/es6/*.es6",
"rx/*": "node_modules/rx/*.js"
},
meta: {
// auto-detection fails to detect properly here - https://github.com/systemjs/builder/issues/123
'rx/dist/rx.all': {
format: 'cjs'
}
}
};
// production build
gulp.task('bundle.js.prod', ['build.js.prod'], function() {
return bundler.bundle(
bundleConfig,
'angular2/angular2',
'./dist/build/angular2.js',
{
sourceMaps: true
});
});
// minified production build
// TODO: minify zone.js
gulp.task('bundle.js.min', ['build.js.prod'], function() {
return bundler.bundle(
bundleConfig,
'angular2/angular2',
'./dist/build/angular2.min.js',
{
sourceMaps: true,
minify: true
});
});
// development build
gulp.task('bundle.js.dev', ['build.js.dev'], function() {
var devBundleConfig = merge(true, bundleConfig);
devBundleConfig.paths =
merge(true, devBundleConfig.paths, {
"*": "dist/js/dev/es6/*.es6"
});
return bundler.bundle(
devBundleConfig,
'angular2/angular2',
'./dist/build/angular2.dev.js',
{
sourceMaps: true
});
});
gulp.task('bundle.js.prod.deps', ['bundle.js.prod'], function() {
return bundler.modify(
['node_modules/zone.js/zone.js', 'dist/build/angular2.js'], 'angular2.js')
.pipe(gulp.dest('dist/bundle'));
});
gulp.task('bundle.js.min.deps', ['bundle.js.min'], function() {
return bundler.modify(
['node_modules/zone.js/zone.js', 'dist/build/angular2.min.js'], 'angular2.min.js')
.pipe(gulp.dest('dist/bundle'));
});
gulp.task('bundle.js.dev.deps', ['bundle.js.dev'], function() {
return bundler.modify(
['node_modules/zone.js/zone.js', 'node_modules/zone.js/long-stack-trace-zone.js', 'dist/build/angular2.dev.js'],
'angular2.dev.js')
.pipe(insert.append('\nzone = zone.fork(Zone.longStackTraceZone);\n'))
.pipe(gulp.dest('dist/bundle'));
});
gulp.task('bundle.js.deps', ['bundle.js.prod.deps', 'bundle.js.dev.deps', 'bundle.js.min.deps']);
gulp.task('build.js', ['build.js.dev', 'build.js.prod', 'build.js.cjs']);
gulp.task('clean', ['build/clean.js', 'build/clean.dart', 'build/clean.docs']);

View File

@ -8,9 +8,12 @@ module.exports = function(config) {
frameworks: ['dart-unittest'],
files: [
// Init and configure guiness.
{pattern: 'test-init.dart', included: true},
// Unit test files needs to be included.
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
{pattern: 'modules/*/test/**/*_spec.js', included: true},
{pattern: 'modules/*/test/**/*_spec.dart', included: true},
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
// These files are not included, they are imported by the unit tests above.
@ -56,25 +59,14 @@ module.exports = function(config) {
},
preprocessors: {
'modules/**/*.js': ['traceur'],
'tools/**/*.js': ['traceur']
'modules/**/*.js': ['ts2dart'],
'tools/**/*.js': ['ts2dart']
},
traceurPreprocessor: {
options: {
outputLanguage: 'dart',
sourceMaps: true,
script: false,
modules: 'register',
memberVariables: true,
types: true,
// typeAssertions: true,
// typeAssertionModule: 'assert',
annotations: true
},
ts2dartPreprocessor: {
resolveModuleName: file2moduleName,
transformPath: function(fileName) {
return fileName.replace('.js', '.dart');
return fileName.replace(/.js$/, '.dart');
}
},
@ -89,5 +81,5 @@ module.exports = function(config) {
});
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
config.plugins.push(require('./tools/transpiler/karma-ts2dart-preprocessor'));
};

View File

@ -18,6 +18,8 @@ module.exports = function(config) {
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'node_modules/systemjs/dist/system.src.js',
'node_modules/systemjs/lib/extension-register.js',
'node_modules/systemjs/lib/extension-cjs.js',
'node_modules/rx/dist/rx.all.js',
'node_modules/zone.js/zone.js',
'node_modules/zone.js/long-stack-trace-zone.js',

View File

@ -1,6 +1,3 @@
/**
* Define public API for Angular here.
*/
export * from './change_detection';
export * from './core';
export * from './annotations';

View File

@ -1,4 +1,11 @@
/**
* Define public API for Angular here.
* @module
* @public
* @description
*
* Annotations provide the additional information that Angular requires in order to run your application. This module
* contains [Component], [Decorator], and [View] annotations, as well as [Parent] and [Ancestor] annotations that are
* used by Angular to resolve dependencies.
*
*/
export * from './src/core/annotations/annotations';

View File

@ -1,37 +1,47 @@
export {AST} from './src/change_detection/parser/ast';
/**
* @module
* @public
* @description
* Description of the change_detection module
*/
export {
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
} from './src/change_detection/parser/ast';
export {Lexer} from './src/change_detection/parser/lexer';
export {Parser} from './src/change_detection/parser/parser';
export {Locals}
from './src/change_detection/parser/locals';
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
from './src/change_detection/exceptions';
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/change_detection/interfaces';
export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector, BindingRecord}
export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces';
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
export {DynamicProtoChangeDetector, JitProtoChangeDetector}
from './src/change_detection/proto_change_detector';
export {BindingRecord}
from './src/change_detection/binding_record';
export {DirectiveRecord}
from './src/change_detection/directive_record';
export {DynamicChangeDetector}
from './src/change_detection/dynamic_change_detector';
export {BindingPropagationConfig}
from './src/change_detection/binding_propagation_config';
export * from './src/change_detection/pipes/pipe_registry';
export {uninitialized} from './src/change_detection/change_detection_util';
export * from './src/change_detection/pipes/pipe';
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
import {DynamicProtoChangeDetector, JitProtoChangeDetector}
from './src/change_detection/proto_change_detector';
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
import {ArrayChangesFactory} from './src/change_detection/pipes/array_changes';
import {IterableChangesFactory} from './src/change_detection/pipes/iterable_changes';
import {KeyValueChangesFactory} from './src/change_detection/pipes/keyvalue_changes';
import {NullPipeFactory} from './src/change_detection/pipes/null_pipe';
export class ChangeDetection {
createProtoChangeDetector(name:string):ProtoChangeDetector{
// TODO: this should be abstract, once supported in AtScript
return null;
}
}
import {DEFAULT} from './src/change_detection/constants';
import {ChangeDetection, ProtoChangeDetector} from './src/change_detection/interfaces';
export var defaultPipes = {
"iterableDiff" : [
new ArrayChangesFactory(),
new IterableChangesFactory(),
new NullPipeFactory()
],
"keyValDiff" : [
@ -40,6 +50,10 @@ export var defaultPipes = {
]
};
/**
* @exportedAs angular2/change_detection
*/
export class DynamicChangeDetection extends ChangeDetection {
registry:PipeRegistry;
@ -48,11 +62,14 @@ export class DynamicChangeDetection extends ChangeDetection {
this.registry = registry;
}
createProtoChangeDetector(name:string):ProtoChangeDetector{
return new DynamicProtoChangeDetector(this.registry);
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new DynamicProtoChangeDetector(this.registry, changeControlStrategy);
}
}
/**
* @exportedAs angular2/change_detection
*/
export class JitChangeDetection extends ChangeDetection {
registry:PipeRegistry;
@ -61,8 +78,8 @@ export class JitChangeDetection extends ChangeDetection {
this.registry = registry;
}
createProtoChangeDetector(name:string):ProtoChangeDetector{
return new JitProtoChangeDetector(this.registry);
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new JitProtoChangeDetector(this.registry, changeControlStrategy);
}
}

View File

@ -1,13 +1,24 @@
/**
* @module
* @public
* @description
* Define public API for Angular here.
*/
export * from './src/core/annotations/visibility';
export * from './src/core/compiler/interfaces';
export * from './src/core/annotations/template';
export * from './src/core/annotations/view';
export * from './src/core/application';
export * from './src/core/application_tokens';
export * from './src/core/annotations/di';
export * from './src/core/compiler/compiler';
export * from './src/core/compiler/template_loader';
// TODO(tbosch): remove this once render migration is complete
export * from 'angular2/src/render/dom/compiler/template_loader';
export * from './src/core/compiler/dynamic_component_loader';
export {ElementRef, DirectiveRef, ComponetRef} from './src/core/compiler/element_injector';
export * from './src/core/compiler/view';
export * from './src/core/compiler/view_container';
export * from './src/core/compiler/binding_propagation_config';
export * from './src/core/dom/element';
export * from './src/core/compiler/ng_element';

View File

@ -1,6 +1,12 @@
/**
* @module
* @description
* This is a description
*/
export {Inject, InjectPromise, InjectLazy, Injectable, Optional, DependencyAnnotation} from './src/di/annotations';
export {Injector} from './src/di/injector';
export {Binding, Dependency, bind} from './src/di/binding';
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
export {Key, KeyRegistry} from './src/di/key';
export {KeyMetadataError, NoProviderError, ProviderError, AsyncBindingError, CyclicDependencyError,
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions';

View File

@ -1,4 +1,12 @@
export * from './src/directives/foreach';
/**
* @module
* @public
* @description
* Describe the directives module here
*/
export * from './src/directives/class';
export * from './src/directives/for';
export * from './src/directives/if';
export * from './src/directives/non_bindable';
export * from './src/directives/switch';

View File

@ -187,7 +187,7 @@ Example:
<pre>
```
<ul>
<li template="foreach: #item in items">
<li template="for: #item of items">
{{item}}
</li>
</ul>
@ -201,8 +201,8 @@ Example:
<pre>
```
<ul>
<template def-foreach:"item"
bind-foreach-in="items">
<template def-for:"item"
bind-for-in="items">
<li>
{{item}}
</li>
@ -221,8 +221,8 @@ Example:
<pre>
```
<template #foreach="item"
[foreach-in]="items">
<template #for="item"
[for-in]="items">
_some_content_to_repeat_
</template>
```
@ -234,8 +234,8 @@ Example:
Example:
<pre>
```
<template def-foreach="item"
bind-foreach-in="items">
<template def-for="item"
bind-for-in="items">
_some_content_to_repeat_
</template>
```
@ -408,22 +408,22 @@ NOTE: Only Viewport directives can be placed on the template element. (Decorator
### Template Microsyntax
Often times it is necessary to encode a lot of different bindings into a template to control how the instantiation
of the templates occurs. One such example is `foreach`.
of the templates occurs. One such example is `for`.
```
<form #foo=form>
</form>
<ul>
<template foreach #person [in]="people" #i="index">
<template for #person [in]="people" #i="index">
<li>{{i}}. {{person}}<li>
</template>
</ul>
```
Where:
* `foreach` triggers the foreach directive.
* `[in]="people"` binds an iterable object to the `foreach` controller.
* `#person` exports the implicit `foreach` item.
* `for` triggers the for directive.
* `[in]="people"` binds an iterable object to the `for` controller.
* `#person` exports the implicit `for` item.
* `#i=index` exports item index as `i`.
The above example is explicit but quite wordy. For this reason in most situations a short hand version of the
@ -431,7 +431,7 @@ syntax is preferable.
```
<ul>
<li template="foreach; #person; in=people; #i=index;">{{i}}. {{person}}<li>
<li template="for; #person; in=people; #i=index;">{{i}}. {{person}}<li>
</ul>
```
@ -441,19 +441,28 @@ which allows us to further shorten the text.
```
<ul>
<li template="foreach #person in people #i=index">{{i}}. {{person}}<li>
<li template="for #person of people #i=index">{{i}}. {{person}}<li>
</ul>
```
We can also optionally use `var` instead of `#` and add `:` to `foreach` which creates the following recommended
microsyntax for `foreach`.
We can also optionally use `var` instead of `#` and add `:` to `for` which creates the following recommended
microsyntax for `for`.
```
<ul>
<li template="foreach: var person in people; var i=index">{{i}}. {{person}}<li>
<li template="for: var person of people; var i=index">{{i}}. {{person}}<li>
</ul>
```
Finally, we can move the `for` keyword to the left hand side and prefix it with `*` as so:
```
<ul>
<li *for="var person of people; var i=index">{{i}}. {{person}}<li>
</ul>
```
The format is intentionally defined freely, so that developers of directives can build an expressive microsyntax for
their directives. The following code describes a more formal definition.

View File

@ -10,7 +10,7 @@ There are three different kinds of directives (described in more detail in later
1. *Decorators*: can be placed on any DOM element and can be combined with other directives.
2. *Components*: Components have an encapsulated view and can configure injectors.
3. *Viewport*: is responsible for adding or removing child views in a parent view. (i.e. foreach, if)
3. *Viewport*: is responsible for adding or removing child views in a parent view. (i.e. for, if)
@ -67,10 +67,10 @@ Here is a trivial example of a tooltip decorator. The directive will log a toolt
```
@Decorator({
selector: '[tooltip]', // CSS Selector which triggers the decorator
bind: { // List which properties need to be bound
properties: { // List which properties need to be bound
text: 'tooltip' // - DOM element tooltip property should be
}, // mapped to the directive text property.
event: { // List which events need to be mapped.
hostListeners: { // List which events need to be mapped.
mouseover: 'show' // - Invoke the show() method every time
} // the mouseover event is fired.
})
@ -112,13 +112,13 @@ Example of a component:
```
@Component({ | Component annotation
selector: 'pane', | CSS selector on <pane> element
bind: { | List which property need to be bound
properties: { | List which property need to be bound
'title': 'title', | - title mapped to component title
'open': 'open' | - open attribute mapped to component's open property
}, |
}) |
@Template({ | Template annotation
url: 'pane.html' | - URL of template HTML
@View({ | View annotation
templateUrl: 'pane.html' | - URL of template HTML
}) |
class Pane { | Component controller class
title:string; | - title property
@ -140,7 +140,7 @@ class Pane { | Component controller class
```
<div class="outer">
<h1>{{title}}</h1>
<div class="inner" [hidden]="!visible">
<div class="inner" [hidden]="!open">
<content></content>
</div>
</div>
@ -165,7 +165,7 @@ Example of usage:
## Viewport
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `foreach`.)
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `for`.)
* Viewports can only be placed on `<template>` elements (or the short hand version which uses `<element template>` attribute.)
* Only one viewport can be present per DOM template element.
@ -179,7 +179,7 @@ Viewport is a directive which can control instantiation of child views which are
```
@Viewport({
selector: '[if]',
bind: {
properties: {
'condition': 'if'
}
})
@ -220,7 +220,7 @@ To better understand the kinds of injections which are supported in Angular we h
### Injecting Services
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `services` and then letting the directive ask for the configured service.
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `injectables` and then letting the directive ask for the configured service.
This example illustrates how to inject `MyService` into `House` directive.
@ -231,10 +231,10 @@ class MyService {} | Assume a service which needs to be inject
|
@Component({ | Assume a top level application component which
selector: 'my-app', | configures the services to be injected.
services: [MyService] |
injectables: [MyService] |
}) |
@Template({ | Assume we have a template that needs to be
url: 'my_app.html', | configured with directives to be injected.
@View({ | Assume we have a template that needs to be
templateUrl: 'my_app.html', | configured with directives to be injected.
directives: [House] |
}) |
class MyApp {} |
@ -279,7 +279,7 @@ Here is an example of the kinds of injections which can be achieved:
@Component({ |
selector: 'my-app', |
template: new TemplateConfig({ |
url: 'my_app.html', |
templateUrl: 'my_app.html', |
directives: [Form, FieldSet, |
Field, Primary] |
}) |
@ -322,8 +322,8 @@ Assume the following DOM structure for `my_app.html`:
<fieldset> |
<field primary></field> |
<field></field> |
</div> |
</fieldset> |
</fieldset> |
</div> |
</form> |
```

View File

@ -12,12 +12,12 @@ Views form a tree structure which mimics the DOM tree.
nested in a tree like structure. The View tree is a simplified version of the DOM tree. A View can
have a single DOM Element or large DOM structures. The key is that the DOM tree in the View can
not undergo structural changes (only property changes).
* Views represent a running instance of a DOM Template. This implies that while elements in a View
* Views represent a running instance of a DOM View. This implies that while elements in a View
can change properties, they can not change structurally. (Structural changes such as, adding or
removing elements requires adding or removing child Views into ViewContainers).
* View can have zero or more ViewPorts. A ViewPort is a marker in the DOM which allows
the insertion of child Views.
* Views are created from a ProtoView. A ProtoView is a compiled DOM Template which is efficient at
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
creating Views.
* View contains a context object. The context represents the object instance against which all
expressions are evaluated.
@ -40,7 +40,7 @@ class Greeter {
}
```
And assume following HTML Template:
And assume following HTML View:
```
<div>
@ -90,7 +90,7 @@ Note:
An important part of an application is to be able to change the DOM structure to render data for the
user. In Angular this is done by inserting child views into the ViewPort.
Let's start with a Template such as:
Let's start with a View such as:
```
<ul>
@ -194,7 +194,7 @@ class Greeter {
}
```
And assume the following HTML Template:
And assume the following HTML View:
```
<div> | viewA(greeter)

View File

@ -43,7 +43,7 @@ class Car {
}
}
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Car).toClass(Car),
bind(Engine).toClass(Engine)
]);
@ -86,7 +86,7 @@ To avoid bugs make sure the registered objects have side-effect-free constructor
Injectors are hierarchical.
```
var child = injector.createChild([
var child = injector.resolveAndCreateChild([
bind(Engine).toClass(TurboEngine)
]);
@ -99,21 +99,21 @@ var car = child.get(Car); // uses the Car binding from the parent injector and E
You can bind to a class, a value, or a factory. It is also possible to alias existing bindings.
```
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Car).toClass(Car),
bind(Engine).toClass(Engine)
]);
var inj = new Injector([
var inj = Injector.resolveAndCreate([
Car, // syntax sugar for bind(Car).toClass(Car)
Engine
]);
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Car).toValue(new Car(new Engine()))
]);
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Car).toFactory((e) => new Car(e), [Engine]),
bind(Engine).toFactory(() => new Engine())
]);
@ -122,7 +122,7 @@ var inj = new Injector([
You can bind any token.
```
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Car).toFactory((e) => new Car(), ["engine!"]),
bind("engine!").toClass(Engine)
]);
@ -131,7 +131,7 @@ var inj = new Injector([
If you want to alias an existing binding, you can do so using `toAlias`:
```
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Engine).toClass(Engine),
bind("engine!").toAlias(Engine)
]);
@ -152,7 +152,7 @@ The `someFactory` function does not have to know that it creates an object for `
Injector can create binding on the fly if we enable default bindings.
```
var inj = new Injector([], {defaultBindings: true});
var inj = Injector.resolveAndCreate([], {defaultBindings: true});
var car = inj.get(Car); //this works as if `bind(Car).toClass(Car)` and `bind(Engine).toClass(Engine)` were present.
```
@ -226,7 +226,7 @@ class UserController {
}
}
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
UserController
])
@ -252,7 +252,7 @@ class UserController {
}
}
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
UserController
])
@ -276,7 +276,7 @@ class UserController {
constructor(ul:UserList){}
}
var inj = new Injector([UserList, UserController]);
var inj = Injector.resolveAndCreate([UserList, UserController]);
var ctrl:UserController = inj.get(UserController);
```
@ -290,7 +290,7 @@ class UserController {
constructor(@InjectPromise(UserList) ul){}
}
var inj = new Injector([UserList, UserController]);
var inj = Injector.resolveAndCreate([UserList, UserController]);
var ctrl:UserController = inj.get(UserController);
// UserController responsible for dealing with asynchrony.
expect(ctrl.ul).toBePromise();
@ -306,7 +306,7 @@ class UserController {
constructor(ul:UserList){}
}
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
UserController
]);
@ -331,7 +331,7 @@ class UserController {
constructor(@InjectPromise(UserList) ul){}
}
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(UserList).toAsyncFactory(() => fetchUsersUsingHttp().then((u) => new UserList(u))),
UserController
]);
@ -369,14 +369,14 @@ If we need a transient dependency, something that we want a new instance of ever
We can create a child injector:
```
var child = inj.createChild([MyClass]);
var child = inj.resolveAndCreateChild([MyClass]);
child.get(MyClass);
```
Or we can register a factory function:
```
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind('MyClassFactory').toFactory(dep => () => new MyClass(dep), [SomeDependency])
]);
@ -393,7 +393,7 @@ expect(instance1).not.toBe(instance2);
Most of the time we do not have to deal with keys.
```
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(Engine).toFactory(() => new TurboEngine()) //the passed in token Engine gets mapped to a key
]);
var engine = inj.get(Engine); //the passed in token Engine gets mapped to a key
@ -404,7 +404,7 @@ Now, the same example, but with keys
```
var ENGINE_KEY = Key.get(Engine);
var inj = new Injector([
var inj = Injector.resolveAndCreate([
bind(ENGINE_KEY).toFactory(() => new TurboEngine()) // no mapping
]);
var engine = inj.get(ENGINE_KEY); // no mapping

View File

@ -1,5 +1,15 @@
/**
* @module
* @public
* @description
* This module is used for handling complex input, by defining and building a [FormObject] that consists of [Control]
* objects, and mapping them onto the DOM. [Control] objects can then be used to read information from the form DOM
* elements.
*
*/
export * from './src/forms/model';
export * from './src/forms/directives';
export * from './src/forms/validators';
export * from './src/forms/validator_directives';
export * from './src/forms/form_builder';
export * from './src/forms/form_builder';

View File

@ -0,0 +1,2 @@
// Globals are provided by lang.dart in Dart.
// This file exists to prevent global.ts from being transpiled.

View File

@ -0,0 +1,24 @@
/**
* This file contains declarations of global symbols we reference in our code
*/
declare var assert: any;
declare var global: Window;
type int = number;
interface List<T> extends Array<T> {
}
interface Window {
Object: typeof Object;
Array: typeof Array;
Map: typeof Map;
Set: typeof Set;
Date: typeof Date;
RegExp: typeof RegExp;
JSON: typeof JSON;
Math: typeof Math;
assert: typeof assert;
NaN: typeof NaN;
gc(): void;
}

View File

@ -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
View File

@ -0,0 +1,6 @@
/**
* @module
* @public
* @description
* This module provides advanced support for extending change detection.
*/

View File

@ -9,11 +9,13 @@ homepage: <%= packageJson.homepage %>
environment:
sdk: '>=1.9.0-dev.8.0'
dependencies:
analyzer: '^0.22.4'
analyzer: '>=0.22.4 <0.25.0'
barback: '^0.15.2+2'
code_transformers: '^0.2.5'
dart_style: '^0.1.3'
html5lib: '^0.12.0'
html: '^0.12.0'
stack_trace: '^1.1.1'
transformers:
- angular2
dev_dependencies:
guinness: "^0.1.17"

View File

@ -0,0 +1,22 @@
library angular2.src.analysis.analyzer_plugin;
import 'package:analyzer/plugin/plugin.dart';
/// Contribute a plugin to the dart analyzer for analysis of
/// Angular 2 dart code.
class AngularAnalyzerPlugin implements Plugin {
/// the unique indetifier for this plugin
static const String UNIQUE_IDENTIFIER = 'angular2.analysis';
@override
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
@override
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {}
@override
void registerExtensions(RegisterExtension registerExtension) {
// TODO(keerti): register extension for analysis
}
}

View File

@ -0,0 +1,23 @@
library angular2.src.analysis.server_plugin;
import 'package:analyzer/plugin/plugin.dart';
/// Contribute a plugin for services such as completions, indexing and refactoring
/// of Angular 2 dart code.
class AngularServerPlugin implements Plugin {
/// the unique indetifier for this plugin
static const String UNIQUE_IDENTIFIER = 'angular2.analysis.services';
@override
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
@override
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {}
@override
void registerExtensions(RegisterExtension registerExtension) {
// TODO: register extension for code completions, indexing etc
}
}

View File

@ -1,25 +1,36 @@
import {isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
import {BindingPropagationConfig} from './binding_propagation_config';
import {ChangeDetector} from './interfaces';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
export class AbstractChangeDetector extends ChangeDetector {
children:List;
lightDomChildren:List;
shadowDomChildren:List;
parent:ChangeDetector;
mode:string;
bindingPropagationConfig:BindingPropagationConfig;
constructor() {
super();
this.children = [];
this.mode = CHECK_ALWAYS;
this.lightDomChildren = [];
this.shadowDomChildren = [];
this.bindingPropagationConfig = new BindingPropagationConfig(this);
this.mode = null;
}
addChild(cd:ChangeDetector) {
ListWrapper.push(this.children, cd);
ListWrapper.push(this.lightDomChildren, cd);
cd.parent = this;
}
removeChild(cd:ChangeDetector) {
ListWrapper.remove(this.children, cd);
ListWrapper.remove(this.lightDomChildren, cd);
}
addShadowDomChild(cd:ChangeDetector) {
ListWrapper.push(this.shadowDomChildren, cd);
cd.parent = this;
}
remove() {
@ -38,17 +49,30 @@ export class AbstractChangeDetector extends ChangeDetector {
if (this.mode === DETACHED || this.mode === CHECKED) return;
this.detectChangesInRecords(throwOnChange);
this._detectChangesInChildren(throwOnChange);
this._detectChangesInLightDomChildren(throwOnChange);
this.callOnAllChangesDone();
this._detectChangesInShadowDomChildren(throwOnChange);
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
}
detectChangesInRecords(throwOnChange:boolean){}
callOnAllChangesDone(){}
_detectChangesInChildren(throwOnChange:boolean) {
var children = this.children;
for(var i = 0; i < children.length; ++i) {
children[i]._detectChanges(throwOnChange);
_detectChangesInLightDomChildren(throwOnChange:boolean) {
var c = this.lightDomChildren;
for(var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange);
}
}
_detectChangesInShadowDomChildren(throwOnChange:boolean) {
var c = this.shadowDomChildren;
for(var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange);
}
}

View File

@ -1,7 +1,8 @@
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'angular2/change_detection';
import {ChangeDetector} from './interfaces';
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
/**
* @publicModule angular2/angular2
* @exportedAs angular2/change_detection
*/
export class BindingPropagationConfig {
_cd:ChangeDetector;

View File

@ -0,0 +1,58 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {SetterFn} from 'angular2/src/reflection/types';
import {AST} from './parser/ast';
import {DirectiveRecord} from './directive_record';
const DIRECTIVE="directive";
const ELEMENT="element";
const TEXT_NODE="textNode";
export class BindingRecord {
mode:string;
ast:AST;
elementIndex:number;
propertyName:string;
setter:SetterFn;
directiveRecord:DirectiveRecord;
constructor(mode:string, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
this.mode = mode;
this.ast = ast;
this.elementIndex = elementIndex;
this.propertyName = propertyName;
this.setter = setter;
this.directiveRecord = directiveRecord;
}
callOnChange() {
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
}
isDirective() {
return this.mode === DIRECTIVE;
}
isElement() {
return this.mode === ELEMENT;
}
isTextNode() {
return this.mode === TEXT_NODE;
}
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
return new BindingRecord(DIRECTIVE, ast, 0, propertyName, setter, directiveRecord);
}
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
return new BindingRecord(ELEMENT, ast, elementIndex, propertyName, null, null);
}
static createForTextNode(ast:AST, elementIndex:number) {
return new BindingRecord(TEXT_NODE, ast, elementIndex, null, null, null);
}
}

View File

@ -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";
}
}

View File

@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {AbstractChangeDetector} from './abstract_change_detector';
import {ChangeDetectionUtil} from './change_detection_util';
import {DirectiveRecord} from './directive_record';
import {
ProtoRecord,
@ -15,6 +16,7 @@ import {
RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
@ -23,103 +25,47 @@ import {
* that "emulates" what the developer would write by hand to implement the same
* kind of behaviour.
*
* For example: An expression `address.city` will result in the following class:
*
* var ChangeDetector0 = function ChangeDetector0(dispatcher, protos) {
* AbstractChangeDetector.call(this);
* this.dispatcher = dispatcher;
* this.protos = protos;
*
* this.context = ChangeDetectionUtil.unitialized();
* this.address0 = ChangeDetectionUtil.unitialized();
* this.city1 = ChangeDetectionUtil.unitialized();
* }
* ChangeDetector0.prototype = Object.create(AbstractChangeDetector.prototype);
*
* ChangeDetector0.prototype.detectChangesInRecords = function(throwOnChange) {
* var address0;
* var city1;
* var change;
* var changes = null;
* var temp;
* var context = this.context;
*
* address0 = context.address;
* if (address0 !== this.address0) {
* this.address0 = address0;
* }
*
* city1 = address0.city;
* if (city1 !== this.city1) {
* changes = ChangeDetectionUtil.addRecord(changes,
* ChangeDetectionUtil.simpleChangeRecord(this.protos[1].bindingMemento, this.city1, city1));
* this.city1 = city1;
* }
*
* if (changes.length > 0) {
* if(throwOnChange) ChangeDetectionUtil.throwOnChange(this.protos[1], changes[0]);
* this.dispatcher.onRecordChange('address.city', changes);
* changes = null;
* }
* }
*
*
* ChangeDetector0.prototype.hydrate = function(context, locals) {
* this.context = context;
* this.locals = locals;
* }
*
* ChangeDetector0.prototype.dehydrate = function(context) {
* this.context = ChangeDetectionUtil.unitialized();
* this.address0 = ChangeDetectionUtil.unitialized();
* this.city1 = ChangeDetectionUtil.unitialized();
* this.locals = null;
* }
*
* ChangeDetector0.prototype.hydrated = function() {
* return this.context !== ChangeDetectionUtil.unitialized();
* }
*
* return ChangeDetector0;
*
*
* The only thing the generated class depends on is the super class AbstractChangeDetector.
*
* The implementation comprises two parts:
* * ChangeDetectorJITGenerator has the logic of how everything fits together.
* * template functions (e.g., constructorTemplate) define what code is generated.
*/
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
var UTIL = "ChangeDetectionUtil";
var DISPATCHER_ACCESSOR = "this.dispatcher";
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
var PROTOS_ACCESSOR = "this.protos";
var DIRECTIVES_ACCESSOR = "this.directiveRecords";
var CONTEXT_ACCESSOR = "this.context";
var CHANGE_LOCAL = "change";
var CHANGES_LOCAL = "changes";
var LOCALS_ACCESSOR = "this.locals";
var MODE_ACCESSOR = "this.mode";
var TEMP_LOCAL = "temp";
var CURRENT_PROTO = "currentProto";
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
function typeTemplate(type:string, cons:string, detectChanges:string,
notifyOnAllChangesDone:string, setContext:string):string {
return `
${cons}
${detectChanges}
${notifyOnAllChangesDone}
${setContext};
return function(dispatcher, pipeRegistry) {
return new ${type}(dispatcher, pipeRegistry, protos);
return new ${type}(dispatcher, pipeRegistry, protos, directiveRecords);
}
`;
}
function constructorTemplate(type:string, fieldsDefinitions:string):string {
return `
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
${ABSTRACT_CHANGE_DETECTOR}.call(this);
${DISPATCHER_ACCESSOR} = dispatcher;
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
${PROTOS_ACCESSOR} = protos;
${DIRECTIVES_ACCESSOR} = directiveRecords;
${LOCALS_ACCESSOR} = null;
${fieldsDefinitions}
}
@ -131,15 +77,23 @@ function pipeOnDestroyTemplate(pipeNames:List) {
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
}
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
directiveFieldNames:List<String>):string {
var directiveInit = "";
for(var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit += `${directiveFieldNames[i]} = directives.directive(this.directiveRecords[${i}]);\n`;
}
return `
${type}.prototype.hydrate = function(context, locals) {
${type}.prototype.hydrate = function(context, locals, directives) {
${MODE_ACCESSOR} = "${mode}";
${CONTEXT_ACCESSOR} = context;
${LOCALS_ACCESSOR} = locals;
${directiveInit}
}
${type}.prototype.dehydrate = function() {
${pipeOnDestroy}
${fieldsDefinitions}
${fieldDefinitions}
${LOCALS_ACCESSOR} = null;
}
${type}.prototype.hydrated = function() {
@ -156,13 +110,26 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
`;
}
function callOnAllChangesDoneTemplate(type:string, body:string):string {
return `
${type}.prototype.callOnAllChangesDone = function() {
${body}
}
`;
}
function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
function onAllChangesDoneTemplate(directive:string):string {
return `${directive}.onAllChangesDone();`;
}
function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
return `
${localDefinitions}
${changeDefinitions}
var ${TEMP_LOCAL};
var ${CHANGE_LOCAL};
var ${CURRENT_PROTO};
var ${CHANGES_LOCAL} = null;
context = ${CONTEXT_ACCESSOR};
@ -170,45 +137,41 @@ ${records}
`;
}
function notifyTemplate(index:number):string{
return `
if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
if(throwOnChange) ${UTIL}.throwOnChange(${PROTOS_ACCESSOR}[${index}], ${CHANGES_LOCAL}[0]);
${DISPATCHER_ACCESSOR}.onRecordChange(${PROTOS_ACCESSOR}[${index}].directiveMemento, ${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
}
`;
}
function pipeCheckTemplate(context:string, pipe:string, pipeType:string,
value:string, change:string, addRecord:string, notify:string):string{
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
oldValue:string, newValue:string, change:string, update:string,
addToChanges, lastInDirective:string):string{
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
if (${pipe} === ${UTIL}.unitialized()) {
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
} else if (!${pipe}.supports(${context})) {
${pipe}.onDestroy();
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
}
${CHANGE_LOCAL} = ${pipe}.transform(${context});
if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) {
${value} = ${CHANGE_LOCAL};
${newValue} = ${pipe}.transform(${context});
if (! ${UTIL}.noChangeMarker(${newValue})) {
${change} = true;
${addRecord}
${update}
${addToChanges}
${oldValue} = ${newValue};
}
${notify}
${lastInDirective}
`;
}
function referenceCheckTemplate(assignment, newValue, oldValue, change, addRecord, notify) {
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
update:string, addToChanges:string, lastInDirective:string):string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${assignment}
if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
${change} = true;
${addRecord}
${update}
${addToChanges}
${oldValue} = ${newValue};
}
${notify}
${lastInDirective}
`;
}
@ -237,23 +200,49 @@ if (${cond}) {
`;
}
function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newValue:string) {
return `${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`;
function addToChangesTemplate(oldValue:string, newValue:string):string {
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
}
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${directiveProperty} = ${newValue};
`;
}
function updateElementTemplate(oldValue:string, newValue:string):string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
`;
}
function notifyOnChangesTemplate(directive:string):string{
return `
if(${CHANGES_LOCAL}) {
${directive}.onChange(${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
}
`;
}
export class ChangeDetectorJITGenerator {
typeName:string;
records:List<ProtoRecord>;
localNames:List<String>;
changeNames:List<String>;
fieldNames:List<String>;
pipeNames:List<String>;
directiveRecords:List;
localNames:List<string>;
changeNames:List<string>;
fieldNames:List<string>;
pipeNames:List<string>;
changeDetectionStrategy:stirng;
constructor(typeName:string, records:List<ProtoRecord>) {
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveRecords:List) {
this.typeName = typeName;
this.changeDetectionStrategy = changeDetectionStrategy;
this.records = records;
this.directiveRecords = directiveRecords;
this.localNames = this.getLocalNames(records);
this.changeNames = this.getChangeNames(this.localNames);
@ -261,7 +250,7 @@ export class ChangeDetectorJITGenerator {
this.pipeNames = this.getPipeNames(this.localNames);
}
getLocalNames(records:List<ProtoRecord>):List<String> {
getLocalNames(records:List<ProtoRecord>):List<string> {
var index = 0;
var names = records.map((r) => {
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
@ -270,21 +259,23 @@ export class ChangeDetectorJITGenerator {
return ["context"].concat(names);
}
getChangeNames(localNames:List<String>):List<String> {
getChangeNames(localNames:List<string>):List<string> {
return localNames.map((n) => `change_${n}`);
}
getFieldNames(localNames:List<String>):List<String> {
getFieldNames(localNames:List<string>):List<string> {
return localNames.map((n) => `this.${n}`);
}
getPipeNames(localNames:List<String>):List<String> {
getPipeNames(localNames:List<string>):List<string> {
return localNames.map((n) => `this.${n}_pipe`);
}
generate():Function {
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records);
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
this.genCallOnAllChangesDone(), this.genHydrate());
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
}
genConstructor():string {
@ -292,21 +283,31 @@ export class ChangeDetectorJITGenerator {
}
genHydrate():string {
return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getnonNullPipeNames()));
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getNonNullPipeNames()), this.getDirectiveFieldNames());
}
getDirectiveFieldNames():List<string> {
return this.directiveRecords.map((d) => this.getDirective(d));
}
getDirective(d:DirectiveRecord) {
return `this.directive_${d.name}`;
}
genFieldDefinitions() {
var fields = [];
fields = fields.concat(this.fieldNames);
fields = fields.concat(this.getnonNullPipeNames());
fields = fields.concat(this.getNonNullPipeNames());
fields = fields.concat(this.getDirectiveFieldNames());
return fieldDefinitionsTemplate(fields);
}
getnonNullPipeNames():List<String> {
getNonNullPipeNames():List<string> {
var pipes = [];
this.records.forEach((r) => {
if (r.mode === RECORD_TYPE_PIPE) {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
pipes.push(this.pipeNames[r.selfIndex]);
}
});
@ -314,13 +315,28 @@ export class ChangeDetectorJITGenerator {
}
genDetectChanges():string {
var body = this.genBody();
var body = this.genDetectChangesBody();
return detectChangesTemplate(this.typeName, body);
}
genBody():string {
genCallOnAllChangesDone():string {
var notifications = [];
var dirs = this.directiveRecords;
for (var i = dirs.length - 1; i >= 0; --i) {
var dir = dirs[i];
if (dir.callOnAllChangesDone) {
var directive = `this.directive_${dir.name}`;
notifications.push(onAllChangesDoneTemplate(directive));
}
}
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
}
genDetectChangesBody():string {
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
}
genLocalDefinitions():string {
@ -332,7 +348,7 @@ export class ChangeDetectorJITGenerator {
}
genRecord(r:ProtoRecord):string {
if (r.mode === RECORD_TYPE_PIPE) {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
return this.genPipeCheck (r);
} else {
return this.genReferenceCheck(r);
@ -341,26 +357,33 @@ export class ChangeDetectorJITGenerator {
genPipeCheck(r:ProtoRecord):string {
var context = this.localNames[r.contextIndex];
var pipe = this.pipeNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var change = this.changeNames[r.selfIndex];
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
var notify = this.genNotify(r);
var pipe = this.pipeNames[r.selfIndex];
var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null";
return pipeCheckTemplate(context, pipe, r.name, newValue, change, addRecord, notify);
var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genNotifyOnChanges(r);
return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change,
update, addToChanges, lastInDirective);
}
genReferenceCheck(r:ProtoRecord):string {
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var change = this.changeNames[r.selfIndex];
var assignment = this.genUpdateCurrentValue(r);
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
var notify = this.genNotify(r);
var check = referenceCheckTemplate(assignment, newValue, oldValue, change, r.lastInBinding ? addRecord : '', notify);;
var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genNotifyOnChanges(r);
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
update, addToChanges, lastInDirective);
if (r.isPureFunction()) {
return this.ifChangedGuard(r, check);
} else {
@ -427,8 +450,34 @@ export class ChangeDetectorJITGenerator {
return JSON.stringify(value);
}
genNotify(r):string{
return r.lastInDirective ? notifyTemplate(r.selfIndex - 1) : '';
genUpdateDirectiveOrElement(r:ProtoRecord):string {
if (! r.lastInBinding) return "";
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
var br = r.bindingRecord;
if (br.isDirective()) {
var directiveProperty = `${this.getDirective(br.directiveRecord)}.${br.propertyName}`;
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
} else {
return updateElementTemplate(oldValue, newValue);
}
}
genAddToChanges(r:ProtoRecord):string {
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
}
genNotifyOnChanges(r:ProtoRecord):string{
var br = r.bindingRecord;
if (r.lastInDirective && br.callOnChange()) {
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord));
} else {
return "";
}
}
genArgs(r:ProtoRecord):string {

View File

@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {ProtoRecord} from './proto_record';
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
import {NO_CHANGE} from './pipes/pipe';
import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
export var uninitialized = new Object();
@ -39,31 +39,7 @@ var _simpleChanges = [
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null)
]
var _changeRecordsIndex = 0;
var _changeRecords = [
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null)
]
];
function _simpleChange(previousValue, currentValue) {
var index = _simpleChangesIndex++ % 20;
@ -73,16 +49,6 @@ function _simpleChange(previousValue, currentValue) {
return s;
}
function _changeRecord(bindingMemento, change) {
var index = _changeRecordsIndex++ % 20;
var s = _changeRecords[index];
s.bindingMemento = bindingMemento;
s.change = change;
return s;
}
var _singleElementList = [null];
export class ChangeDetectionUtil {
static unitialized() {
return uninitialized;
@ -151,29 +117,19 @@ export class ChangeDetectionUtil {
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
}
static changeDetectionMode(strategy:string) {
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
}
static simpleChange(previousValue:any, currentValue:any):SimpleChange {
return _simpleChange(previousValue, currentValue);
}
static changeRecord(memento:any, change:any):ChangeRecord {
return _changeRecord(memento, change);
}
static simpleChangeRecord(memento:any, previousValue:any, currentValue:any):ChangeRecord {
return _changeRecord(memento, _simpleChange(previousValue, currentValue));
}
static addRecord(updatedRecords:List, changeRecord:ChangeRecord):List {
if (isBlank(updatedRecords)) {
updatedRecords = _singleElementList;
updatedRecords[0] = changeRecord;
} else if (updatedRecords === _singleElementList) {
updatedRecords = [_singleElementList[0], changeRecord];
} else {
ListWrapper.push(updatedRecords, changeRecord);
static addChange(changes, propertyName:string, change){
if (isBlank(changes)) {
changes = {};
}
return updatedRecords;
changes[propertyName] = change;
return changes;
}
}

View File

@ -46,8 +46,7 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
r.fixedArgs,
contextIndex,
selfIndex,
r.bindingMemento,
r.directiveMemento,
r.bindingRecord,
r.expressionAsString,
r.lastInBinding,
r.lastInDirective
@ -74,8 +73,7 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
r.fixedArgs,
contextIndex,
selfIndex,
r.bindingMemento,
r.directiveMemento,
r.bindingRecord,
r.expressionAsString,
r.lastInBinding,
r.lastInDirective

View 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";

View File

@ -0,0 +1,19 @@
export class DirectiveRecord {
elementIndex:number;
directiveIndex:number;
callOnAllChangesDone:boolean;
callOnChange:boolean;
constructor(elementIndex:number, directiveIndex:number,
callOnAllChangesDone:boolean,
callOnChange:boolean) {
this.elementIndex = elementIndex;
this.directiveIndex = directiveIndex;
this.callOnAllChangesDone = callOnAllChangesDone;
this.callOnChange = callOnChange;
}
get name() {
return `${this.elementIndex}_${this.directiveIndex}`;
}
}

View File

@ -2,8 +2,10 @@ import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/f
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {AbstractChangeDetector} from './abstract_change_detector';
import {BindingRecord} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {PipeRegistry} from './pipes/pipe_registry';
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
import {
@ -17,6 +19,7 @@ import {
RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
@ -33,8 +36,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
prevContexts:List;
protos:List<ProtoRecord>;
directives:any;
directiveRecords:List;
changeControlStrategy:string;
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
protoRecords:List<ProtoRecord>, directiveRecords:List) {
super();
this.dispatcher = dispatcher;
this.pipeRegistry = pipeRegistry;
@ -49,13 +56,18 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
ListWrapper.fill(this.prevContexts, uninitialized);
ListWrapper.fill(this.changes, false);
this.locals = null;
this.directives = null;
this.protos = protoRecords;
this.directiveRecords = directiveRecords;
this.changeControlStrategy = changeControlStrategy;
}
hydrate(context:any, locals:any) {
hydrate(context:any, locals:any, directives:any) {
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
this.values[0] = context;
this.locals = locals;
this.directives = directives;
}
dehydrate() {
@ -82,28 +94,57 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
detectChangesInRecords(throwOnChange:boolean) {
var protos:List<ProtoRecord> = this.protos;
var updatedRecords = null;
var changes = null;
for (var i = 0; i < protos.length; ++i) {
var proto:ProtoRecord = protos[i];
var change = this._check(proto);
var change = this._check(proto);
if (isPresent(change)) {
var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change);
updatedRecords = ChangeDetectionUtil.addRecord(updatedRecords, record);
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change);
this._updateDirectiveOrElement(change, proto.bindingRecord);
changes = this._addChange(proto.bindingRecord, change, changes);
}
if (proto.lastInDirective && isPresent(updatedRecords)) {
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, updatedRecords[0]);
this.dispatcher.onRecordChange(proto.directiveMemento, updatedRecords);
updatedRecords = null;
if (proto.lastInDirective && isPresent(changes)) {
this._directive(proto.bindingRecord.directiveRecord).onChange(changes);
changes = null;
}
}
}
callOnAllChangesDone() {
var dirs = this.directiveRecords;
for (var i = dirs.length - 1; i >= 0; --i) {
var dir = dirs[i];
if (dir.callOnAllChangesDone) {
this._directive(dir).onAllChangesDone();
}
}
}
_updateDirectiveOrElement(change, bindingRecord) {
if (isBlank(bindingRecord.directiveRecord)) {
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
} else {
bindingRecord.setter(this._directive(bindingRecord.directiveRecord), change.currentValue);
}
}
_addChange(bindingRecord:BindingRecord, change, changes) {
if (bindingRecord.callOnChange()) {
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
} else {
return changes;
}
}
_directive(directive:DirectiveRecord) {
return this.directives.directive(directive);
}
_check(proto:ProtoRecord) {
try {
if (proto.mode == RECORD_TYPE_PIPE) {
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
return this._pipeCheck(proto);
} else {
return this._referenceCheck(proto);
@ -202,7 +243,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
if (isPresent(storedPipe)) {
storedPipe.onDestroy();
}
var pipe = this.pipeRegistry.get(proto.name, context);
// Currently, only pipes that used in bindings in the template get
// the bindingPropagationConfig of the encompassing component.
//
// In the future, pipes declared in the bind configuration should
// be able to access the bindingPropagationConfig of that component.
var bpc = proto.mode === RECORD_TYPE_BINDING_PIPE ? this.bindingPropagationConfig : null;
var pipe = this.pipeRegistry.get(proto.name, context, bpc);
this._writePipe(proto, pipe);
return pipe;
}
@ -255,11 +303,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
var _singleElementList = [null];
function isSame(a, b) {
if (a === b) return true;
if (a instanceof String && b instanceof String && a == b) return true;
if ((a !== a) && (b !== b)) return true;
return false;
}

View File

@ -1,52 +1,22 @@
import {List} from 'angular2/src/facade/collection';
import {Locals} from './parser/locals';
import {DEFAULT} from './constants';
import {BindingRecord} from './binding_record';
export class ChangeRecord {
bindingMemento:any;
change:any;
constructor(bindingMemento, change) {
this.bindingMemento = bindingMemento;
this.change = change;
}
//REMOVE IT
get currentValue() {
return this.change.currentValue;
}
get previousValue() {
return this.change.previousValue;
export class ProtoChangeDetector {
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List):ChangeDetector{
return null;
}
}
/**
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
* will become CHECKED.
*/
export const CHECK_ONCE="CHECK_ONCE";
/**
* CHECKED means that the change detector should be skipped until its mode changes to
* CHECK_ONCE or CHECK_ALWAYS.
*/
export const CHECKED="CHECKED";
/**
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
* will remain CHECK_ALWAYS.
*/
export const CHECK_ALWAYS="ALWAYS_CHECK";
/**
* DETACHED means that the change detector sub tree is not a part of the main tree and
* should be skipped.
*/
export const DETACHED="DETACHED";
export class ChangeDetection {
createProtoChangeDetector(name:string, changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
return null;
}
}
export class ChangeDispatcher {
onRecordChange(directiveMemento, records:List<ChangeRecord>) {}
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
}
export class ChangeDetector {
@ -54,9 +24,10 @@ export class ChangeDetector {
mode:string;
addChild(cd:ChangeDetector) {}
addShadowDomChild(cd:ChangeDetector) {}
removeChild(cd:ChangeDetector) {}
remove() {}
hydrate(context:any, locals:Locals) {}
hydrate(context:any, locals:Locals, directives:any) {}
dehydrate() {}
markPathToRootAsCheckOnce() {}

View File

@ -168,11 +168,13 @@ export class Pipe extends AST {
exp:AST;
name:string;
args:List<AST>;
constructor(exp:AST, name:string, args:List) {
inBinding:boolean;
constructor(exp:AST, name:string, args:List, inBinding:boolean) {
super();
this.exp = exp;
this.name = name;
this.args = args;
this.inBinding = inBinding;
}
visit(visitor) {
@ -418,7 +420,6 @@ export class TemplateBinding {
name:string;
expression:ASTWithSource;
constructor(key:string, keyIsVar:boolean, name:string, expression:ASTWithSource) {
super();
this.key = key;
this.keyIsVar = keyIsVar;
// only either name or expression will be filled.
@ -445,6 +446,72 @@ export class AstVisitor {
visitPrefixNot(ast:PrefixNot) {}
}
export class AstTransformer {
visitImplicitReceiver(ast:ImplicitReceiver) {
return new ImplicitReceiver();
}
visitInterpolation(ast:Interpolation) {
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
}
visitLiteralPrimitive(ast:LiteralPrimitive) {
return new LiteralPrimitive(ast.value);
}
visitAccessMember(ast:AccessMember) {
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
}
visitMethodCall(ast:MethodCall) {
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
}
visitFunctionCall(ast:FunctionCall) {
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
}
visitLiteralArray(ast:LiteralArray) {
return new LiteralArray(this.visitAll(ast.expressions));
}
visitLiteralMap(ast:LiteralMap) {
return new LiteralMap(ast.keys, this.visitAll(ast.values));
}
visitBinary(ast:Binary) {
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
}
visitPrefixNot(ast:PrefixNot) {
return new PrefixNot(ast.expression.visit(this));
}
visitConditional(ast:Conditional) {
return new Conditional(
ast.condition.visit(this),
ast.trueExp.visit(this),
ast.falseExp.visit(this)
);
}
visitPipe(ast:Pipe) {
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
}
visitKeyedAccess(ast:KeyedAccess) {
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
}
visitAll(asts:List) {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
}
var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0],
[0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]];

View File

@ -58,7 +58,7 @@ export class Parser {
if (ListWrapper.isEmpty(pipes)) return bindingAst;
var res = ListWrapper.reduce(pipes,
(result, currentPipeName) => new Pipe(result, currentPipeName, []),
(result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
bindingAst.ast);
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
}
@ -220,7 +220,7 @@ class _ParseAST {
while (this.optionalCharacter($COLON)) {
ListWrapper.push(args, this.parseExpression());
}
result = new Pipe(result, name, args);
result = new Pipe(result, name, args, true);
}
return result;
}

View File

@ -16,17 +16,20 @@ import {
import {NO_CHANGE, Pipe} from './pipe';
export class ArrayChangesFactory {
export class IterableChangesFactory {
supports(obj):boolean {
return ArrayChanges.supportsObj(obj);
return IterableChanges.supportsObj(obj);
}
create():Pipe {
return new ArrayChanges();
create(bpc):Pipe {
return new IterableChanges();
}
}
export class ArrayChanges extends Pipe {
/**
* @exportedAs angular2/pipes
*/
export class IterableChanges extends Pipe {
_collection;
_length:int;
_linkedRecords:_DuplicateMap;
@ -66,7 +69,7 @@ export class ArrayChanges extends Pipe {
}
supports(obj):boolean {
return ArrayChanges.supportsObj(obj);
return IterableChanges.supportsObj(obj);
}
get collection() {
@ -126,7 +129,8 @@ export class ArrayChanges extends Pipe {
var record:CollectionChangeRecord = this._itHead;
var mayBeDirty:boolean = false;
var index:int, item;
var index:int;
var item;
if (ListWrapper.isList(collection)) {
var list = collection;
@ -500,6 +504,9 @@ export class ArrayChanges extends Pipe {
}
}
/**
* @exportedAs angular2/pipes
*/
export class CollectionChangeRecord {
currentIndex:int;
previousIndex:int;

View File

@ -3,16 +3,22 @@ import {stringify, looseIdentical, isJsObject} from 'angular2/src/facade/lang';
import {NO_CHANGE, Pipe} from './pipe';
/**
* @exportedAs angular2/pipes
*/
export class KeyValueChangesFactory {
supports(obj):boolean {
return KeyValueChanges.supportsObj(obj);
}
create():Pipe {
create(bpc):Pipe {
return new KeyValueChanges();
}
}
/**
* @exportedAs angular2/pipes
*/
export class KeyValueChanges extends Pipe {
_records:Map;
@ -107,9 +113,9 @@ export class KeyValueChanges extends Pipe {
var newSeqRecord;
if (oldSeqRecord !== null && key === oldSeqRecord.key) {
newSeqRecord = oldSeqRecord;
if (!looseIdentical(value, oldSeqRecord._currentValue)) {
oldSeqRecord._previousValue = oldSeqRecord._currentValue;
oldSeqRecord._currentValue = value;
if (!looseIdentical(value, oldSeqRecord.currentValue)) {
oldSeqRecord.previousValue = oldSeqRecord.currentValue;
oldSeqRecord.currentValue = value;
this._addToChanges(oldSeqRecord);
}
} else {
@ -124,7 +130,7 @@ export class KeyValueChanges extends Pipe {
} else {
newSeqRecord = new KVChangeRecord(key);
MapWrapper.set(records, key, newSeqRecord);
newSeqRecord._currentValue = value;
newSeqRecord.currentValue = value;
this._addToAdditions(newSeqRecord);
}
}
@ -158,11 +164,11 @@ export class KeyValueChanges extends Pipe {
}
for (record = this._changesHead; record !== null; record = record._nextChanged) {
record._previousValue = record._currentValue;
record.previousValue = record.currentValue;
}
for (record = this._additionsHead; record != null; record = record._nextAdded) {
record._previousValue = record._currentValue;
record.previousValue = record.currentValue;
}
// todo(vicb) once assert is supported
@ -215,8 +221,8 @@ export class KeyValueChanges extends Pipe {
}
for (var rec:KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
rec._previousValue = rec._currentValue;
rec._currentValue = null;
rec.previousValue = rec.currentValue;
rec.currentValue = null;
MapWrapper.delete(this._records, rec.key);
}
}
@ -349,10 +355,13 @@ export class KeyValueChanges extends Pipe {
/**
* @exportedAs angular2/pipes
*/
export class KVChangeRecord {
key;
_previousValue;
_currentValue;
previousValue;
currentValue;
_nextPrevious:KVChangeRecord;
_next:KVChangeRecord;
@ -363,8 +372,8 @@ export class KVChangeRecord {
constructor(key) {
this.key = key;
this._previousValue = null;
this._currentValue = null;
this.previousValue = null;
this.currentValue = null;
this._nextPrevious = null;
this._next = null;
@ -375,9 +384,9 @@ export class KVChangeRecord {
}
toString():string {
return looseIdentical(this._previousValue, this._currentValue) ?
return looseIdentical(this.previousValue, this.currentValue) ?
stringify(this.key) :
(stringify(this.key) + '[' + stringify(this._previousValue) + '->' +
stringify(this._currentValue) + ']');
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
stringify(this.currentValue) + ']');
}
}

View File

@ -1,16 +1,22 @@
import {isBlank} from 'angular2/src/facade/lang';
import {Pipe, NO_CHANGE} from './pipe';
/**
* @exportedAs angular2/pipes
*/
export class NullPipeFactory {
supports(obj):boolean {
return NullPipe.supportsObj(obj);
}
create():Pipe {
create(bpc):Pipe {
return new NullPipe();
}
}
/**
* @exportedAs angular2/pipes
*/
export class NullPipe extends Pipe {
called:boolean;
constructor() {

View File

@ -1,7 +1,37 @@
/**
* Indicates that the result of a [Pipe] transformation has not changed since the last time the pipe was called.
*
* Suppose we have a pipe that computes changes in an array by performing a simple diff operation. If
* we call this pipe with the same array twice, it will return `NO_CHANGE` the second time.
*
* @exportedAs angular2/pipes
*/
export var NO_CHANGE = new Object();
/**
* An interface for extending the list of pipes known to Angular.
*
* If you are writing a custom [Pipe], you must extend this interface.
*
* #Example
*
* ```
* class DoublePipe extends Pipe {
* supports(obj) {
* return true;
* }
*
* transform(value) {
* return `${value}${value}`;
* }
* }
* ```
*
* @exportedAs angular2/pipes
*/
export class Pipe {
supports(obj):boolean {return false;}
onDestroy() {}
transform(value:any):any {return null;}
}
}

View File

@ -1,6 +1,7 @@
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
import {Pipe} from './pipe';
import {BindingPropagationConfig} from '../binding_propagation_config';
export class PipeRegistry {
config;
@ -9,7 +10,7 @@ export class PipeRegistry {
this.config = config;
}
get(type:string, obj):Pipe {
get(type:string, obj, bpc:BindingPropagationConfig):Pipe {
var listOfConfigs = this.config[type];
if (isBlank(listOfConfigs)) {
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
@ -22,6 +23,6 @@ export class PipeRegistry {
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
}
return matchingConfig.create();
return matchingConfig.create(bpc);
}
}

View File

@ -22,11 +22,12 @@ import {
PrefixNot
} from './parser/ast';
import {ChangeRecord, ChangeDispatcher, ChangeDetector} from './interfaces';
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record';
import {coalesce} from './coalesce';
@ -41,47 +42,32 @@ import {
RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
export class ProtoChangeDetector {
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{
return null;
}
}
export class BindingRecord {
ast:AST;
bindingMemento:any;
directiveMemento:any;
constructor(ast:AST, bindingMemento:any, directiveMemento:any) {
this.ast = ast;
this.bindingMemento = bindingMemento;
this.directiveMemento = directiveMemento;
}
}
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry:PipeRegistry) {
constructor(pipeRegistry:PipeRegistry, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createRecordsIfNecessary(bindingRecords, variableBindings);
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
this._pipeRegistry, this._records, directiveRecords);
}
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
if (isBlank(this._records)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (r) => {
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(b, variableBindings);
});
this._records = coalesce(recordBuilder.records);
}
@ -92,28 +78,31 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
export class JitProtoChangeDetector extends ProtoChangeDetector {
_factory:Function;
_pipeRegistry;
_changeControlStrategy:string;
constructor(pipeRegistry) {
constructor(pipeRegistry, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._factory = null;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
this._createFactoryIfNecessary(bindingRecords, variableBindings);
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveRecords);
return this._factory(dispatcher, this._pipeRegistry);
}
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveRecords:List) {
if (isBlank(this._factory)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (r) => {
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings);
ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(b, variableBindings);
});
var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
directiveRecords).generate();
}
}
}
@ -125,13 +114,13 @@ class ProtoRecordBuilder {
this.records = [];
}
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, variableBindings:List = null) {
addAst(b:BindingRecord, variableBindings:List = null) {
var last = ListWrapper.last(this.records);
if (isPresent(last) && last.directiveMemento == directiveMemento) {
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
last.lastInDirective = false;
}
var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length, variableBindings);
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
if (! ListWrapper.isEmpty(pr)) {
var last = ListWrapper.last(pr);
last.lastInBinding = true;
@ -144,24 +133,22 @@ class ProtoRecordBuilder {
class _ConvertAstIntoProtoRecords {
protoRecords:List;
bindingMemento:any;
directiveMemento:any;
bindingRecord:BindingRecord;
variableBindings:List;
contextIndex:number;
expressionAsString:string;
constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string, variableBindings:List) {
constructor(bindingRecord:BindingRecord, contextIndex:number, expressionAsString:string, variableBindings:List) {
this.protoRecords = [];
this.bindingMemento = bindingMemento;
this.directiveMemento = directiveMemento;
this.bindingRecord = bindingRecord;
this.contextIndex = contextIndex;
this.expressionAsString = expressionAsString;
this.variableBindings = variableBindings;
}
static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number, variableBindings:List) {
var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString(), variableBindings);
ast.visit(c);
static convert(b:BindingRecord, contextIndex:number, variableBindings:List) {
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
b.ast.visit(c);
return c.protoRecords;
}
@ -239,7 +226,8 @@ class _ConvertAstIntoProtoRecords {
visitPipe(ast:Pipe) {
var value = ast.exp.visit(this);
return this._addRecord(RECORD_TYPE_PIPE, ast.name, ast.name, [], null, value);
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
return this._addRecord(type, ast.name, ast.name, [], null, value);
}
visitKeyedAccess(ast:KeyedAccess) {
@ -261,7 +249,7 @@ class _ConvertAstIntoProtoRecords {
var selfIndex = ++ this.contextIndex;
ListWrapper.push(this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
this.bindingMemento, this.directiveMemento, this.expressionAsString, false, false));
this.bindingRecord, this.expressionAsString, false, false));
return selfIndex;
}
}

View File

@ -1,4 +1,5 @@
import {List} from 'angular2/src/facade/collection';
import {BindingRecord} from './binding_record';
export const RECORD_TYPE_SELF = 0;
export const RECORD_TYPE_CONST = 1;
@ -9,7 +10,8 @@ export const RECORD_TYPE_INVOKE_METHOD = 5;
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
export const RECORD_TYPE_KEYED_ACCESS = 7;
export const RECORD_TYPE_PIPE = 8;
export const RECORD_TYPE_INTERPOLATE = 9;
export const RECORD_TYPE_BINDING_PIPE = 9;
export const RECORD_TYPE_INTERPOLATE = 10;
export class ProtoRecord {
mode:number;
@ -19,8 +21,7 @@ export class ProtoRecord {
fixedArgs:List;
contextIndex:number;
selfIndex:number;
bindingMemento:any;
directiveMemento:any;
bindingRecord:BindingRecord;
lastInBinding:boolean;
lastInDirective:boolean;
expressionAsString:string;
@ -32,8 +33,7 @@ export class ProtoRecord {
fixedArgs:List,
contextIndex:number,
selfIndex:number,
bindingMemento:any,
directiveMemento:any,
bindingRecord:BindingRecord,
expressionAsString:string,
lastInBinding:boolean,
lastInDirective:boolean) {
@ -45,8 +45,7 @@ export class ProtoRecord {
this.fixedArgs = fixedArgs;
this.contextIndex = contextIndex;
this.selfIndex = selfIndex;
this.bindingMemento = bindingMemento;
this.directiveMemento = directiveMemento;
this.bindingRecord = bindingRecord;
this.lastInBinding = lastInBinding;
this.lastInDirective = lastInDirective;
this.expressionAsString = expressionAsString;

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,10 @@ export class EventEmitter extends DependencyAnnotation {
super();
this.eventName = eventName;
}
get token() {
return Function;
}
}
/**
@ -25,4 +29,40 @@ export class PropertySetter extends DependencyAnnotation {
super();
this.propName = propName;
}
get token() {
return Function;
}
}
/**
* The directive can inject the value of an attribute of the host element
*/
export class Attribute extends DependencyAnnotation {
attributeName: string;
@CONST()
constructor(attributeName) {
super();
this.attributeName = attributeName;
}
get token() {
//Normally one would default a token to a type of an injected value but here
//the type of a variable is "string" and we can't use primitive type as a return value
//so we use instance of Attribute instead. This doesn't matter much in practice as arguments
//with @Attribute annotation are injected by ElementInjector that doesn't take tokens into account.
return this;
}
}
/**
* The directive can inject an query that would reflect a list of ancestor directives
*/
export class Query extends DependencyAnnotation {
directive;
@CONST()
constructor(directive) {
super();
this.directive = directive;
}
}

View File

@ -1,41 +0,0 @@
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
/**
* @publicModule angular2/angular2
*/
export class Template {
url:any; //string;
inline:any; //string;
directives:any; //List<Type>;
formatters:any; //List<Type>;
source:any;//List<Template>;
locale:any; //string
device:any; //string
@CONST()
constructor({
url,
inline,
directives,
formatters,
source,
locale,
device
}: {
url: string,
inline: string,
directives: List<Type>,
formatters: List<Type>,
source: List<Template>,
locale: string,
device: string
})
{
this.url = url;
this.inline = inline;
this.directives = directives;
this.formatters = formatters;
this.source = source;
this.locale = locale;
this.device = device;
}
}

View File

@ -0,0 +1,70 @@
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
/**
* Declare the available HTML templates for an application.
*
* Each angular component requires a single `@Component` and at least one `@View` annotation. The @View
* annotation specifies the HTML template to use, and lists the directives that are active within the template.
*
* When a component is instantiated, the template is loaded into the component's shadow root, and the
* expressions and statements in the template are evaluated against the component.
*
* For details on the `@Component` annotation, see [Component].
*
* ## Example
*
* ```
* @Component({
* selector: 'greet'
* })
* @View({
* template: 'Hello {{name}}!',
* directives: [GreetUser, Bold]
* })
* class Greet {
* name: string;
*
* constructor() {
* this.name = 'World';
* }
* }
* ```
*
* @exportedAs angular2/annotations
*/
export class View {
templateUrl:any; //string;
template:any; //string;
directives:any; //List<Type>;
formatters:any; //List<Type>;
source:any;//List<View>;
locale:any; //string
device:any; //string
@CONST()
constructor({
templateUrl,
template,
directives,
formatters,
source,
locale,
device
}: {
templateUrl: string,
template: string,
directives: List<Type>,
formatters: List<Type>,
source: List<View>,
locale: string,
device: string
})
{
this.templateUrl = templateUrl;
this.template = template;
this.directives = directives;
this.formatters = formatters;
this.source = source;
this.locale = locale;
this.device = device;
}
}

View File

@ -2,10 +2,45 @@ import {CONST} from 'angular2/src/facade/lang';
import {DependencyAnnotation} from 'angular2/di';
/**
* The directive can only be injected from the current element
* or from its parent.
* Specifies that an injector should retrieve a dependency from the direct parent.
*
* @publicModule angular2/annotations
* ## Example
*
* Here is a simple directive that retrieves a dependency from its parent element.
*
* ```
* @Decorator({
* selector: '[dependency]',
* properties: {
* 'id':'dependency'
* }
* })
* class Dependency {
* id:string;
* }
*
*
* @Decorator({
* selector: '[my-directive]'
* })
* class Dependency {
* constructor(@Parent() dependency:Dependency) {
* expect(dependency.id).toEqual(1);
* };
* }
* ```
*
* We use this with the following HTML template:
*
* ```
* <div dependency="1">
* <div dependency="2" my-directive></div>
* </div>
* ```
* The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from the
* parent element (even thought the current element could resolve it): Angular injects `dependency=1`.
*
* @exportedAs angular2/annotations
*/
export class Parent extends DependencyAnnotation {
@CONST()
@ -15,10 +50,58 @@ export class Parent extends DependencyAnnotation {
}
/**
* The directive can only be injected from the current element
* or from its ancestor.
* Specifies that an injector should retrieve a dependency from any ancestor element.
*
* @publicModule angular2/annotations
* An ancestor is any element between the parent element and shadow root.
*
*
* ## Example
*
* Here is a simple directive that retrieves a dependency from an ancestor element.
*
* ```
* @Decorator({
* selector: '[dependency]',
* properties: {
* 'id':'dependency'
* }
* })
* class Dependency {
* id:string;
* }
*
*
* @Decorator({
* selector: '[my-directive]'
* })
* class Dependency {
* constructor(@Ancestor() dependency:Dependency) {
* expect(dependency.id).toEqual(2);
* };
* }
* ```
*
* We use this with the following HTML template:
*
* ```
* <div dependency="1">
* <div dependency="2">
* <div>
* <div dependency="3" my-directive></div>
* </div>
* </div>
* </div>
* ```
*
* The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency from the
* nearest ancestor element:
* - The current element `dependency="3"` is skipped because it is not an ancestor.
* - Next parent has no directives `<div>`
* - Next parent has the `Dependency` directive and so the dependency is satisfied.
*
* Angular injects `dependency=2`.
*
* @exportedAs angular2/annotations
*/
export class Ancestor extends DependencyAnnotation {
@CONST()

View File

@ -3,43 +3,54 @@ import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print, strin
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Compiler, CompilerCache} from './compiler/compiler';
import {ProtoView} from './compiler/view';
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection, jitChangeDetection} from 'angular2/change_detection';
import {ExceptionHandler} from './exception_handler';
import {TemplateLoader} from './compiler/template_loader';
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
import {TemplateResolver} from './compiler/template_resolver';
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
import {HammerGesturesPlugin} from 'angular2/src/core/events/hammer_gestures';
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
import {XHR} from 'angular2/src/services/xhr';
import {XHRImpl} from 'angular2/src/services/xhr_impl';
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
import {Binding} from 'angular2/src/di/binding';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
import {StyleInliner} from 'angular2/src/core/compiler/style_inliner';
import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
import {Component} from 'angular2/src/core/annotations/annotations';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {Renderer} from 'angular2/src/render/api';
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
import * as rc from 'angular2/src/render/dom/compiler/compiler';
import {ComponentRef} from 'angular2/src/core/compiler/element_injector';
import * as rvf from 'angular2/src/render/dom/view/view_factory';
import {
appComponentRefToken,
appChangeDetectorToken,
appElementToken,
appComponentAnnotatedTypeToken,
appDocumentToken,
} from './application_tokens';
var _rootInjector: Injector;
// Contains everything that is safe to share between applications.
var _rootBindings = [
bind(Reflector).toValue(reflector)
bind(Reflector).toValue(reflector),
TestabilityRegistry
];
export var appViewToken = new OpaqueToken('AppView');
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
export var appElementToken = new OpaqueToken('AppElement');
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
export var appDocumentToken = new OpaqueToken('AppDocument');
function _injectorBindings(appComponentType): List<Binding> {
return [
bind(appDocumentToken).toValue(DOM.defaultDoc()),
@ -56,42 +67,45 @@ function _injectorBindings(appComponentType): List<Binding> {
}
return element;
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector, appElement,
appComponentAnnotatedType, testability, registry) => {
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
appComponentAnnotatedType, strategy, eventManager) => {
var annotation = appComponentAnnotatedType.annotation;
if(!isBlank(annotation) && !(annotation instanceof Component)) {
var type = appComponentAnnotatedType.type;
throw new BaseException(`Only Components can be bootstrapped; ` +
`Directive of ${stringify(type)} is not a Component`);
}
return compiler.compile(appComponentAnnotatedType.type).then(
(protoView) => {
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
appComponentAnnotatedType, changeDetection.createProtoChangeDetector('root'),
strategy);
// The light Dom of the app element is not considered part of
// the angular application. Thus the context and lightDomInjector are
// empty.
var view = appProtoView.instantiate(null, eventManager);
view.hydrate(injector, null, null, new Object(), null);
return view;
});
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
ShadowDomStrategy, EventManager]),
// We need to do this here to ensure that we create Testability and
// it's ready on the window for users.
registry.registerApplication(appElement, testability);
return dynamicComponentLoader.loadIntoNewLocation(appElement, appComponentAnnotatedType.type, null, injector);
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
Testability, TestabilityRegistry]),
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
[appViewToken]),
bind(appComponentType).toFactory((rootView) => rootView.elementInjectors[0].getComponent(),
[appViewToken]),
bind(appChangeDetectorToken).toFactory((ref) => ref.hostView.changeDetector,
[appComponentRefToken]),
bind(appComponentType).toFactory((ref) => ref.instance,
[appComponentRefToken]),
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),[ExceptionHandler]),
bind(EventManager).toFactory((zone) => {
var plugins = [new HammerGesturesPlugin(), new DomEventsPlugin()];
var plugins = [new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
return new EventManager(plugins, zone);
}, [VmTurnZone]),
bind(ShadowDomStrategy).toFactory(
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
[StyleUrlResolver, appDocumentToken]),
bind(Renderer).toClass(DirectDomRenderer),
bind(rc.Compiler).toClass(rc.DefaultCompiler),
// TODO(tbosch): We need an explicit factory here, as
// we are getting errors in dart2js with mirrors...
bind(rvf.ViewFactory).toFactory(
(capacity, eventManager, shadowDomStrategy) => new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy),
[rvf.VIEW_POOL_CAPACITY, EventManager, ShadowDomStrategy]
),
bind(rvf.VIEW_POOL_CAPACITY).toValue(10000),
ProtoViewFactory,
// TODO(tbosch): We need an explicit factory here, as
// we are getting errors in dart2js with mirrors...
bind(ViewFactory).toFactory(
(capacity) => new ViewFactory(capacity),
[VIEW_POOL_CAPACITY]
),
bind(VIEW_POOL_CAPACITY).toValue(10000),
Compiler,
CompilerCache,
TemplateResolver,
@ -106,7 +120,8 @@ function _injectorBindings(appComponentType): List<Binding> {
UrlResolver,
StyleUrlResolver,
StyleInliner,
bind(CssProcessor).toFactory(() => new CssProcessor(null), []),
DynamicComponentLoader,
Testability
];
}
@ -146,8 +161,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
* An application is bootstrapped inside an existing browser DOM, typically `index.html`. Unlike Angular 1, Angular 2
* does not compile/process bindings in `index.html`. This is mainly for security reasons, as well as architectural
* changes in Angular 2. This means that `index.html` can safely be processed using server-side technologies such as
* bindings. (which may use double-curly `{{ syntax }}` without collision from Angular 2 component double-curly
* `{{ syntax }}`.)
* bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from Angular 2 component double-curly
* `{{ syntax }}`.
*
* We can use this script code:
*
@ -155,8 +170,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
* @Component({
* selector: 'my-app'
* })
* @Template({
* inline: 'Hello {{ name }}!'
* @View({
* template: 'Hello {{ name }}!'
* })
* class MyApp {
* name:string;
@ -177,8 +192,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
* 1. It uses the component's `selector` property to locate the DOM element which needs to be upgraded into
* the angular component.
* 2. It creates a new child injector (from the primordial injector) and configures the injector with the component's
* `services`. Optionally, you can also override the injector configuration for an app by invoking
* `bootstrap` with the `componentServiceBindings` argument.
* `injectables`. Optionally, you can also override the injector configuration for an app by invoking
* `bootstrap` with the `componentInjectableBindings` argument.
* 3. It creates a new [Zone] and connects it to the angular application's change detection domain instance.
* 4. It creates a shadow DOM on the selected component's host element and loads the template into it.
* 5. It instantiates the specified component.
@ -202,8 +217,8 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
*
* If you need to bootstrap multiple applications that share common data, the applications must share a common
* change detection and zone. To do that, create a meta-component that lists the application components in its template.
* By only invoking the `bootstrap()` method once, with the meta-component as its argument, you ensure that only a single
* change detection zone is created and therefore data can be shared across the applications.
* By only invoking the `bootstrap()` method once, with the meta-component as its argument, you ensure that only a
* single change detection zone is created and therefore data can be shared across the applications.
*
*
* ## Primordial Injector
@ -220,17 +235,17 @@ function _createVmZone(givenReporter:Function): VmTurnZone {
* # API
* - [appComponentType]: The root component which should act as the application. This is a reference to a [Type]
* which is annotated with `@Component(...)`.
* - [componentServiceBindings]: An additional set of bindings that can be added to the [Component.services] to
* - [componentInjectableBindings]: An additional set of bindings that can be added to the [Component.injectables] to
* override default injection behavior.
* - [errorReporter]: `function(exception:any, stackTrace:string)` a default error reporter for unhandled exceptions.
*
* Returns a [Promise] with the application`s private [Injector].
*
* @publicModule angular2/angular2
* @exportedAs angular2/core
*/
export function bootstrap(appComponentType: Type,
componentServiceBindings: List<Binding>=null,
errorReporter: Function=null): Promise<Injector> {
componentInjectableBindings: List<Binding> = null,
errorReporter: Function = null): Promise<ComponentRef> {
BrowserDomAdapter.makeCurrent();
var bootstrapProcess = PromiseWrapper.completer();
@ -239,16 +254,17 @@ export function bootstrap(appComponentType: Type,
// TODO(rado): prepopulate template cache, so applications with only
// index.html and main.js are possible.
var appInjector = _createAppInjector(appComponentType, componentServiceBindings, zone);
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
PromiseWrapper.then(appInjector.asyncGet(appViewToken),
(rootView) => {
PromiseWrapper.then(appInjector.asyncGet(appComponentRefToken),
(componentRef) => {
var appChangeDetector = componentRef.hostView.changeDetector;
// retrieve life cycle: may have already been created if injected in root component
var lc=appInjector.get(LifeCycle);
lc.registerWith(zone, rootView.changeDetector);
var lc = appInjector.get(LifeCycle);
lc.registerWith(zone, appChangeDetector);
lc.tick(); //the first tick that will bootstrap the app
bootstrapProcess.resolve(appInjector);
bootstrapProcess.resolve(componentRef);
},
(err) => {
@ -260,10 +276,10 @@ export function bootstrap(appComponentType: Type,
}
function _createAppInjector(appComponentType: Type, bindings: List<Binding>, zone: VmTurnZone): Injector {
if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings);
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
var mergedBindings = isPresent(bindings) ?
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
_injectorBindings(appComponentType);
ListWrapper.push(mergedBindings, bind(VmTurnZone).toValue(zone));
return _rootInjector.createChild(mergedBindings);
return _rootInjector.resolveAndCreateChild(mergedBindings);
}

View File

@ -0,0 +1,7 @@
import {OpaqueToken} from 'angular2/di';
export var appComponentRefToken = new OpaqueToken('ComponentRef');
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
export var appElementToken = new OpaqueToken('AppElement');
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
export var appDocumentToken = new OpaqueToken('AppDocument');

View File

@ -3,26 +3,21 @@ import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
import {ChangeDetection, Parser} from 'angular2/change_detection';
import {DirectiveMetadataReader} from './directive_metadata_reader';
import {ProtoView} from './view';
import {CompilePipeline} from './pipeline/compile_pipeline';
import {CompileElement} from './pipeline/compile_element';
import {createDefaultSteps} from './pipeline/default_steps';
import {TemplateLoader} from './template_loader';
import {Component, Viewport, DynamicComponent, Decorator} from '../annotations/annotations';
import {AppProtoView} from './view';
import {DirectiveBinding} from './element_injector';
import {TemplateResolver} from './template_resolver';
import {Template} from '../annotations/template';
import {ShadowDomStrategy} from './shadow_dom_strategy';
import {CompileStep} from './pipeline/compile_step';
import {View} from '../annotations/view';
import {ComponentUrlMapper} from './component_url_mapper';
import {UrlResolver} from './url_resolver';
import {CssProcessor} from './css_processor';
import {ProtoViewFactory} from './proto_view_factory';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import * as renderApi from 'angular2/src/render/api';
/**
* Cache that stores the ProtoView of the template of a component.
* Cache that stores the AppProtoView of the template of a component.
* Used to prevent duplicate work and resolve cyclic dependencies.
* @publicModule angular2/angular2
*/
@Injectable()
export class CompilerCache {
@ -31,11 +26,11 @@ export class CompilerCache {
this._cache = MapWrapper.create();
}
set(component:Type, protoView:ProtoView) {
set(component:Type, protoView:AppProtoView) {
MapWrapper.set(this._cache, component, protoView);
}
get(component:Type):ProtoView {
get(component:Type):AppProtoView {
var result = MapWrapper.get(this._cache, component);
return normalizeBlank(result);
}
@ -45,74 +40,66 @@ export class CompilerCache {
}
}
/**
* The compiler loads and translates the html templates of components into
* nested ProtoViews. To decompose its functionality it uses
* the CompilePipeline and the CompileSteps.
* @publicModule angular2/angular2
*/
@Injectable()
export class Compiler {
_reader: DirectiveMetadataReader;
_parser:Parser;
_compilerCache:CompilerCache;
_changeDetection:ChangeDetection;
_templateLoader:TemplateLoader;
_compiling:Map<Type, Promise>;
_shadowDomStrategy: ShadowDomStrategy;
_templateResolver: TemplateResolver;
_componentUrlMapper: ComponentUrlMapper;
_urlResolver: UrlResolver;
_appUrl: string;
_cssProcessor: CssProcessor;
_renderer: renderApi.Renderer;
_protoViewFactory:ProtoViewFactory;
constructor(changeDetection:ChangeDetection,
templateLoader:TemplateLoader,
reader: DirectiveMetadataReader,
parser:Parser,
constructor(reader: DirectiveMetadataReader,
cache:CompilerCache,
shadowDomStrategy: ShadowDomStrategy,
templateResolver: TemplateResolver,
componentUrlMapper: ComponentUrlMapper,
urlResolver: UrlResolver,
cssProcessor: CssProcessor) {
this._changeDetection = changeDetection;
renderer: renderApi.Renderer,
protoViewFactory: ProtoViewFactory) {
this._reader = reader;
this._parser = parser;
this._compilerCache = cache;
this._templateLoader = templateLoader;
this._compiling = MapWrapper.create();
this._shadowDomStrategy = shadowDomStrategy;
this._templateResolver = templateResolver;
this._componentUrlMapper = componentUrlMapper;
this._urlResolver = urlResolver;
this._appUrl = urlResolver.resolve(null, './');
this._cssProcessor = cssProcessor;
this._renderer = renderer;
this._protoViewFactory = protoViewFactory;
}
createSteps(component:Type, template: Template):List<CompileStep> {
var dirMetadata = ListWrapper.map(this._flattenDirectives(template),
(d) => this._reader.read(d));
var cmpMetadata = this._reader.read(component);
var templateUrl = this._templateLoader.getTemplateUrl(template);
return createDefaultSteps(this._changeDetection, this._parser, cmpMetadata, dirMetadata,
this._shadowDomStrategy, templateUrl, this._cssProcessor);
_bindDirective(directiveTypeOrBinding) {
if (directiveTypeOrBinding instanceof DirectiveBinding) {
return directiveTypeOrBinding;
}
var meta = this._reader.read(directiveTypeOrBinding);
return DirectiveBinding.createFromType(meta.type, meta.annotation);
}
compile(component: Type):Promise<ProtoView> {
var protoView = this._compile(component);
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>.
// Used for bootstrapping.
compileRoot(elementOrSelector, componentTypeOrBinding:any):Promise<AppProtoView> {
return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => {
return this._compileNestedProtoViews(null, rootRenderPv, [this._bindDirective(componentTypeOrBinding)], true);
});
}
compile(component: Type):Promise<AppProtoView> {
var protoView = this._compile(this._bindDirective(component));
return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
}
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
_compile(component: Type) {
// TODO(vicb): union type return AppProtoView or Promise<AppProtoView>
_compile(componentBinding: DirectiveBinding) {
var component = componentBinding.key.token;
var protoView = this._compilerCache.get(component);
if (isPresent(protoView)) {
// The component has already been compiled into a ProtoView,
// returns a resolved Promise.
// The component has already been compiled into an AppProtoView,
// returns a plain AppProtoView, not wrapped inside of a Promise.
// Needed for recursive components.
return protoView;
}
@ -125,84 +112,131 @@ export class Compiler {
}
var template = this._templateResolver.resolve(component);
var directives = ListWrapper.map(
this._flattenDirectives(template),
(directive) => this._bindDirective(directive)
);
var renderTemplate = this._buildRenderTemplate(component, template, directives);
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
});
var componentUrl = this._componentUrlMapper.getUrl(component);
var baseUrl = this._urlResolver.resolve(this._appUrl, componentUrl);
this._templateLoader.setBaseUrl(template, baseUrl);
var tplElement = this._templateLoader.load(template);
if (PromiseWrapper.isPromise(tplElement)) {
pvPromise = PromiseWrapper.then(tplElement,
(el) => this._compileTemplate(template, el, component),
(_) => { throw new BaseException(`Failed to load the template for ${stringify(component)}`); }
);
MapWrapper.set(this._compiling, component, pvPromise);
return pvPromise;
}
return this._compileTemplate(template, tplElement, component);
MapWrapper.set(this._compiling, component, pvPromise);
return pvPromise;
}
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
_compileTemplate(template: Template, tplElement, component: Type) {
var pipeline = new CompilePipeline(this.createSteps(component, template));
var compileElements;
try {
compileElements = pipeline.process(tplElement, stringify(component));
} catch(ex) {
return PromiseWrapper.reject(ex);
}
var protoView = compileElements[0].inheritedProtoView;
// Populate the cache before compiling the nested components,
// so that components can reference themselves in their template.
this._compilerCache.set(component, protoView);
MapWrapper.delete(this._compiling, component);
// Compile all the components from the template
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
_compileNestedProtoViews(componentBinding, renderPv, directives, isComponentRootView) {
var nestedPVPromises = [];
for (var i = 0; i < compileElements.length; i++) {
var ce = compileElements[i];
if (ce.hasNestedView) {
this._compileNestedProtoView(ce, nestedPVPromises);
var protoView = this._protoViewFactory.createProtoView(componentBinding, renderPv, directives);
if (isComponentRootView && isPresent(componentBinding)) {
// Populate the cache before compiling the nested components,
// so that components can reference themselves in their template.
var component = componentBinding.key.token;
this._compilerCache.set(component, protoView);
MapWrapper.delete(this._compiling, component);
}
var binderIndex = 0;
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
var nestedComponent = elementBinder.componentDirective;
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
var elementBinderDone = (nestedPv) => {
elementBinder.nestedProtoView = nestedPv;
// Can't set the parentProtoView for components,
// as their AppProtoView might be used in multiple other components.
nestedPv.parentProtoView = isPresent(nestedComponent) ? null : protoView;
};
var nestedCall = null;
if (isPresent(nestedComponent)) {
if (!(nestedComponent.annotation instanceof DynamicComponent)) {
nestedCall = this._compile(nestedComponent);
}
} else if (isPresent(nestedRenderProtoView)) {
nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false);
}
}
if (protoView.stylePromises.length > 0) {
// The protoView is ready after all asynchronous styles are ready
var syncProtoView = protoView;
protoView = PromiseWrapper.all(syncProtoView.stylePromises).then((_) => syncProtoView);
}
if (PromiseWrapper.isPromise(nestedCall)) {
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
} else if (isPresent(nestedCall)) {
elementBinderDone(nestedCall);
}
binderIndex++;
});
var protoViewDone = (_) => {
var childComponentRenderPvRefs = [];
ListWrapper.forEach(protoView.elementBinders, (eb) => {
if (isPresent(eb.componentDirective)) {
var componentPv = eb.nestedProtoView;
ListWrapper.push(childComponentRenderPvRefs, isPresent(componentPv) ? componentPv.render : null);
}
});
this._renderer.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs);
return protoView;
};
if (nestedPVPromises.length > 0) {
// Returns ProtoView Promise when there are any asynchronous nested ProtoViews.
// The promise will resolved after nested ProtoViews are compiled.
return PromiseWrapper.then(PromiseWrapper.all(nestedPVPromises),
(_) => protoView,
(e) => { throw new BaseException(`${e.message} -> Failed to compile ${stringify(component)}`); }
);
}
return protoView;
}
_compileNestedProtoView(ce: CompileElement, promises: List<Promise>) {
var protoView = this._compile(ce.componentDirective.type);
if (PromiseWrapper.isPromise(protoView)) {
ListWrapper.push(
promises,
protoView.then(function(pv) { ce.inheritedElementBinder.nestedProtoView = pv;})
);
return PromiseWrapper.all(nestedPVPromises).then(protoViewDone);
} else {
ce.inheritedElementBinder.nestedProtoView = protoView;
return protoViewDone(null);
}
}
_flattenDirectives(template: Template):List<Type> {
_buildRenderTemplate(component, view, directives) {
var componentUrl = this._urlResolver.resolve(
this._appUrl, this._componentUrlMapper.getUrl(component)
);
var templateAbsUrl = null;
if (isPresent(view.templateUrl)) {
templateAbsUrl = this._urlResolver.resolve(componentUrl, view.templateUrl);
} else {
// Note: If we have an inline template, we also need to send
// the url for the component to the renderer so that it
// is able to resolve urls in stylesheets.
templateAbsUrl = componentUrl;
}
return new renderApi.ViewDefinition({
componentId: stringify(component),
absUrl: templateAbsUrl,
template: view.template,
directives: ListWrapper.map(directives, this._buildRenderDirective)
});
}
_buildRenderDirective(directiveBinding) {
var ann = directiveBinding.annotation;
var renderType;
var compileChildren = true;
if ((ann instanceof Component) || (ann instanceof DynamicComponent)) {
renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE;
} else if (ann instanceof Viewport) {
renderType = renderApi.DirectiveMetadata.VIEWPORT_TYPE;
} else if (ann instanceof Decorator) {
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
compileChildren = ann.compileChildren;
}
var setters = [];
var readAttributes = [];
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
if (isPresent(dep.propSetterName)) {
ListWrapper.push(setters, dep.propSetterName);
}
if (isPresent(dep.attributeName)) {
ListWrapper.push(readAttributes, dep.attributeName);
}
});
return new renderApi.DirectiveMetadata({
id: stringify(directiveBinding.key.token),
type: renderType,
selector: ann.selector,
compileChildren: compileChildren,
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
setters: setters,
readAttributes: readAttributes
});
}
_flattenDirectives(template: View):List<Type> {
if (isBlank(template.directives)) return [];
var directives = [];
@ -223,5 +257,3 @@ export class Compiler {
}
}

View File

@ -1,71 +0,0 @@
import {Injectable} from 'angular2/di';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {isPresent} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import {CompileStep} from './pipeline/compile_step';
import {CompileElement} from './pipeline/compile_element';
import {CompileControl} from './pipeline/compile_control';
import {ShadowDomStrategy} from './shadow_dom_strategy';
import {DirectiveMetadata} from './directive_metadata';
/**
* Processes the <style> tags during the compilation:
* - Apply any given transformers,
* - Apply the shadow DOM strategy style step.
*/
@Injectable()
export class CssProcessor {
_transformers: List<CssTransformer>;
constructor(transformers: List<CssTransformer>) {
this._transformers = transformers;
}
/**
* Returns a compile step to be added to the compiler pipeline.
*
* @param {DirectiveMetadata} cmpMetadata
* @param {ShadowDomStrategy} shadowDomStrategy
* @param {string} templateUrl The base URL of the template
*/
getCompileStep(cmpMetadata: DirectiveMetadata, shadowDomStrategy: ShadowDomStrategy,
templateUrl: string) {
var strategyStep = shadowDomStrategy.getStyleCompileStep(cmpMetadata, templateUrl);
return new _CssProcessorStep(strategyStep, this._transformers);
}
}
export class CssTransformer {
transform(styleElement) {};
}
class _CssProcessorStep extends CompileStep {
_strategyStep: CompileStep;
_transformers: List<CssTransformer>;
constructor(strategyStep: CompileStep, transformers: List<CssTransformer>) {
super();
this._strategyStep = strategyStep;
this._transformers = transformers;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (DOM.tagName(current.element) == 'STYLE') {
current.ignoreBindings = true;
if (isPresent(this._transformers)) {
var styleEl = current.element;
for (var i = 0; i < this._transformers.length; i++) {
this._transformers[i].transform(styleEl);
}
}
if (isPresent(this._strategyStep)) {
this._strategyStep.process(parent, current, control);
}
}
}
}

View File

@ -1,5 +1,7 @@
import {Type} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import {Directive} from 'angular2/src/core/annotations/annotations'
import {ResolvedBinding} from 'angular2/di';
/**
* Combination of a type with the Directive annotation
@ -7,9 +9,11 @@ import {Directive} from 'angular2/src/core/annotations/annotations'
export class DirectiveMetadata {
type:Type;
annotation:Directive;
resolvedInjectables:List<ResolvedBinding>;
constructor(type:Type, annotation:Directive) {
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
this.annotation = annotation;
this.type = type;
this.resolvedInjectables = resolvedInjectables;
}
}

View File

@ -1,6 +1,6 @@
import {Injectable} from 'angular2/di';
import {Injectable, Injector} from 'angular2/di';
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
import {Directive} from '../annotations/annotations';
import {Directive, Component} from '../annotations/annotations';
import {DirectiveMetadata} from './directive_metadata';
import {reflector} from 'angular2/src/reflection/reflection';
@ -13,7 +13,11 @@ export class DirectiveMetadataReader {
var annotation = annotations[i];
if (annotation instanceof Directive) {
return new DirectiveMetadata(type, annotation);
var resolvedInjectables = null;
if (annotation instanceof Component && isPresent(annotation.injectables)) {
resolvedInjectables = Injector.resolve(annotation.injectables);
}
return new DirectiveMetadata(type, annotation, resolvedInjectables);
}
}
}

View File

@ -0,0 +1,100 @@
import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di'
import {Compiler} from './compiler';
import {DirectiveMetadataReader} from './directive_metadata_reader';
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import {Promise} from 'angular2/src/facade/async';
import {Component} from 'angular2/src/core/annotations/annotations';
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
import {Renderer} from 'angular2/src/render/api';
import {ElementRef, DirectiveRef, ComponentRef} from './element_injector';
/**
* Service for dynamically loading a Component into an arbitrary position in the internal Angular
* application tree.
*/
@Injectable()
export class DynamicComponentLoader {
_compiler:Compiler;
_viewFactory:ViewFactory;
_renderer:Renderer;
_directiveMetadataReader:DirectiveMetadataReader;
constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader,
renderer:Renderer, viewFactory:ViewFactory) {
this._compiler = compiler;
this._directiveMetadataReader = directiveMetadataReader;
this._renderer = renderer;
this._viewFactory = viewFactory
}
/**
* Loads a component into the location given by the provided ElementRef. The loaded component
* receives injection as if it in the place of the provided ElementRef.
*/
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
this._assertTypeIsComponent(type);
var directiveMetadata = this._directiveMetadataReader.read(type);
var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables);
var hostEi = location.elementInjector;
var hostView = location.hostView;
return this._compiler.compile(type).then(componentProtoView => {
var context = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj);
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context);
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
//same problem exists on the render side
hostView.addComponentChildView(componentView);
this._renderer.setDynamicComponentView(hostView.render, location.boundElementIndex, componentView.render);
// TODO(vsavkin): return a component ref that dehydrates the component view and removes it
// from the component child views
return new ComponentRef(Key.get(type), hostEi, componentView);
});
}
/**
* Loads a component as a child of the View given by the provided ElementRef. The loaded
* component receives injection normally as a hosted view.
*
* TODO(vsavkin, jelbourn): remove protoViewFactory after render layer exists.
*/
loadIntoNewLocation(elementOrSelector:any, type:Type, location:ElementRef,
injector:Injector = null):Promise<ComponentRef> {
this._assertTypeIsComponent(type);
var inj = this._componentAppInjector(location, injector, null);
//TODO(tbosch) this should always be a selector
return this._compiler.compileRoot(elementOrSelector, type).then(pv => {
var hostView = this._instantiateAndHydrateView(pv, inj, null, new Object());
// TODO(vsavkin): return a component ref that dehydrates the host view
return new ComponentRef(Key.get(type), hostView.elementInjectors[0], hostView.componentChildViews[0]);
});
}
_componentAppInjector(location, injector:Injector, resolvedBindings:List<ResolvedBinding>) {
var inj = isPresent(injector) ? injector : location.injector;
return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj;
}
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {
var componentView = this._viewFactory.getView(protoView);
componentView.hydrate(injector, hostElementInjector, context, null);
return componentView;
}
/** Asserts that the type being dynamically instantiated is a Component. */
_assertTypeIsComponent(type:Type) {
var annotation = this._directiveMetadataReader.read(type).annotation;
if (!(annotation instanceof Component)) {
throw new BaseException(`Could not load '${stringify(type)}' because it is not a component.`);
}
}
}

View File

@ -1,25 +1,22 @@
import {int, isBlank, BaseException} from 'angular2/src/facade/lang';
import * as eiModule from './element_injector';
import {DirectiveMetadata} from './directive_metadata';
import {DirectiveBinding} from './element_injector';
import {List, StringMap} from 'angular2/src/facade/collection';
import * as viewModule from './view';
export class ElementBinder {
protoElementInjector:eiModule.ProtoElementInjector;
componentDirective:DirectiveMetadata;
viewportDirective:DirectiveMetadata;
textNodeIndices:List<int>;
hasElementPropertyBindings:boolean;
nestedProtoView: viewModule.ProtoView;
events:StringMap;
contentTagSelector:string;
componentDirective:DirectiveBinding;
viewportDirective:DirectiveBinding;
nestedProtoView: viewModule.AppProtoView;
hostListeners:StringMap;
parent:ElementBinder;
index:int;
distanceToParent:int;
constructor(
index:int, parent:ElementBinder, distanceToParent: int,
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveMetadata,
viewportDirective:DirectiveMetadata) {
index:int, parent:ElementBinder, distanceToParent: int,
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveBinding,
viewportDirective:DirectiveBinding) {
if (isBlank(index)) {
throw new BaseException('null index not allowed.');
}
@ -31,14 +28,8 @@ export class ElementBinder {
this.index = index;
this.distanceToParent = distanceToParent;
// updated later when events are bound
this.events = null;
// updated later when text nodes are bound
this.textNodeIndices = null;
// updated later when element properties are bound
this.hasElementPropertyBindings = false;
this.hostListeners = null;
// updated later, so we are able to resolve cycles
this.nestedProtoView = null;
// updated later in the compilation pipeline
this.contentTagSelector = null;
}
}

View File

@ -1,16 +1,15 @@
import {isPresent, isBlank, Type, int, BaseException} from 'angular2/src/facade/lang';
import {Math} from 'angular2/src/facade/math';
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
import * as viewModule from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {NgElement} from 'angular2/src/core/dom/element';
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config';
import * as pclModule from 'angular2/src/core/compiler/private_component_location';
import {reflector} from 'angular2/src/reflection/reflection';
import {NgElement} from 'angular2/src/core/compiler/ng_element';
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
import {BindingPropagationConfig} from 'angular2/change_detection';
import {QueryList} from './query_list';
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
@ -20,20 +19,62 @@ var _undefined = new Object();
var _staticKeys;
export class ElementRef {
elementInjector:ElementInjector;
constructor(elementInjector:ElementInjector){
this.elementInjector = elementInjector;
}
get hostView() {
return this.elementInjector._preBuiltObjects.view;
}
get injector() {
return this.elementInjector._lightDomAppInjector;
}
get boundElementIndex() {
return this.elementInjector._proto.index;
}
}
export class DirectiveRef extends ElementRef {
_key:Key;
constructor(key:Key, elementInjector:ElementInjector){
super(elementInjector);
this._key = key;
}
get instance() {
return this.elementInjector.get(this._key);
}
}
export class ComponentRef extends DirectiveRef {
componentView:viewModule.AppView;
constructor(key:Key, elementInjector:ElementInjector, componentView:viewModule.AppView){
super(key, elementInjector);
this.componentView = componentView;
}
}
class StaticKeys {
viewId:number;
ngElementId:number;
viewContainerId:number;
bindingPropagationConfigId:number;
privateComponentLocationId:number;
directiveRefId:number;
constructor() {
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
this.viewId = Key.get(viewModule.View).id;
//TODO: vsavkin Key.annotate(Key.get(AppView), 'static')
this.viewId = Key.get(viewModule.AppView).id;
this.ngElementId = Key.get(NgElement).id;
this.viewContainerId = Key.get(ViewContainer).id;
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
this.privateComponentLocationId = Key.get(pclModule.PrivateComponentLocation).id;
this.directiveRefId = Key.get(DirectiveRef).id;
}
static instance() {
@ -42,39 +83,123 @@ class StaticKeys {
}
}
class TreeNode {
export class TreeNode {
_parent:TreeNode;
_head:TreeNode;
_tail:TreeNode;
_next:TreeNode;
constructor(parent:TreeNode) {
this._parent = parent;
this._head = null;
this._tail = null;
this._next = null;
if (isPresent(parent)) parent._addChild(this);
if (isPresent(parent)) parent.addChild(this);
}
_assertConsistency() {
this._assertHeadBeforeTail();
this._assertTailReachable();
this._assertPresentInParentList();
}
_assertHeadBeforeTail() {
if (isBlank(this._tail) && isPresent(this._head)) throw new BaseException('null tail but non-null head');
}
_assertTailReachable() {
if (isBlank(this._tail)) return;
if (isPresent(this._tail._next)) throw new BaseException('node after tail');
var p = this._head;
while (isPresent(p) && p != this._tail) p = p._next;
if (isBlank(p) && isPresent(this._tail)) throw new BaseException('tail not reachable.')
}
_assertPresentInParentList() {
var p = this._parent;
if (isBlank(p)) {
return;
}
var cur = p._head;
while (isPresent(cur) && cur != this) cur = cur._next;
if (isBlank(cur)) throw new BaseException('node not reachable through parent.')
}
/**
* Adds a child to the parent node. The child MUST NOT be a part of a tree.
*/
_addChild(child:TreeNode) {
addChild(child:TreeNode) {
if (isPresent(this._tail)) {
this._tail._next = child;
this._tail = child;
} else {
this._tail = this._head = child;
}
child._next = null;
child._parent = this;
this._assertConsistency();
}
/**
* Adds a child to the parent node after a given sibling.
* The child MUST NOT be a part of a tree and the sibling must be present.
*/
addChildAfter(child:TreeNode, prevSibling:TreeNode) {
this._assertConsistency();
if (isBlank(prevSibling)) {
var prevHead = this._head;
this._head = child;
child._next = prevHead;
if (isBlank(this._tail)) this._tail = child;
} else if (isBlank(prevSibling._next)) {
this.addChild(child);
return;
} else {
prevSibling._assertPresentInParentList();
child._next = prevSibling._next;
prevSibling._next = child;
}
child._parent = this;
this._assertConsistency();
}
/**
* Detaches a node from the parent's tree.
*/
remove() {
this._assertConsistency();
if (isBlank(this.parent)) return;
var nextSibling = this._next;
var prevSibling = this._findPrev();
if (isBlank(prevSibling)) {
this.parent._head = this._next;
} else {
prevSibling._next = this._next;
}
if (isBlank(nextSibling)) {
this._parent._tail = prevSibling;
}
this._parent._assertConsistency();
this._parent = null;
this._next = null;
this._assertConsistency();
}
/**
* Finds a previous sibling or returns null if first child.
* Assumes the node has a parent.
* TODO(rado): replace with DoublyLinkedList to avoid O(n) here.
*/
_findPrev() {
var node = this.parent._head;
if (node == this) return null;
while (node._next !== this) node = node._next;
return node;
}
get parent() {
return this._parent;
}
set parent(node:TreeNode) {
this._parent = node;
}
// TODO(rado): replace with a function call, does too much work for a getter.
get children() {
var res = [];
var child = this._head;
@ -90,56 +215,95 @@ export class DirectiveDependency extends Dependency {
depth:int;
eventEmitterName:string;
propSetterName:string;
attributeName:string;
queryDirective;
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
properties:List, depth:int, eventEmitterName: string, propSetterName: string) {
properties:List, depth:int, eventEmitterName: string,
propSetterName: string, attributeName:string, queryDirective) {
super(key, asPromise, lazy, optional, properties);
this.depth = depth;
this.eventEmitterName = eventEmitterName;
this.propSetterName = propSetterName;
this.attributeName = attributeName;
this.queryDirective = queryDirective;
this._verify();
}
_verify() {
var count = 0;
if (isPresent(this.eventEmitterName)) count++;
if (isPresent(this.propSetterName)) count++;
if (isPresent(this.queryDirective)) count++;
if (isPresent(this.attributeName)) count++;
if (count > 1) throw new BaseException(
'A directive injectable can contain only one of the following @EventEmitter, @PropertySetter, @Attribute or @Query.');
}
static createFrom(d:Dependency):Dependency {
var depth = 0;
var eventName = null;
var propName = null;
var properties = d.properties;
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
d.properties, DirectiveDependency._depth(d.properties),
DirectiveDependency._eventEmitterName(d.properties),
DirectiveDependency._propSetterName(d.properties),
DirectiveDependency._attributeName(d.properties),
DirectiveDependency._query(d.properties)
);
}
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if (property instanceof Parent) {
depth = 1;
} else if (property instanceof Ancestor) {
depth = MAX_DEPTH;
} else if (property instanceof EventEmitter) {
eventName = property.eventName;
} else if (property instanceof PropertySetter) {
propName = property.propName;
}
}
static _depth(properties):int {
if (properties.length == 0) return 0;
if (ListWrapper.any(properties, p => p instanceof Parent)) return 1;
if (ListWrapper.any(properties, p => p instanceof Ancestor)) return MAX_DEPTH;
return 0;
}
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties, depth,
eventName, propName);
static _eventEmitterName(properties):string {
var p = ListWrapper.find(properties, (p) => p instanceof EventEmitter);
return isPresent(p) ? p.eventName : null;
}
static _propSetterName(properties):string {
var p = ListWrapper.find(properties, (p) => p instanceof PropertySetter);
return isPresent(p) ? p.propName : null;
}
static _attributeName(properties):string {
var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
return isPresent(p) ? p.attributeName : null;
}
static _query(properties) {
var p = ListWrapper.find(properties, (p) => p instanceof Query);
return isPresent(p) ? p.directive : null;
}
}
export class DirectiveBinding extends Binding {
export class DirectiveBinding extends ResolvedBinding {
callOnDestroy:boolean;
callOnChange:boolean;
callOnAllChangesDone:boolean;
annotation:Directive;
resolvedInjectables:List<ResolvedBinding>;
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
super(key, factory, dependencies, providedAsPromise);
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
this.annotation = annotation;
if (annotation instanceof Component && isPresent(annotation.injectables)) {
this.resolvedInjectables = Injector.resolve(annotation.injectables);
}
}
static createFromBinding(b:Binding, annotation:Directive):Binding {
var deps = ListWrapper.map(b.dependencies, DirectiveDependency.createFrom);
return new DirectiveBinding(b.key, b.factory, deps, b.providedAsPromise, annotation);
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
var rb = b.resolve();
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, annotation);
}
static createFromType(type:Type, annotation:Directive):Binding {
var binding = bind(type).toClass(type);
static createFromType(type:Type, annotation:Directive):DirectiveBinding {
var binding = new Binding(type, {toClass: type});
return DirectiveBinding.createFromBinding(binding, annotation);
}
@ -150,7 +314,7 @@ export class DirectiveBinding extends Binding {
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
export class PreBuiltObjects {
view:viewModule.View;
view:viewModule.AppView;
element:NgElement;
viewContainer:ViewContainer;
bindingPropagationConfig:BindingPropagationConfig;
@ -184,16 +348,16 @@ ElementInjector:
*/
export class ProtoElementInjector {
_binding0:Binding;
_binding1:Binding;
_binding2:Binding;
_binding3:Binding;
_binding4:Binding;
_binding5:Binding;
_binding6:Binding;
_binding7:Binding;
_binding8:Binding;
_binding9:Binding;
_binding0:ResolvedBinding;
_binding1:ResolvedBinding;
_binding2:ResolvedBinding;
_binding3:ResolvedBinding;
_binding4:ResolvedBinding;
_binding5:ResolvedBinding;
_binding6:ResolvedBinding;
_binding7:ResolvedBinding;
_binding8:ResolvedBinding;
_binding9:ResolvedBinding;
_binding0IsComponent:boolean;
_keyId0:int;
_keyId1:int;
@ -207,8 +371,11 @@ export class ProtoElementInjector {
_keyId9:int;
parent:ProtoElementInjector;
index:int;
view:viewModule.View;
view:viewModule.AppView;
distanceToParent:number;
attributes:Map;
numberOfDirectives:number;
/** Whether the element is exported as $implicit. */
exportElement:boolean;
@ -238,6 +405,7 @@ export class ProtoElementInjector {
this._binding8 = null; this._keyId8 = null;
this._binding9 = null; this._keyId9 = null;
this.numberOfDirectives = bindings.length;
var length = bindings.length;
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
@ -255,8 +423,8 @@ export class ProtoElementInjector {
}
}
instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector {
return new ElementInjector(this, parent, host);
instantiate(parent:ElementInjector):ElementInjector {
return new ElementInjector(this, parent);
}
directParent(): ProtoElementInjector {
@ -276,6 +444,20 @@ export class ProtoElementInjector {
return isPresent(this._binding0);
}
getDirectiveBindingAtIndex(index:int) {
if (index == 0) return this._binding0;
if (index == 1) return this._binding1;
if (index == 2) return this._binding2;
if (index == 3) return this._binding3;
if (index == 4) return this._binding4;
if (index == 5) return this._binding5;
if (index == 6) return this._binding6;
if (index == 7) return this._binding7;
if (index == 8) return this._binding8;
if (index == 9) return this._binding9;
throw new OutOfBoundsAccess(index);
}
hasEventEmitter(eventName: string) {
var p = this;
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
@ -297,6 +479,8 @@ export class ElementInjector extends TreeNode {
_lightDomAppInjector:Injector;
_shadowDomAppInjector:Injector;
_host:ElementInjector;
// If this element injector has a component, the component instance will be stored in _obj0
_obj0:any;
_obj1:any;
_obj2:any;
@ -309,21 +493,17 @@ export class ElementInjector extends TreeNode {
_obj9:any;
_preBuiltObjects;
_constructionCounter;
_privateComponent;
_privateComponentBinding:DirectiveBinding;
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) {
_dynamicallyCreatedComponent:any;
_dynamicallyCreatedComponentBinding:DirectiveBinding;
// Queries are added during construction or linking with a new parent.
// They are never removed.
_query0: QueryRef;
_query1: QueryRef;
_query2: QueryRef;
constructor(proto:ProtoElementInjector, parent:ElementInjector) {
super(parent);
if (isPresent(parent) && isPresent(host)) {
throw new BaseException('Only either parent or host is allowed');
}
this._host = null; // needed to satisfy Dart
if (isPresent(parent)) {
this._host = parent._host;
} else {
this._host = host;
}
this._proto = proto;
//we cannot call clearDirectives because fields won't be detected
@ -341,9 +521,13 @@ export class ElementInjector extends TreeNode {
this._obj8 = null;
this._obj9 = null;
this._constructionCounter = 0;
this._inheritQueries(parent);
this._buildQueries();
}
clearDirectives() {
this._host = null;
this._preBuiltObjects = null;
this._lightDomAppInjector = null;
this._shadowDomAppInjector = null;
@ -360,8 +544,8 @@ export class ElementInjector extends TreeNode {
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
if (isPresent(this._privateComponentBinding) && this._privateComponentBinding.callOnDestroy) {
this._privateComponent.onDestroy();
if (isPresent(this._dynamicallyCreatedComponentBinding) && this._dynamicallyCreatedComponentBinding.callOnDestroy) {
this._dynamicallyCreatedComponent.onDestroy();
}
this._obj0 = null;
@ -374,12 +558,14 @@ export class ElementInjector extends TreeNode {
this._obj7 = null;
this._obj8 = null;
this._obj9 = null;
this._privateComponent = null;
this._dynamicallyCreatedComponent = null;
this._dynamicallyCreatedComponentBinding = null;
this._constructionCounter = 0;
}
instantiateDirectives(lightDomAppInjector:Injector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) {
instantiateDirectives(lightDomAppInjector:Injector, host:ElementInjector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) {
this._host = host;
this._checkShadowDomAppInjector(shadowDomAppInjector);
this._preBuiltObjects = preBuiltObjects;
@ -397,15 +583,13 @@ export class ElementInjector extends TreeNode {
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
if (isPresent(this._privateComponentBinding)) {
this._privateComponent = this._new(this._privateComponentBinding);
}
}
createPrivateComponent(componentType:Type, annotation:Directive) {
this._privateComponentBinding = DirectiveBinding.createFromType(componentType, annotation);
this._privateComponent = this._new(this._privateComponentBinding);
return this._privateComponent;
dynamicallyCreateComponent(componentType:Type, annotation:Directive, injector:Injector) {
this._shadowDomAppInjector = injector;
this._dynamicallyCreatedComponentBinding = DirectiveBinding.createFromType(componentType, annotation);
this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding);
return this._dynamicallyCreatedComponent;
}
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
@ -417,22 +601,22 @@ export class ElementInjector extends TreeNode {
}
get(token) {
if (this._isDynamicallyLoadedComponent(token)) {
return this._dynamicallyCreatedComponent;
}
return this._getByKey(Key.get(token), 0, false, null);
}
_isDynamicallyLoadedComponent(token) {
return isPresent(this._dynamicallyCreatedComponentBinding) &&
Key.get(token) === this._dynamicallyCreatedComponentBinding.key;
}
hasDirective(type:Type):boolean {
return this._getDirectiveByKeyId(Key.get(type).id) !== _undefined;
}
hasPreBuiltObject(type:Type):boolean {
var pb = this._getPreBuiltObjectByKeyId(Key.get(type).id);
return pb !== _undefined && isPresent(pb);
}
forElement(el):boolean {
return this._preBuiltObjects.element.domElement === el;
}
/** Gets the NgElement associated with this ElementInjector */
getNgElement() {
return this._preBuiltObjects.element;
@ -442,16 +626,12 @@ export class ElementInjector extends TreeNode {
if (this._proto._binding0IsComponent) {
return this._obj0;
} else {
throw new BaseException('There is not component stored in this ElementInjector');
throw new BaseException('There is no component stored in this ElementInjector');
}
}
getPrivateComponent() {
return this._privateComponent;
}
getShadowDomAppInjector() {
return this._shadowDomAppInjector;
getDynamicallyLoadedComponent() {
return this._dynamicallyCreatedComponent;
}
directParent(): ElementInjector {
@ -462,11 +642,12 @@ export class ElementInjector extends TreeNode {
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
}
_isPrivateComponentKey(key:Key) {
return isPresent(this._privateComponentBinding) && key.id === this._privateComponentBinding.key.id;
_isDynamicallyLoadedComponentKey(key:Key) {
return isPresent(this._dynamicallyCreatedComponentBinding) && key.id ===
this._dynamicallyCreatedComponentBinding.key.id;
}
_new(binding:Binding) {
_new(binding:ResolvedBinding) {
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
throw new CyclicDependencyError(binding.key);
}
@ -508,16 +689,24 @@ export class ElementInjector extends TreeNode {
default: throw `Directive ${binding.key.token} can only have up to 10 dependencies.`;
}
this._addToQueries(obj, binding.key.token);
return obj;
}
_getByDependency(dep:DirectiveDependency, requestor:Key) {
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
if (dep.key.id === StaticKeys.instance().directiveRefId) {
// TODO: we need store component view here and pass it to directive ref
return new DirectiveRef(requestor, this);
}
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
}
_buildEventEmitter(dep) {
_buildEventEmitter(dep: DirectiveDependency) {
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
return (event) => {
view.triggerEventHandlers(dep.eventEmitterName, event, this._proto.index);
@ -525,10 +714,135 @@ export class ElementInjector extends TreeNode {
}
_buildPropSetter(dep) {
var ngElement = this._getPreBuiltObjectByKeyId(StaticKeys.instance().ngElementId);
var domElement = ngElement.domElement;
var setter = reflector.setter(dep.propSetterName);
return function(v) { setter(domElement, v) };
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
var renderer = view.proto.renderer;
var index = this._proto.index;
return function(v) {
renderer.setElementProperty(view.render, index, dep.propSetterName, v);
};
}
_buildAttribute(dep): string {
var attributes = this._proto.attributes;
if (isPresent(attributes) && MapWrapper.contains(attributes, dep.attributeName)) {
return MapWrapper.get(attributes, dep.attributeName);
} else {
return null;
}
}
_buildQueriesForDeps(deps: List<DirectiveDependency>) {
for (var i = 0; i < deps.length; i++) {
var dep = deps[i];
if (isPresent(dep.queryDirective)) {
this._createQueryRef(dep.queryDirective);
}
}
}
_createQueryRef(directive) {
var queryList = new QueryList();
if (isBlank(this._query0)) {this._query0 = new QueryRef(directive, queryList, this);}
else if (isBlank(this._query1)) {this._query1 = new QueryRef(directive, queryList, this);}
else if (isBlank(this._query2)) {this._query2 = new QueryRef(directive, queryList, this);}
else throw new QueryError();
}
_addToQueries(obj, token) {
if (isPresent(this._query0) && (this._query0.directive === token)) {this._query0.list.add(obj);}
if (isPresent(this._query1) && (this._query1.directive === token)) {this._query1.list.add(obj);}
if (isPresent(this._query2) && (this._query2.directive === token)) {this._query2.list.add(obj);}
}
// TODO(rado): unify with _addParentQueries.
_inheritQueries(parent: ElementInjector) {
if (isBlank(parent)) return;
if (isPresent(parent._query0)) {this._query0 = parent._query0;}
if (isPresent(parent._query1)) {this._query1 = parent._query1;}
if (isPresent(parent._query2)) {this._query2 = parent._query2;}
}
_buildQueries() {
if (isBlank(this._proto)) return;
var p = this._proto;
if (isPresent(p._binding0)) {this._buildQueriesForDeps(p._binding0.dependencies);}
if (isPresent(p._binding1)) {this._buildQueriesForDeps(p._binding1.dependencies);}
if (isPresent(p._binding2)) {this._buildQueriesForDeps(p._binding2.dependencies);}
if (isPresent(p._binding3)) {this._buildQueriesForDeps(p._binding3.dependencies);}
if (isPresent(p._binding4)) {this._buildQueriesForDeps(p._binding4.dependencies);}
if (isPresent(p._binding5)) {this._buildQueriesForDeps(p._binding5.dependencies);}
if (isPresent(p._binding6)) {this._buildQueriesForDeps(p._binding6.dependencies);}
if (isPresent(p._binding7)) {this._buildQueriesForDeps(p._binding7.dependencies);}
if (isPresent(p._binding8)) {this._buildQueriesForDeps(p._binding8.dependencies);}
if (isPresent(p._binding9)) {this._buildQueriesForDeps(p._binding9.dependencies);}
}
_findQuery(token) {
if (isPresent(this._query0) && this._query0.directive === token) {return this._query0;}
if (isPresent(this._query1) && this._query1.directive === token) {return this._query1;}
if (isPresent(this._query2) && this._query2.directive === token) {return this._query2;}
throw new BaseException(`Cannot find query for directive ${token}.`);
}
link(parent: ElementInjector) {
parent.addChild(this);
this._addParentQueries();
}
linkAfter(parent: ElementInjector, prevSibling: ElementInjector) {
parent.addChildAfter(this, prevSibling);
this._addParentQueries();
}
_addParentQueries() {
if (isPresent(this.parent._query0)) {this._addQueryToTree(this.parent._query0); this.parent._query0.update();}
if (isPresent(this.parent._query1)) {this._addQueryToTree(this.parent._query1); this.parent._query1.update();}
if (isPresent(this.parent._query2)) {this._addQueryToTree(this.parent._query2); this.parent._query2.update();}
}
unlink() {
var queriesToUpDate = [];
if (isPresent(this.parent._query0)) {this._pruneQueryFromTree(this.parent._query0); ListWrapper.push(queriesToUpDate, this.parent._query0);}
if (isPresent(this.parent._query1)) {this._pruneQueryFromTree(this.parent._query1); ListWrapper.push(queriesToUpDate, this.parent._query1);}
if (isPresent(this.parent._query2)) {this._pruneQueryFromTree(this.parent._query2); ListWrapper.push(queriesToUpDate, this.parent._query2);}
this.remove();
ListWrapper.forEach(queriesToUpDate, (q) => q.update());
}
_pruneQueryFromTree(query: QueryRef) {
this._removeQueryRef(query);
var child = this._head;
while (isPresent(child)) {
child._pruneQueryFromTree(query);
child = child._next;
}
}
_addQueryToTree(query: QueryRef) {
this._assignQueryRef(query);
var child = this._head;
while (isPresent(child)) {
child._addQueryToTree(query);
child = child._next;
}
}
_assignQueryRef(query: QueryRef) {
if (isBlank(this._query0)) {this._query0 = query; return;}
else if (isBlank(this._query1)) {this._query1 = query; return;}
else if (isBlank(this._query2)) {this._query2 = query; return;}
throw new QueryError();
}
_removeQueryRef(query: QueryRef) {
if (this._query0 == query) this._query0 = null;
if (this._query1 == query) this._query1 = null;
if (this._query2 == query) this._query2 = null;
}
/*
@ -545,7 +859,6 @@ export class ElementInjector extends TreeNode {
*/
_getByKey(key:Key, depth:number, optional:boolean, requestor:Key) {
var ei = this;
if (! this._shouldIncludeSelf(depth)) {
depth -= ei._proto.distanceToParent;
ei = ei._parent;
@ -562,10 +875,11 @@ export class ElementInjector extends TreeNode {
ei = ei._parent;
}
if (isPresent(this._host) && this._host._isComponentKey(key)) {
return this._host.getComponent();
} else if (isPresent(this._host) && this._host._isPrivateComponentKey(key)) {
return this._host.getPrivateComponent();
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
return this._host.getDynamicallyLoadedComponent();
} else if (optional) {
return this._appInjector(requestor).getOptional(key);
} else {
@ -574,7 +888,7 @@ export class ElementInjector extends TreeNode {
}
_appInjector(requestor:Key) {
if (isPresent(requestor) && this._isComponentKey(requestor)) {
if (isPresent(requestor) && (this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
return this._shadowDomAppInjector;
} else {
return this._lightDomAppInjector;
@ -587,16 +901,12 @@ export class ElementInjector extends TreeNode {
_getPreBuiltObjectByKeyId(keyId:int) {
var staticKeys = StaticKeys.instance();
// TODO: View should not be injectable. Remove it.
// TODO: AppView should not be injectable. Remove it.
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
if (keyId === staticKeys.privateComponentLocationId) {
return new pclModule.PrivateComponentLocation(this, this._preBuiltObjects.element, this._preBuiltObjects.view);
}
//TODO add other objects as needed
return _undefined;
}
@ -632,18 +942,7 @@ export class ElementInjector extends TreeNode {
}
getDirectiveBindingAtIndex(index:int) {
var p = this._proto;
if (index == 0) return p._binding0;
if (index == 1) return p._binding1;
if (index == 2) return p._binding2;
if (index == 3) return p._binding3;
if (index == 4) return p._binding4;
if (index == 5) return p._binding5;
if (index == 6) return p._binding6;
if (index == 7) return p._binding7;
if (index == 8) return p._binding8;
if (index == 9) return p._binding9;
throw new OutOfBoundsAccess(index);
return this._proto.getDirectiveBindingAtIndex(index);
}
hasInstances() {
@ -681,3 +980,45 @@ class OutOfBoundsAccess extends Error {
return this.message;
}
}
class QueryError extends Error {
message:string;
// TODO(rado): pass the names of the active directives.
constructor() {
super();
this.message = 'Only 3 queries can be concurrently active in a template.';
}
toString() {
return this.message;
}
}
class QueryRef {
directive;
list: QueryList;
originator: ElementInjector;
constructor(directive, list: QueryList, originator: ElementInjector) {
this.directive = directive;
this.list = list;
this.originator = originator;
}
update() {
var aggregator = [];
this.visit(this.originator, aggregator);
this.list.reset(aggregator);
}
visit(inj: ElementInjector, aggregator) {
if (isBlank(inj)) return;
if (inj.hasDirective(this.directive)) {
ListWrapper.push(aggregator, inj.get(this.directive));
}
var child = inj._head;
while (isPresent(child)) {
this.visit(child, aggregator);
child = child._next;
}
}
}

View File

@ -1,5 +1,5 @@
/**
* @publicModule angular2/angular2
* @exportedAs angular2/angular2
*/
export class OnChange {
onChange(changes) {

View File

@ -0,0 +1,34 @@
import {DOM} from 'angular2/src/dom/dom_adapter';
import {normalizeBlank} from 'angular2/src/facade/lang';
import * as viewModule from '../compiler/view';
import {DirectDomViewRef} from 'angular2/src/render/dom/direct_dom_renderer';
/**
* Allows direct access to the underlying DOM element.
*
* Attention: NgElement will be replaced by a different concept
* for accessing an element in a way that is compatible with the render layer.
*
* @exportedAs angular2/core
*/
export class NgElement {
_view:viewModule.AppView;
_boundElementIndex:number;
constructor(view, boundElementIndex) {
this._view = view;
this._boundElementIndex = boundElementIndex;
}
// TODO(tbosch): Here we expose the real DOM element.
// We need a more general way to read/write to the DOM element
// via a proper abstraction in the render layer
get domElement() {
var domViewRef:DirectDomViewRef = this._view.render;
return domViewRef.delegate.boundElements[this._boundElementIndex];
}
getAttribute(name:string) {
return normalizeBlank(DOM.getAttribute(this.domElement, name));
}
}

View File

@ -1,219 +0,0 @@
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
import {DirectiveMetadata} from '../directive_metadata';
import {Decorator, Component, Viewport, DynamicComponent} from '../../annotations/annotations';
import {ElementBinder} from '../element_binder';
import {ProtoElementInjector} from '../element_injector';
import * as viewModule from '../view';
import {dashCaseToCamelCase} from './util';
import {AST} from 'angular2/change_detection';
/**
* Collects all data that is needed to process an element
* in the compile process. Fields are filled
* by the CompileSteps starting out with the pure HTMLElement.
*/
export class CompileElement {
element;
_attrs:Map;
_classList:List;
textNodeBindings:Map;
propertyBindings:Map;
eventBindings:Map;
/// Store directive name to template name mapping.
/// Directive name is what the directive exports the variable as
/// Template name is how it is reffered to it in template
variableBindings:Map;
decoratorDirectives:List<DirectiveMetadata>;
viewportDirective:DirectiveMetadata;
componentDirective:DirectiveMetadata;
hasNestedView:boolean;
_allDirectives:List<DirectiveMetadata>;
isViewRoot:boolean;
hasBindings:boolean;
inheritedProtoView:viewModule.ProtoView;
inheritedProtoElementInjector:ProtoElementInjector;
inheritedElementBinder:ElementBinder;
distanceToParentInjector:int;
distanceToParentBinder:int;
compileChildren: boolean;
ignoreBindings: boolean;
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
contentTagSelector: string;
constructor(element, compilationUnit = '') {
this.element = element;
this._attrs = null;
this._classList = null;
this.textNodeBindings = null;
this.propertyBindings = null;
this.eventBindings = null;
this.variableBindings = null;
this.decoratorDirectives = null;
this.viewportDirective = null;
this.componentDirective = null;
this.hasNestedView = false;
this._allDirectives = null;
this.isViewRoot = false;
this.hasBindings = false;
// inherited down to children if they don't have
// an own protoView
this.inheritedProtoView = null;
// inherited down to children if they don't have
// an own protoElementInjector
this.inheritedProtoElementInjector = null;
// inherited down to children if they don't have
// an own elementBinder
this.inheritedElementBinder = null;
this.distanceToParentInjector = 0;
this.distanceToParentBinder = 0;
this.compileChildren = true;
// set to true to ignore all the bindings on the element
this.ignoreBindings = false;
this.contentTagSelector = null;
// description is calculated here as compilation steps may change the element
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
if (compilationUnit !== '') {
this.elementDescription = compilationUnit;
if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
} else {
this.elementDescription = tplDesc;
}
}
refreshAttrs() {
this._attrs = null;
}
attrs():Map<string,string> {
if (isBlank(this._attrs)) {
this._attrs = DOM.attributeMap(this.element);
}
return this._attrs;
}
refreshClassList() {
this._classList = null;
}
classList():List<string> {
if (isBlank(this._classList)) {
this._classList = ListWrapper.create();
var elClassList = DOM.classList(this.element);
for (var i = 0; i < elClassList.length; i++) {
ListWrapper.push(this._classList, elClassList[i]);
}
}
return this._classList;
}
addTextNodeBinding(indexInParent:int, expression:AST) {
if (isBlank(this.textNodeBindings)) {
this.textNodeBindings = MapWrapper.create();
}
MapWrapper.set(this.textNodeBindings, indexInParent, expression);
}
addPropertyBinding(property:string, expression:AST) {
if (isBlank(this.propertyBindings)) {
this.propertyBindings = MapWrapper.create();
}
MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
}
addVariableBinding(variableName:string, variableValue:string) {
if (isBlank(this.variableBindings)) {
this.variableBindings = MapWrapper.create();
}
// Store the variable map from value to variable, reflecting how it will be used later by
// View. When a local is set to the view, a lookup for the variable name will take place keyed
// by the "value", or exported identifier. For example, ng-repeat sets a view local of "index".
// When this occurs, a lookup keyed by "index" must occur to find if there is a var referencing
// it.
MapWrapper.set(this.variableBindings, variableValue, dashCaseToCamelCase(variableName));
}
addEventBinding(eventName:string, expression:AST) {
if (isBlank(this.eventBindings)) {
this.eventBindings = MapWrapper.create();
}
MapWrapper.set(this.eventBindings, eventName, expression);
}
addDirective(directive:DirectiveMetadata) {
var annotation = directive.annotation;
this._allDirectives = null;
if (annotation instanceof Decorator) {
if (isBlank(this.decoratorDirectives)) {
this.decoratorDirectives = ListWrapper.create();
}
ListWrapper.push(this.decoratorDirectives, directive);
if (!annotation.compileChildren) {
this.compileChildren = false;
}
} else if (annotation instanceof Viewport) {
this.viewportDirective = directive;
} else if (annotation instanceof Component) {
this.componentDirective = directive;
this.hasNestedView = true;
} else if (annotation instanceof DynamicComponent) {
this.componentDirective = directive;
}
}
getAllDirectives(): List<DirectiveMetadata> {
if (this._allDirectives === null) {
// Collect all the directives
// When present the component directive must be first
var directives = ListWrapper.create();
if (isPresent(this.componentDirective)) {
ListWrapper.push(directives, this.componentDirective);
}
if (isPresent(this.viewportDirective)) {
ListWrapper.push(directives, this.viewportDirective);
}
if (isPresent(this.decoratorDirectives)) {
directives = ListWrapper.concat(directives, this.decoratorDirectives);
}
this._allDirectives = directives;
}
return this._allDirectives;
}
}
// return an HTML representation of an element start tag - without its content
// this is used to give contextual information in case of errors
function getElementDescription(domElement):string {
var buf = new StringJoiner();
var atts = DOM.attributeMap(domElement);
buf.add("<");
buf.add(DOM.tagName(domElement).toLowerCase());
// show id and class first to ease element identification
addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
MapWrapper.forEach(atts, (attValue, attName) => {
if (attName !== "id" && attName !== "class") {
addDescriptionAttribute(buf, attName, attValue);
}
});
buf.add(">");
return buf.toString();
}
function addDescriptionAttribute(buffer:StringJoiner, attName:string, attValue) {
if (isPresent(attValue)) {
if (attValue.length === 0) {
buffer.add(' ' + attName);
} else {
buffer.add(' ' + attName + '="' + attValue + '"');
}
}
}

View File

@ -1,45 +0,0 @@
import {ChangeDetection, Parser} from 'angular2/change_detection';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {PropertyBindingParser} from './property_binding_parser';
import {TextInterpolationParser} from './text_interpolation_parser';
import {DirectiveParser} from './directive_parser';
import {ViewSplitter} from './view_splitter';
import {ElementBindingMarker} from './element_binding_marker';
import {ProtoViewBuilder} from './proto_view_builder';
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
import {ElementBinderBuilder} from './element_binder_builder';
import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
import {ShadowDomStrategy, EmulatedScopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
/**
* Default steps used for compiling a template.
* Takes in an HTMLElement and produces the ProtoViews,
* ProtoElementInjectors and ElementBinders in the end.
*/
export function createDefaultSteps(
changeDetection:ChangeDetection,
parser:Parser,
compiledComponent: DirectiveMetadata,
directives: List<DirectiveMetadata>,
shadowDomStrategy: ShadowDomStrategy,
templateUrl: string,
cssProcessor: CssProcessor) {
var steps = [
new ViewSplitter(parser),
cssProcessor.getCompileStep(compiledComponent, shadowDomStrategy, templateUrl),
shadowDomStrategy.getTemplateCompileStep(compiledComponent),
new PropertyBindingParser(parser),
new DirectiveParser(directives),
new TextInterpolationParser(parser),
new ElementBindingMarker(),
new ProtoViewBuilder(changeDetection, shadowDomStrategy),
new ProtoElementInjectorBuilder(),
new ElementBinderBuilder(parser),
];
return steps;
}

View File

@ -1,150 +0,0 @@
import {isPresent, isBlank, BaseException, assertionsEnabled, RegExpWrapper} from 'angular2/src/facade/lang';
import {List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {SelectorMatcher} from '../selector';
import {CssSelector} from '../selector';
import {DirectiveMetadata} from '../directive_metadata';
import {DynamicComponent, Component, Viewport} from '../../annotations/annotations';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {isSpecialProperty} from './element_binder_builder';
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
/**
* Parses the directives on a single element. Assumes ViewSplitter has already created
* <template> elements for template directives.
*
* Fills:
* - CompileElement#decoratorDirectives
* - CompileElement#templateDirecitve
* - CompileElement#componentDirective.
*
* Reads:
* - CompileElement#propertyBindings (to find directives contained
* in the property bindings)
* - CompileElement#variableBindings (to find directives contained
* in the variable bindings)
*/
export class DirectiveParser extends CompileStep {
_selectorMatcher:SelectorMatcher;
constructor(directives:List<DirectiveMetadata>) {
super();
var selector;
this._selectorMatcher = new SelectorMatcher();
for (var i=0; i<directives.length; i++) {
var directiveMetadata = directives[i];
selector=CssSelector.parse(directiveMetadata.annotation.selector);
this._selectorMatcher.addSelectable(selector, directiveMetadata);
}
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var attrs = current.attrs();
var classList = current.classList();
var cssSelector = new CssSelector();
var nodeName = DOM.nodeName(current.element);
cssSelector.setElement(nodeName);
for (var i=0; i < classList.length; i++) {
cssSelector.addClassName(classList[i]);
}
MapWrapper.forEach(attrs, (attrValue, attrName) => {
cssSelector.addAttribute(attrName, attrValue);
});
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
// only be present on <template> elements any more!
var isTemplateElement = DOM.isTemplateElement(current.element);
var matchedProperties; // StringMap - used in dev mode to store all properties that have been matched
this._selectorMatcher.match(cssSelector, (selector, directive) => {
matchedProperties = updateMatchedProperties(matchedProperties, selector, directive);
checkDirectiveValidity(directive, current, isTemplateElement);
current.addDirective(directive);
});
// raise error if some directives are missing
checkMissingDirectives(current, matchedProperties, isTemplateElement);
}
}
// calculate all the properties that are used or interpreted by all directives
// those properties correspond to the directive selectors and the directive bindings
function updateMatchedProperties(matchedProperties, selector, directive) {
if (assertionsEnabled()) {
var attrs = selector.attrs;
if (!isPresent(matchedProperties)) {
matchedProperties = StringMapWrapper.create();
}
if (isPresent(attrs)) {
for (var idx = 0; idx<attrs.length; idx+=2) {
// attribute name is stored on even indexes
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(attrs[idx]), true);
}
}
// some properties can be used by the directive, so we need to register them
if (isPresent(directive.annotation) && isPresent(directive.annotation.bind)) {
var bindMap = directive.annotation.bind;
StringMapWrapper.forEach(bindMap, (value, key) => {
// value is the name of the property that is interpreted
// e.g. 'myprop' or 'myprop | double' when a pipe is used to transform the property
// keep the property name and remove the pipe
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
if (isPresent(bindProp) && isPresent(bindProp[1])) {
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(bindProp[1]), true);
}
});
}
}
return matchedProperties;
}
// check if the directive is compatible with the current element
function checkDirectiveValidity(directive, current, isTemplateElement) {
var isComponent = directive.annotation instanceof Component || directive.annotation instanceof DynamicComponent;
var alreadyHasComponent = isPresent(current.componentDirective);
if (directive.annotation instanceof Viewport) {
if (!isTemplateElement) {
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
`with template attribute - check ${current.elementDescription}`);
} else if (isPresent(current.viewportDirective)) {
throw new BaseException(`Only one viewport directive can be used per element - check ${current.elementDescription}`);
}
} else if (isTemplateElement) {
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
} else if (isComponent && alreadyHasComponent) {
throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`);
}
}
// validates that there is no missing directive - dev mode only
function checkMissingDirectives(current, matchedProperties, isTemplateElement) {
if (assertionsEnabled()) {
var ppBindings=current.propertyBindings;
if (isPresent(ppBindings)) {
// check that each property corresponds to a real property or has been matched by a directive
MapWrapper.forEach(ppBindings, (expression, prop) => {
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
throw new BaseException(`Missing directive to handle '${camelCaseToDashCase(prop)}' in ${current.elementDescription}`);
}
}
});
}
// template only store directives as attribute when they are not bound to expressions
// so we have to validate the expression case too (e.g. !if="condition")
if (isTemplateElement && !current.isViewRoot && !isPresent(current.viewportDirective)) {
throw new BaseException(`Missing directive to handle: ${current.elementDescription}`);
}
}
}

View File

@ -1,281 +0,0 @@
import {int, isPresent, isBlank, Type, BaseException, StringWrapper, RegExpWrapper, isString, stringify} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {ListWrapper, List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection';
import {Parser, ProtoChangeDetector} from 'angular2/change_detection';
import {DirectiveMetadata} from '../directive_metadata';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
var DOT_REGEXP = RegExpWrapper.create('\\.');
const ARIA_PREFIX = 'aria';
var ariaSettersCache = StringMapWrapper.create();
function ariaSetterFactory(attrName:string) {
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
var ariaAttrName;
if (isBlank(setterFn)) {
ariaAttrName = camelCaseToDashCase(attrName);
setterFn = function(element, value) {
if (isPresent(value)) {
DOM.setAttribute(element, ariaAttrName, stringify(value));
} else {
DOM.removeAttribute(element, ariaAttrName);
}
};
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
}
return setterFn;
}
const CLASS_PREFIX = 'class.';
var classSettersCache = StringMapWrapper.create();
function classSetterFactory(className:string) {
var setterFn = StringMapWrapper.get(classSettersCache, className);
if (isBlank(setterFn)) {
setterFn = function(element, value) {
if (value) {
DOM.addClass(element, className);
} else {
DOM.removeClass(element, className);
}
};
StringMapWrapper.set(classSettersCache, className, setterFn);
}
return setterFn;
}
const STYLE_PREFIX = 'style.';
var styleSettersCache = StringMapWrapper.create();
function styleSetterFactory(styleName:string, stylesuffix:string) {
var cacheKey = styleName + stylesuffix;
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
var dashCasedStyleName;
if (isBlank(setterFn)) {
dashCasedStyleName = camelCaseToDashCase(styleName);
setterFn = function(element, value) {
var valAsStr;
if (isPresent(value)) {
valAsStr = stringify(value);
DOM.setStyle(element, dashCasedStyleName, valAsStr + stylesuffix);
} else {
DOM.removeStyle(element, dashCasedStyleName);
}
};
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
}
return setterFn;
}
const ROLE_ATTR = 'role';
function roleSetter(element, value) {
if (isString(value)) {
DOM.setAttribute(element, ROLE_ATTR, value);
} else {
DOM.removeAttribute(element, ROLE_ATTR);
if (isPresent(value)) {
throw new BaseException("Invalid role attribute, only string values are allowed, got '" + stringify(value) + "'");
}
}
}
// tells if an attribute is handled by the ElementBinderBuilder step
export function isSpecialProperty(propName:string) {
return StringWrapper.startsWith(propName, ARIA_PREFIX)
|| StringWrapper.startsWith(propName, CLASS_PREFIX)
|| StringWrapper.startsWith(propName, STYLE_PREFIX)
|| StringMapWrapper.contains(DOM.attrToPropMap, propName);
}
/**
* Creates the ElementBinders and adds watches to the
* ProtoChangeDetector.
*
* Fills:
* - CompileElement#inheritedElementBinder
*
* Reads:
* - (in parent) CompileElement#inheritedElementBinder
* - CompileElement#hasBindings
* - CompileElement#inheritedProtoView
* - CompileElement#inheritedProtoElementInjector
* - CompileElement#textNodeBindings
* - CompileElement#propertyBindings
* - CompileElement#eventBindings
* - CompileElement#decoratorDirectives
* - CompileElement#componentDirective
* - CompileElement#viewportDirective
*
* Note: This actually only needs the CompileElements with the flags
* `hasBindings` and `isViewRoot`,
* and only needs the actual HTMLElement for the ones
* with the flag `isViewRoot`.
*/
export class ElementBinderBuilder extends CompileStep {
_parser:Parser;
constructor(parser:Parser) {
super();
this._parser = parser;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var elementBinder = null;
var parentElementBinder = null;
var distanceToParentBinder = this._getDistanceToParentBinder(parent, current);
if (isPresent(parent)) {
parentElementBinder = parent.inheritedElementBinder;
}
if (current.hasBindings) {
var protoView = current.inheritedProtoView;
var protoInjectorWasBuilt = isBlank(parent) ? true :
current.inheritedProtoElementInjector !== parent.inheritedProtoElementInjector;
var currentProtoElementInjector = protoInjectorWasBuilt ?
current.inheritedProtoElementInjector : null;
elementBinder = protoView.bindElement(parentElementBinder, distanceToParentBinder,
currentProtoElementInjector, current.componentDirective, current.viewportDirective);
current.distanceToParentBinder = 0;
if (isPresent(current.textNodeBindings)) {
this._bindTextNodes(protoView, current);
}
if (isPresent(current.propertyBindings)) {
this._bindElementProperties(protoView, current);
}
if (isPresent(current.eventBindings)) {
this._bindEvents(protoView, current);
}
if (isPresent(current.contentTagSelector)) {
elementBinder.contentTagSelector = current.contentTagSelector;
}
var directives = current.getAllDirectives();
this._bindDirectiveProperties(directives, current);
this._bindDirectiveEvents(directives, current);
} else if (isPresent(parent)) {
elementBinder = parentElementBinder;
current.distanceToParentBinder = distanceToParentBinder;
}
current.inheritedElementBinder = elementBinder;
}
_getDistanceToParentBinder(parent, current) {
return isPresent(parent) ? parent.distanceToParentBinder + 1 : 0;
}
_bindTextNodes(protoView, compileElement) {
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
protoView.bindTextNode(indexInParent, expression);
});
}
_bindElementProperties(protoView, compileElement) {
MapWrapper.forEach(compileElement.propertyBindings, (expression, property) => {
var setterFn, styleParts, styleSuffix;
if (StringWrapper.startsWith(property, ARIA_PREFIX)) {
setterFn = ariaSetterFactory(property);
} else if (StringWrapper.equals(property, ROLE_ATTR)) {
setterFn = roleSetter;
} else if (StringWrapper.startsWith(property, CLASS_PREFIX)) {
setterFn = classSetterFactory(StringWrapper.substring(property, CLASS_PREFIX.length));
} else if (StringWrapper.startsWith(property, STYLE_PREFIX)) {
styleParts = StringWrapper.split(property, DOT_REGEXP);
styleSuffix = styleParts.length > 2 ? ListWrapper.get(styleParts, 2) : '';
setterFn = styleSetterFactory(ListWrapper.get(styleParts, 1), styleSuffix);
} else {
property = this._resolvePropertyName(property);
//TODO(pk): special casing innerHtml, see: https://github.com/angular/angular/issues/789
if (StringWrapper.equals(property, 'innerHTML')) {
setterFn = (element, value) => DOM.setInnerHTML(element, value);
} else if (DOM.hasProperty(compileElement.element, property) || StringWrapper.equals(property, 'innerHtml')) {
setterFn = reflector.setter(property);
}
}
if (isPresent(setterFn)) {
protoView.bindElementProperty(expression.ast, property, setterFn);
}
});
}
_bindEvents(protoView, compileElement) {
MapWrapper.forEach(compileElement.eventBindings, (expression, eventName) => {
protoView.bindEvent(eventName, expression);
});
}
_bindDirectiveEvents(directives: List<DirectiveMetadata>, compileElement: CompileElement) {
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
var directive = directives[directiveIndex];
var annotation = directive.annotation;
if (isBlank(annotation.events)) continue;
var protoView = compileElement.inheritedProtoView;
StringMapWrapper.forEach(annotation.events, (action, eventName) => {
var expression = this._parser.parseAction(action, compileElement.elementDescription);
protoView.bindEvent(eventName, expression, directiveIndex);
});
}
}
_bindDirectiveProperties(directives: List<DirectiveMetadata>,
compileElement: CompileElement) {
var protoView = compileElement.inheritedProtoView;
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
var directive = ListWrapper.get(directives, directiveIndex);
var annotation = directive.annotation;
if (isBlank(annotation.bind)) continue;
StringMapWrapper.forEach(annotation.bind, (bindConfig, dirProp) => {
var pipes = this._splitBindConfig(bindConfig);
var elProp = ListWrapper.removeAt(pipes, 0);
var bindingAst = isPresent(compileElement.propertyBindings) ?
MapWrapper.get(compileElement.propertyBindings, dashCaseToCamelCase(elProp)) :
null;
if (isBlank(bindingAst)) {
var attributeValue = MapWrapper.get(compileElement.attrs(), elProp);
if (isPresent(attributeValue)) {
bindingAst = this._parser.wrapLiteralPrimitive(attributeValue, compileElement.elementDescription);
}
}
// Bindings are optional, so this binding only needs to be set up if an expression is given.
if (isPresent(bindingAst)) {
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
protoView.bindDirectiveProperty(
directiveIndex,
fullExpAstWithBindPipes,
dirProp,
reflector.setter(dashCaseToCamelCase(dirProp))
);
}
});
}
}
_splitBindConfig(bindConfig:string) {
return ListWrapper.map(bindConfig.split('|'), (s) => s.trim());
}
_resolvePropertyName(attrName:string) {
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, attrName);
return isPresent(mappedPropName) ? mappedPropName : attrName;
}
}

View File

@ -1,49 +0,0 @@
import {isPresent} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
const NG_BINDING_CLASS = 'ng-binding';
/**
* Marks elements that have bindings with a css class
* and sets the CompileElement.hasBindings flag.
*
* Fills:
* - CompileElement#hasBindings
*
* Reads:
* - CompileElement#textNodeBindings
* - CompileElement#propertyBindings
* - CompileElement#variableBindings
* - CompileElement#eventBindings
* - CompileElement#decoratorDirectives
* - CompileElement#componentDirective
* - CompileElement#viewportDirective
*/
export class ElementBindingMarker extends CompileStep {
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (current.ignoreBindings) {
return;
}
var hasBindings =
(isPresent(current.textNodeBindings) && MapWrapper.size(current.textNodeBindings)>0) ||
(isPresent(current.propertyBindings) && MapWrapper.size(current.propertyBindings)>0) ||
(isPresent(current.variableBindings) && MapWrapper.size(current.variableBindings)>0) ||
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
isPresent(current.viewportDirective) ||
isPresent(current.componentDirective) ||
isPresent(current.contentTagSelector);
if (hasBindings) {
var element = current.element;
DOM.addClass(element, NG_BINDING_CLASS);
current.hasBindings = true;
}
}
}

View File

@ -1,95 +0,0 @@
import {isPresent, isBlank, RegExpWrapper, BaseException} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection';
import {Parser, AST, ExpressionWithSource} from 'angular2/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
// Group 1 = "bind"
// Group 2 = "var"
// Group 3 = "on"
// Group 4 = the identifier after "bind", "var", or "on"
// Group 5 = idenitifer inside square braces
// Group 6 = identifier inside parenthesis
// Group 7 = "#"
// Group 8 = identifier after "#"
var BIND_NAME_REGEXP = RegExpWrapper.create(
'^(?:(?:(?:(bind)|(var)|(on))-(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\)|(#)(.+))$');
/**
* Parses the property bindings on a single element.
*
* Fills:
* - CompileElement#propertyBindings
* - CompileElement#eventBindings
* - CompileElement#variableBindings
*/
export class PropertyBindingParser extends CompileStep {
_parser:Parser;
constructor(parser:Parser) {
super();
this._parser = parser;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (current.ignoreBindings) {
return;
}
var attrs = current.attrs();
var newAttrs = MapWrapper.create();
var desc = current.elementDescription;
MapWrapper.forEach(attrs, (attrValue, attrName) => {
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
if (isPresent(bindParts)) {
if (isPresent(bindParts[1])) {
// match: bind-prop
current.addPropertyBinding(bindParts[4], this._parseBinding(attrValue, desc));
MapWrapper.set(newAttrs, bindParts[4], attrValue);
} else if (isPresent(bindParts[2]) || isPresent(bindParts[7])) {
// match: var-name / var-name="iden" / #name / #name="iden"
var identifier = (isPresent(bindParts[4]) && bindParts[4] !== '') ?
bindParts[4] : bindParts[8];
var value = attrValue == '' ? '\$implicit' : attrValue;
current.addVariableBinding(identifier, value);
MapWrapper.set(newAttrs, identifier, value);
} else if (isPresent(bindParts[3])) {
// match: on-event
current.addEventBinding(bindParts[4], this._parseAction(attrValue, desc));
} else if (isPresent(bindParts[5])) {
// match: [prop]
current.addPropertyBinding(bindParts[5], this._parseBinding(attrValue, desc));
MapWrapper.set(newAttrs, bindParts[5], attrValue);
} else if (isPresent(bindParts[6])) {
// match: (event)
current.addEventBinding(bindParts[6], this._parseAction(attrValue, desc));
}
} else {
var ast = this._parseInterpolation(attrValue, desc);
if (isPresent(ast)) {
current.addPropertyBinding(attrName, ast);
}
}
});
MapWrapper.forEach(newAttrs, (attrValue, attrName) => {
MapWrapper.set(attrs, attrName, attrValue);
});
}
_parseInterpolation(input:string, location:string):AST {
return this._parser.parseInterpolation(input, location);
}
_parseBinding(input:string, location:string):AST {
return this._parser.parseBinding(input, location);
}
_parseAction(input:string, location:string):AST {
return this._parser.parseAction(input, location);
}
}

View File

@ -1,83 +0,0 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {ProtoElementInjector, ComponentKeyMetaData, DirectiveBinding} from '../element_injector';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {DirectiveMetadata} from '../directive_metadata';
/**
* Creates the ProtoElementInjectors.
*
* Fills:
* - CompileElement#inheritedProtoElementInjector
* - CompileElement#distanceToParentInjector
*
* Reads:
* - (in parent) CompileElement#inheritedProtoElementInjector
* - (in parent) CompileElement#distanceToParentInjector
* - CompileElement#isViewRoot
* - CompileElement#inheritedProtoView
* - CompileElement#decoratorDirectives
* - CompileElement#componentDirective
* - CompileElement#viewportDirective
*/
export class ProtoElementInjectorBuilder extends CompileStep {
// public so that we can overwrite it in tests
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance) {
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance);
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
// Create a protoElementInjector for any element that either has bindings *or* has one
// or more var- defined. Elements with a var- defined need a their own element injector
// so that, when hydrating, $implicit can be set to the element.
if (injectorBindings.length > 0 || isPresent(current.variableBindings)) {
var protoView = current.inheritedProtoView;
var hasComponent = isPresent(current.componentDirective);
current.inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings,
hasComponent, distanceToParentInjector
);
current.distanceToParentInjector = 0;
// Viewport directives are treated differently than other element with var- definitions.
if (isPresent(current.variableBindings) && !isPresent(current.viewportDirective)) {
current.inheritedProtoElementInjector.exportComponent = hasComponent;
current.inheritedProtoElementInjector.exportElement = !hasComponent;
// experiment
var exportImplicitName = MapWrapper.get(current.variableBindings, '\$implicit');
if (isPresent(exportImplicitName)) {
current.inheritedProtoElementInjector.exportImplicitName = exportImplicitName;
}
}
} else {
current.inheritedProtoElementInjector = parentProtoElementInjector;
current.distanceToParentInjector = distanceToParentInjector;
}
}
_getDistanceToParentInjector(parent, current) {
return isPresent(parent) ? parent.distanceToParentInjector + 1 : 0;
}
_getParentProtoElementInjector(parent, current) {
if (isPresent(parent) && !current.isViewRoot) {
return parent.inheritedProtoElementInjector;
}
return null;
}
_createBinding(d:DirectiveMetadata): DirectiveBinding {
return DirectiveBinding.createFromType(d.type, d.annotation);
}
}

View File

@ -1,75 +0,0 @@
import {isPresent, BaseException} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {ProtoView} from '../view';
import {ChangeDetection} from 'angular2/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {ShadowDomStrategy} from '../shadow_dom_strategy';
/**
* Creates ProtoViews and forwards variable bindings from parent to children.
*
* Fills:
* - (in parent): CompileElement#inheritedElementBinder.nestedProtoView
* - CompileElement#inheritedProtoView
*
* Reads:
* - (in parent): CompileElement#inheritedProtoView
* - (in parent): CompileElement#variableBindings
* - CompileElement#isViewRoot
*/
export class ProtoViewBuilder extends CompileStep {
changeDetection:ChangeDetection;
_shadowDomStrategy:ShadowDomStrategy;
constructor(changeDetection:ChangeDetection, shadowDomStrategy:ShadowDomStrategy) {
super();
this._shadowDomStrategy = shadowDomStrategy;
this.changeDetection = changeDetection;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var inheritedProtoView = null;
if (current.isViewRoot) {
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
inheritedProtoView = new ProtoView(current.element, protoChangeDetector,
this._shadowDomStrategy, this._getParentProtoView(parent));
if (isPresent(parent)) {
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
throw new BaseException('Only one nested view per element is allowed');
}
parent.inheritedElementBinder.nestedProtoView = inheritedProtoView;
// When current is a view root, the variable bindings are set to the *nested* proto view.
// The root view conceptually signifies a new "block scope" (the nested view), to which
// the variables are bound.
if (isPresent(parent.variableBindings)) {
MapWrapper.forEach(parent.variableBindings, (mappedName, varName) => {
inheritedProtoView.bindVariable(varName, mappedName);
});
}
}
} else if (isPresent(parent)) {
inheritedProtoView = parent.inheritedProtoView;
}
// The view's locals needs to have a full set of variable names at construction time
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
// to actually create variable bindings for the $implicit bindings, add to the
// protoLocals manually.
if (isPresent(current.variableBindings)) {
MapWrapper.forEach(current.variableBindings, (mappedName, varName) => {
MapWrapper.set(inheritedProtoView.protoLocals, mappedName, null);
});
}
current.inheritedProtoView = inheritedProtoView;
}
_getParentProtoView(parent:CompileElement) {
return isPresent(parent) ? parent.inheritedProtoView : null;
}
}

View File

@ -1,34 +0,0 @@
import {Compiler} from './compiler';
import {ShadowDomStrategy} from './shadow_dom_strategy';
import {EventManager} from 'angular2/src/core/events/event_manager';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {PrivateComponentLocation} from './private_component_location';
import {Type} from 'angular2/src/facade/lang';
export class PrivateComponentLoader {
compiler:Compiler;
shadowDomStrategy:ShadowDomStrategy;
eventManager:EventManager;
directiveMetadataReader:DirectiveMetadataReader;
constructor(compiler:Compiler, shadowDomStrategy:ShadowDomStrategy,
eventManager:EventManager, directiveMetadataReader:DirectiveMetadataReader) {
this.compiler = compiler;
this.shadowDomStrategy = shadowDomStrategy;
this.eventManager = eventManager;
this.directiveMetadataReader = directiveMetadataReader;
}
load(type:Type, location:PrivateComponentLocation) {
var annotation = this.directiveMetadataReader.read(type).annotation;
return this.compiler.compile(type).then((componentProtoView) => {
location.createComponent(
type, annotation,
componentProtoView,
this.eventManager,
this.shadowDomStrategy);
});
}
}

View File

@ -1,34 +0,0 @@
import {Directive} from 'angular2/src/core/annotations/annotations'
import {NgElement} from 'angular2/src/core/dom/element';
import * as viewModule from './view';
import * as eiModule from './element_injector';
import {ShadowDomStrategy} from './shadow_dom_strategy';
import {EventManager} from 'angular2/src/core/events/event_manager';
import {ListWrapper} from 'angular2/src/facade/collection';
import {Type} from 'angular2/src/facade/lang';
export class PrivateComponentLocation {
_elementInjector:eiModule.ElementInjector;
_elt:NgElement;
_view:viewModule.View;
constructor(elementInjector:eiModule.ElementInjector, elt:NgElement, view:viewModule.View){
this._elementInjector = elementInjector;
this._elt = elt;
this._view = view;
}
createComponent(type:Type, annotation:Directive, componentProtoView:viewModule.ProtoView,
eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) {
var context = this._elementInjector.createPrivateComponent(type, annotation);
var view = componentProtoView.instantiate(this._elementInjector, eventManager);
view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, null, context, null);
shadowDomStrategy.attachTemplate(this._elt.domElement, view);
ListWrapper.push(this._view.componentChildViews, view);
this._view.changeDetector.addChild(view.changeDetector);
}
}

View File

@ -0,0 +1,187 @@
import {Injectable} from 'angular2/di';
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {ChangeDetection} from 'angular2/change_detection';
import {Component, Viewport, DynamicComponent} from '../annotations/annotations';
import * as renderApi from 'angular2/src/render/api';
import {AppProtoView} from './view';
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
@Injectable()
export class ProtoViewFactory {
_changeDetection:ChangeDetection;
_renderer:renderApi.Renderer;
constructor(changeDetection:ChangeDetection, renderer:renderApi.Renderer) {
this._changeDetection = changeDetection;
this._renderer = renderer;
}
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
var protoChangeDetector;
if (isBlank(componentBinding)) {
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
} else {
var componentAnnotation:Component = componentBinding.annotation;
protoChangeDetector = this._changeDetection.createProtoChangeDetector(
'dummy', componentAnnotation.changeDetection
);
}
var protoView = new AppProtoView(this._renderer, renderProtoView.render, protoChangeDetector);
for (var i=0; i<renderProtoView.elementBinders.length; i++) {
var renderElementBinder = renderProtoView.elementBinders[i];
var sortedDirectives = new SortedDirectives(renderElementBinder.directives, directives);
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
i, protoView.elementBinders, renderProtoView.elementBinders
);
var protoElementInjector = this._createProtoElementInjector(
i, parentPeiWithDistance,
sortedDirectives, renderElementBinder
);
this._createElementBinder(
protoView, renderElementBinder, protoElementInjector, sortedDirectives
);
this._createDirectiveBinders(protoView, sortedDirectives);
}
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
protoView.bindVariable(varName, mappedName);
});
return protoView;
}
_findParentProtoElementInjectorWithDistance(binderIndex, elementBinders, renderElementBinders) {
var distance = 0;
do {
var renderElementBinder = renderElementBinders[binderIndex];
binderIndex = renderElementBinder.parentIndex;
if (binderIndex !== -1) {
distance += renderElementBinder.distanceToParent;
var elementBinder = elementBinders[binderIndex];
if (isPresent(elementBinder.protoElementInjector)) {
return new ParentProtoElementInjectorWithDistance(elementBinder.protoElementInjector, distance);
}
}
} while (binderIndex !== -1);
return new ParentProtoElementInjectorWithDistance(null, -1);
}
_createProtoElementInjector(binderIndex, parentPeiWithDistance, sortedDirectives, renderElementBinder) {
var protoElementInjector = null;
// Create a protoElementInjector for any element that either has bindings *or* has one
// or more var- defined. Elements with a var- defined need a their own element injector
// so that, when hydrating, $implicit can be set to the element.
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
if (sortedDirectives.directives.length > 0 || hasVariables) {
protoElementInjector = new ProtoElementInjector(
parentPeiWithDistance.protoElementInjector, binderIndex,
sortedDirectives.directives,
isPresent(sortedDirectives.componentDirective), parentPeiWithDistance.distance
);
protoElementInjector.attributes = renderElementBinder.readAttributes;
// Viewport directives are treated differently than other element with var- definitions.
if (hasVariables && !isPresent(sortedDirectives.viewportDirective)) {
protoElementInjector.exportComponent = isPresent(sortedDirectives.componentDirective);
protoElementInjector.exportElement = isBlank(sortedDirectives.componentDirective);
// experiment
var exportImplicitName = MapWrapper.get(renderElementBinder.variableBindings, '\$implicit');
if (isPresent(exportImplicitName)) {
protoElementInjector.exportImplicitName = exportImplicitName;
}
}
}
return protoElementInjector;
}
_createElementBinder(protoView, renderElementBinder, protoElementInjector, sortedDirectives) {
var parent = null;
if (renderElementBinder.parentIndex !== -1) {
parent = protoView.elementBinders[renderElementBinder.parentIndex];
}
var elBinder = protoView.bindElement(
parent,
renderElementBinder.distanceToParent,
protoElementInjector,
sortedDirectives.componentDirective,
sortedDirectives.viewportDirective
);
// text nodes
for (var i=0; i<renderElementBinder.textBindings.length; i++) {
protoView.bindTextNode(renderElementBinder.textBindings[i].ast);
}
// element properties
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
protoView.bindElementProperty(astWithSource.ast, propertyName);
});
// events
protoView.bindEvent(renderElementBinder.eventBindings, -1);
// variables
// The view's locals needs to have a full set of variable names at construction time
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
// to actually create variable bindings for the $implicit bindings, add to the
// protoLocals manually.
MapWrapper.forEach(renderElementBinder.variableBindings, (mappedName, varName) => {
MapWrapper.set(protoView.protoLocals, mappedName, null);
});
return elBinder;
}
_createDirectiveBinders(protoView, sortedDirectives) {
for (var i=0; i<sortedDirectives.renderDirectives.length; i++) {
var renderDirectiveMetadata = sortedDirectives.renderDirectives[i];
// directive properties
MapWrapper.forEach(renderDirectiveMetadata.propertyBindings, (astWithSource, propertyName) => {
// TODO: these setters should eventually be created by change detection, to make
// it monomorphic!
var setter = reflector.setter(propertyName);
protoView.bindDirectiveProperty(i, astWithSource.ast, propertyName, setter);
});
// directive events
protoView.bindEvent(renderDirectiveMetadata.eventBindings, i);
}
}
}
class SortedDirectives {
componentDirective: DirectiveBinding;
viewportDirective: DirectiveBinding;
renderDirectives: List<renderApi.DirectiveMetadata>;
directives: List<DirectiveBinding>;
constructor(renderDirectives, allDirectives) {
this.renderDirectives = [];
this.directives = [];
this.viewportDirective = null;
this.componentDirective = null;
ListWrapper.forEach(renderDirectives, (renderDirectiveMetadata) => {
var directiveBinding = allDirectives[renderDirectiveMetadata.directiveIndex];
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) {
// component directives need to be the first binding in ElementInjectors!
this.componentDirective = directiveBinding;
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveMetadata);
ListWrapper.insert(this.directives, 0, directiveBinding);
} else {
if (directiveBinding.annotation instanceof Viewport) {
this.viewportDirective = directiveBinding;
}
ListWrapper.push(this.renderDirectives, renderDirectiveMetadata);
ListWrapper.push(this.directives, directiveBinding);
}
});
}
}
class ParentProtoElementInjectorWithDistance {
protoElementInjector:ProtoElementInjector;
distance:number;
constructor(protoElementInjector:ProtoElementInjector, distance:number) {
this.protoElementInjector = protoElementInjector;
this.distance = distance;
}
}

View File

@ -0,0 +1,47 @@
library angular2.src.core.compiler.query_list;
import 'package:angular2/src/core/annotations/annotations.dart';
import 'dart:collection';
/**
* Injectable Objects that contains a live list of child directives in the light Dom of a directive.
* The directives are kept in depth-first pre-order traversal of the DOM.
*
* In the future this class will implement an Observable interface.
* For now it uses a plain list of observable callbacks.
*/
class QueryList extends Object with IterableMixin<Directive> {
List<Directive> _results;
List _callbacks;
bool _dirty;
QueryList(): _results = [], _callbacks = [], _dirty = false;
Iterator<Directive> get iterator => _results.iterator;
reset(newList) {
_results = newList;
_dirty = true;
}
add(obj) {
_results.add(obj);
_dirty = true;
}
// TODO(rado): hook up with change detection after #995.
fireCallbacks() {
if (_dirty) {
_callbacks.forEach((c) => c());
_dirty = false;
}
}
onChange(callback) {
this._callbacks.add(callback);
}
removeCallback(callback) {
this._callbacks.remove(callback);
}
}

View File

@ -0,0 +1,51 @@
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {Directive} from 'angular2/src/core/annotations/annotations';
/**
* Injectable Objects that contains a live list of child directives in the light Dom of a directive.
* The directives are kept in depth-first pre-order traversal of the DOM.
*
* In the future this class will implement an Observable interface.
* For now it uses a plain list of observable callbacks.
*/
export class QueryList {
_results: List<Directive>;
_callbacks;
_dirty;
constructor() {
this._results = [];
this._callbacks = [];
this._dirty = false;
}
[Symbol.iterator]() {
return this._results[Symbol.iterator]();
}
reset(newList) {
this._results = newList;
this._dirty = true;
}
add(obj) {
ListWrapper.push(this._results, obj);
this._dirty = true;
}
// TODO(rado): hook up with change detection after #995.
fireCallbacks() {
if (this._dirty) {
ListWrapper.forEach(this._callbacks, (c) => c());
this._dirty = false;
}
}
onChange(callback) {
ListWrapper.push(this._callbacks, callback);
}
removeCallback(callback) {
ListWrapper.remove(this._callbacks, callback);
}
}

View File

@ -1,378 +0,0 @@
import {Injectable} from 'angular2/di';
import {Type, isBlank, isPresent, int, StringWrapper, assertionsEnabled} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, Map} from 'angular2/src/facade/collection';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {DOM} from 'angular2/src/dom/dom_adapter';
import * as viewModule from './view';
import {LightDom} from './shadow_dom_emulation/light_dom';
import {ShadowCss} from './shadow_dom_emulation/shadow_css';
import {StyleInliner} from './style_inliner';
import {StyleUrlResolver} from './style_url_resolver';
import {DirectiveMetadata} from './directive_metadata';
import * as NS from './pipeline/compile_step';
import {CompileElement} from './pipeline/compile_element';
import {CompileControl} from './pipeline/compile_control';
var _EMPTY_STEP;
// Note: fill _EMPTY_STEP to prevent
// problems from cyclic dependencies
function _emptyStep() {
if (isBlank(_EMPTY_STEP)) {
_EMPTY_STEP = new _EmptyCompileStep();
}
return _EMPTY_STEP;
}
export class ShadowDomStrategy {
attachTemplate(el, view:viewModule.View) {}
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { return null; }
/**
* An optional step that can modify the template style elements.
*
* @param {DirectiveMetadata} cmpMetadata
* @param {string} templateUrl the template base URL
* @returns {CompileStep} a compile step to append to the compiler pipeline
*/
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
return _emptyStep();
}
/**
* An optional step that can modify the template elements (style elements exlcuded).
*
* This step could be used to modify the template in order to scope the styles.
*
* @param {DirectiveMetadata} cmpMetadata
* @returns {CompileStep} a compile step to append to the compiler pipeline
*/
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep { return _emptyStep(); }
/**
* The application element does not go through the compiler pipeline.
*
* This methods is called when the root ProtoView is created and to optionnaly update the
* application root element.
*
* @see ProtoView.createRootProtoView
*
* @param {DirectiveMetadata} cmpMetadata
* @param element
*/
shimAppElement(cmpMetadata: DirectiveMetadata, element) {}
}
/**
* This strategy emulates the Shadow DOM for the templates, styles **excluded**:
* - components templates are added as children of their component element,
* - styles are moved from the templates to the styleHost (i.e. the document head).
*
* Notes:
* - styles are **not** scoped to their component and will apply to the whole document,
* - you can **not** use shadow DOM specific selectors in the styles
*/
@Injectable()
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
_styleUrlResolver: StyleUrlResolver;
_styleHost;
constructor(styleUrlResolver: StyleUrlResolver, styleHost) {
super();
this._styleUrlResolver = styleUrlResolver;
this._styleHost = styleHost;
}
attachTemplate(el, view:viewModule.View) {
DOM.clearNodes(el);
_moveViewNodesIntoParent(el, view);
}
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom {
return new LightDom(lightDomView, shadowDomView, el);
}
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
return new _EmulatedUnscopedCssStep(cmpMetadata, templateUrl, this._styleUrlResolver,
this._styleHost);
}
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
return new _BaseEmulatedShadowDomStep();
}
}
/**
* This strategy emulates the Shadow DOM for the templates, styles **included**:
* - components templates are added as children of their component element,
* - both the template and the styles are modified so that styles are scoped to the component
* they belong to,
* - styles are moved from the templates to the styleHost (i.e. the document head).
*
* Notes:
* - styles are scoped to their component and will apply only to it,
* - a common subset of shadow DOM selectors are supported,
* - see `ShadowCss` for more information and limitations.
*/
@Injectable()
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
_styleInliner: StyleInliner;
constructor(styleInliner: StyleInliner, styleUrlResolver: StyleUrlResolver, styleHost) {
super(styleUrlResolver, styleHost);
this._styleInliner = styleInliner;
}
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
return new _EmulatedScopedCssStep(cmpMetadata, templateUrl, this._styleInliner,
this._styleUrlResolver, this._styleHost);
}
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
return new _ShimShadowDomStep(cmpMetadata);
}
shimAppElement(cmpMetadata: DirectiveMetadata, element) {
var cmpType = cmpMetadata.type;
var hostAttribute = _getHostAttribute(_getComponentId(cmpType));
DOM.setAttribute(element, hostAttribute, '');
}
}
/**
* This strategies uses the native Shadow DOM support.
*
* The templates for the component are inserted in a Shadow Root created on the component element.
* Hence they are strictly isolated.
*/
@Injectable()
export class NativeShadowDomStrategy extends ShadowDomStrategy {
_styleUrlResolver: StyleUrlResolver;
constructor(styleUrlResolver: StyleUrlResolver) {
super();
this._styleUrlResolver = styleUrlResolver;
}
attachTemplate(el, view:viewModule.View){
_moveViewNodesIntoParent(DOM.createShadowRoot(el), view);
}
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
return new _NativeCssStep(templateUrl, this._styleUrlResolver);
}
}
class _BaseEmulatedShadowDomStep extends NS.CompileStep {
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
if (current.ignoreBindings) {
return;
}
var nodeName = DOM.nodeName(current.element);
if (StringWrapper.equals(nodeName.toUpperCase(), 'CONTENT')) {
var attrs = current.attrs();
var selector = MapWrapper.get(attrs, 'select');
current.contentTagSelector = isPresent(selector) ? selector : '';
var contentStart = DOM.createScriptTag('type', 'ng/contentStart');
if (assertionsEnabled()) {
DOM.setAttribute(contentStart, 'select', current.contentTagSelector);
}
var contentEnd = DOM.createScriptTag('type', 'ng/contentEnd');
DOM.insertBefore(current.element, contentStart);
DOM.insertBefore(current.element, contentEnd);
DOM.remove(current.element);
current.element = contentStart;
}
}
}
class _EmptyCompileStep extends NS.CompileStep {
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
}
}
class _ShimShadowDomStep extends _BaseEmulatedShadowDomStep {
_contentAttribute: string;
constructor(cmpMetadata: DirectiveMetadata) {
super();
var id = _getComponentId(cmpMetadata.type);
this._contentAttribute = _getContentAttribute(id);
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
super.process(parent, current, control);
if (current.ignoreBindings) {
return;
}
// Shim the element as a child of the compiled component
DOM.setAttribute(current.element, this._contentAttribute, '');
// If the current element is also a component, shim it as a host
var host = current.componentDirective;
if (isPresent(host)) {
var hostId = _getComponentId(host.type);
var hostAttribute = _getHostAttribute(hostId);
DOM.setAttribute(current.element, hostAttribute, '');
}
}
}
class _EmulatedUnscopedCssStep extends NS.CompileStep {
_templateUrl: string;
_styleUrlResolver: StyleUrlResolver;
_styleHost;
constructor(cmpMetadata: DirectiveMetadata, templateUrl: string,
styleUrlResolver: StyleUrlResolver, styleHost) {
super();
this._templateUrl = templateUrl;
this._styleUrlResolver = styleUrlResolver;
this._styleHost = styleHost;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var styleEl = current.element;
var cssText = DOM.getText(styleEl);
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
DOM.setText(styleEl, cssText);
DOM.remove(styleEl);
if (!MapWrapper.contains(_sharedStyleTexts, cssText)) {
// Styles are unscoped and shared across components, only append them to the head
// when there are not present yet
MapWrapper.set(_sharedStyleTexts, cssText, true);
_insertStyleElement(this._styleHost, styleEl);
}
}
}
class _EmulatedScopedCssStep extends NS.CompileStep {
_templateUrl: string;
_component: Type;
_styleInliner: StyleInliner;
_styleUrlResolver: StyleUrlResolver;
_styleHost;
constructor(cmpMetadata: DirectiveMetadata, templateUrl: string, styleInliner: StyleInliner,
styleUrlResolver: StyleUrlResolver, styleHost) {
super();
this._templateUrl = templateUrl;
this._component = cmpMetadata.type;
this._styleInliner = styleInliner;
this._styleUrlResolver = styleUrlResolver;
this._styleHost = styleHost;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var styleEl = current.element;
var cssText = DOM.getText(styleEl);
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
var css = this._styleInliner.inlineImports(cssText, this._templateUrl);
if (PromiseWrapper.isPromise(css)) {
DOM.setText(styleEl, '');
ListWrapper.push(parent.inheritedProtoView.stylePromises, css);
return css.then((css) => {
css = _shimCssForComponent(css, this._component);
DOM.setText(styleEl, css);
});
} else {
css = _shimCssForComponent(css, this._component);
DOM.setText(styleEl, css);
}
DOM.remove(styleEl);
_insertStyleElement(this._styleHost, styleEl);
}
}
class _NativeCssStep extends NS.CompileStep {
_styleUrlResolver: StyleUrlResolver;
_templateUrl: string;
constructor(templateUrl: string, styleUrlResover: StyleUrlResolver) {
super();
this._styleUrlResolver = styleUrlResover;
this._templateUrl = templateUrl;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var styleEl = current.element;
var cssText = DOM.getText(styleEl);
cssText = this._styleUrlResolver.resolveUrls(cssText, this._templateUrl);
DOM.setText(styleEl, cssText);
}
}
function _moveViewNodesIntoParent(parent, view) {
for (var i = 0; i < view.nodes.length; ++i) {
DOM.appendChild(parent, view.nodes[i]);
}
}
var _componentUIDs: Map<Type, int> = MapWrapper.create();
var _nextComponentUID: int = 0;
var _sharedStyleTexts: Map<string, boolean> = MapWrapper.create();
var _lastInsertedStyleEl;
function _getComponentId(component: Type) {
var id = MapWrapper.get(_componentUIDs, component);
if (isBlank(id)) {
id = _nextComponentUID++;
MapWrapper.set(_componentUIDs, component, id);
}
return id;
}
function _insertStyleElement(host, styleEl) {
if (isBlank(_lastInsertedStyleEl)) {
var firstChild = DOM.firstChild(host);
if (isPresent(firstChild)) {
DOM.insertBefore(firstChild, styleEl);
} else {
DOM.appendChild(host, styleEl);
}
} else {
DOM.insertAfter(_lastInsertedStyleEl, styleEl);
}
_lastInsertedStyleEl = styleEl;
}
// Return the attribute to be added to the component
function _getHostAttribute(id: int) {
return `_nghost-${id}`;
}
// Returns the attribute to be added on every single element nodes in the component
function _getContentAttribute(id: int) {
return `_ngcontent-${id}`;
}
function _shimCssForComponent(cssText: string, component: Type): string {
var id = _getComponentId(component);
var shadowCss = new ShadowCss();
return shadowCss.shimCssText(cssText, _getContentAttribute(id), _getHostAttribute(id));
}
// Reset the caches - used for tests only
export function resetShadowDomCache() {
MapWrapper.clear(_componentUIDs);
_nextComponentUID = 0;
MapWrapper.clear(_sharedStyleTexts);
_lastInsertedStyleEl = null;
}

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