+```
+
+Notes:
+ - while tag name are case sensitive, the best practice is to use dash case for component elements so that the browser
+ interpret them as custom elements,
+ - `(some-event)` would now bind to the `some-event` event (i.e. there is no implicit dash to camel case conversion),
+ - `[some-property]` would now bind to the `some-property` property (i.e. there is no implicit dash to camel case conversion),
+ - `#some-var` is not allowed any more ("-" can not be used in variable names).
+
+## Migration
+
+#### Templates
+
+1. Directives selectors, property bindings, event bindings, template variables and template element attributes should be changed to camel case:
+
+ Examples:
+ - `` should be changed to `
`,
+ - `` should be changed to ``,
+ - `` should be changed to ``,
+ - `` should be changed to ``,
+ - ` ` should be changed to ` `,
+ - `` should be changed to ``,
+
+ Note: while the tag names are now case-sensitive the best practice is to keep them lower-dash-cased so that the browser
+ treat them as custom elements. Using dashes in custom element names is required by the [Custom Element HTML Spec](http://www.w3.org/TR/custom-elements/#concepts).
+ This explains why the `` component is left unchanged.
+
+ `on-`, `bindon-`, `bind-` and `var-` prefixes are still part of the canonical syntax and should remain unchanged (lower cased):
+ - `on-some-event` should be changed to `on-someEvent`,
+ - `bind-my-prop` should be changed to `bind-myProp`,
+ - `bindon-my-prop` should be changed to `bindon-myProp`,
+ - `var-my-var` should be changed to `var-myVar`.
+
+2. Update variable binding
+
+ - `` should be changed to `
`
+
+3. The `template` attribute values should also be updated in the same way
+
+ `
` should be changed to `
`.
+
+ Note that both angular directives and your own directives must be updated in the same way.
+
+#### Directives and Components
+
+Take the following steps to upgrade your directives and components:
+
+1. Update the selector:
+ ```
+ @Directive({selector: 'tag[attr][name=value]'})
+ @Component({selector: 'tag[attr][name=value]'})
+ ```
+
+ Tag and attributes names are case sensitive:
+ - For tag names, the best practice is to keep them lower dash cased, do not update them,
+ - For attribute names, the best practice is to convert them from lower dash case to camel case.
+
+ Examples:
+ - `custom-tag` should stay `custom-tag` (do not update tag names),
+ - `[attr-name]` should be updated to `[attrName]`,
+ - `[attr-name=someValue]` should be updated to `[attrName=someValue]`,
+ - `custom-tag[attr-name=someValue]` should be updated to `custom-tag[attrName=someValue]`
+
+ Note: attribute values and classes are still matched case insensitive.
+
+2. Update the inputs
+ ```
+ @Directive({inputs: ['myProp', 'myOtherProp: my-attr-name']})
+ ```
+
+ As attribute names are now case sensitive, they should be converted from dash to camel case where they are specified.
+ The previous decorator becomes:
+
+ ```
+ @Directive({inputs: ['myProp', 'myOtherProp: myAttrName']})
+ ```
+
+ Notes:
+ - only the long syntax (with ":") needs to be updated,
+ - `properties` is the legacy name for `inputs` and should be updated in the same way - it is a good idea to replace
+ `properties` with `inputs` at the same time as support for the former will be removed soon.
+
+ The same applies for the `@Input` decorator:
+
+ ```
+ @Input() myProp;
+ @Input('my-attr-name') myProp;
+ ```
+
+ That is they only need to be updated when the attribute name is specified:
+
+ ```
+ @Input() myProp; // Nothing to update
+ @Input('myAttrName') myProp; // Convert the attribute name to camel case
+ ```
+
+3. Update the outputs
+
+ Update the outputs in the same way the inputs are updated:
+
+ ```
+ @Directive({outputs: ['myEvent', 'myOtherEvent: my-event-name']})
+ ```
+
+ should be updated to:
+
+ ```
+ @Directive({outputs: ['myEvent', 'myOtherEvent: myEventName']})
+ ```
+
+ If you use `events` instead of `outputs` you should update in the same way and switch to `outputs` as `events` is deprecated.
+
+ ```
+ @Output() myEvent;
+ @Output('my-event-name') myEvent;
+ ```
+
+ should be changed to:
+
+ ```
+ @Output() myEvent;
+ @Output('myEventName') myEvent;
+ ```
+
+4. Update the host bindings
+
+ ```
+ @Directive({
+ host: {
+ '[some-prop]': 'exp',
+ '[style.background-color]': 'exp',
+ '[class.some-class]': 'exp',
+ '[attr.some-attr]': 'exp',
+ '(some-event)': 'action()',
+ 'some-attr': 'value'
+ }
+ })
+ ```
+
+ should be changed to:
+
+ ```
+ @Directive({
+ host: {
+ '[someProp]': 'exp',
+ '[style.background-color]': 'exp',
+ '[class.some-class]': 'exp',
+ '[attr.some-attr]': 'exp',
+ '(someEvent)': 'action()',
+ 'some-attr': 'value'
+ }
+ })
+ ```
+
+ The property bindings (`[...]`) and event bindings (`(...)`) must be updated in the same way as they are updated in a
+ template - ie converted to camel case (reminder: `[attr.]`, `[class.]` and `[style.]` should not be converted to camel case).
+
+5. Update export name
+
+ ```
+ @Directive({
+ exportAs: 'some-name'
+ })
+ ```
+
+ should be changed to:
+
+ ```
+ @Directive({
+ exportAs: 'someName'
+ })
+ ```
+
+
+# CSS
+
+As the attribute names from your templates have been updated from dash to camel case, you should also reflect the changes
+in your stylesheets.
+
+The attributes that need to be updated are the ones used in the selector and the inputs of your directives.
+
+Before:
+
+```
+// Directive
+@Directive({
+ selector: '[my-dir]',
+ inputs: ['someProp: some-input'],
+})
+
+
+
+
+/* css */
+[my-dir] { ... }
+[some-input] { ... }
+[not-an-input] { ... }
+```
+
+After:
+
+```
+// Directive
+@Directive({
+ selector: '[myDir]',
+ inputs: ['someProp: someInput'],
+})
+
+
+
+
+/* css */
+[myDir] { ... }
+[someInput] { ... }
+[not-an-input] { ... }
+```
+
+Notes:
+ - `not-an-input` is not used in a selector nor it is an input of a directive, it need not be camel cased,
+ - CSS selectors are case insensitive you can use `[myDir]`, `[mydir]` or any other casing.
diff --git a/modules/angular2/docs/web_workers/web_workers.md b/modules/angular2/docs/web_workers/web_workers.md
new file mode 100644
index 000000000000..7ec8f2694c53
--- /dev/null
+++ b/modules/angular2/docs/web_workers/web_workers.md
@@ -0,0 +1,560 @@
+# WebWorkers in Angular 2: Documentation
+
+Angular 2 includes native support for writing applications which live in a
+WebWorker. This document describes how to write applications that take advantage
+of this feature.
+It also provides a detailed description of the underlying messaging
+infrastructure that angular uses to communicate between the main process and the
+worker. This infrastructure can be modified by an application developer to
+enable driving an angular 2 application from an iFrame, different window / tab,
+server, etc..
+
+## Introduction
+WebWorker support in Angular2 is designed to make it easy to leverage parallelization in your web application.
+When you choose to run your application in a WebWorker angular runs both your application's logic and the
+majority of the core angular framework in a WebWorker.
+By offloading as much code as possible to the WebWorker we keep the UI thread
+free to handle events, manipulate the DOM, and run animations. This provides a
+better framerate and UX for applications.
+
+## Bootstrapping a WebWorker Application
+Bootstrapping a WebWorker application is not much different than bootstrapping a normal application.
+The main difference is that you need to do the bootstrap process on both the worker and render thread.
+Unlike in a standard Angular2 application you don't bootstrap your main component on the render thread.
+Instead you initialize a new application injector with the WORKER_APP_PLATFORM providers and provide the name
+of your WebWorker script. See the example below for details:
+
+### Example
+To bootstrap Hello World in a WebWorker we do the following in TypeScript
+```HTML
+
+
+
+
+
+
+
+
+
+
+
+```
+```TypeScript
+// index.js
+import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT} from "angular2/platform/worker_render";
+import {platform} from "angular2/core";
+
+platform([WORKER_RENDER_PLATFORM])
+.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
+```
+```JavaScript
+// loader.js
+importScripts("https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.js", "https://jspm.io/system@0.16.js", "angular2/web_worker/worker.js");
+System.import("app");
+```
+```TypeScript
+// app.ts
+import {Component, View, platform} from "angular2/core";
+import {WORKER_APP_PLATFORM, WORKER_APP_APPLICATION} from "angular2/platform/worker_app";
+
+@Component({
+ selector: "hello-world"
+})
+@View({
+ template: "Hello {{name}}
+})
+export class HelloWorld {
+ name: string = "Jane";
+}
+
+platform([WORKER_APP_PLATFORM])
+.application([WORKER_APP_APPLICATION])
+.then((ref) => ref.bootstrap(RootComponent));
+```
+There's a few important things to note here:
+* The UI loads angular from the file `angular2/web_worker/ui.js` and the Worker loads angular from
+`angular2/web_worker/worker.js`. These bundles are created specifically for using WebWorkers and should be used
+instead of the normal angular2.js file. Both files contain subsets of the angular2 codebase that is designed to
+run specifically on the UI or Worker. Additionally, they contain the core messaging infrastructure used to
+communicate between the Worker and the UI. This messaging code is not in the standard angular2.js file.
+* We pass `loader.js` to our application injector using the WORKER_SCRIPT symbol. This tells angular that our WebWorkers's init script is located at `loader.js`.
+You can think of `loader.js` as the index.html file for the WebWorker.
+Since WebWorkers share no memory with the UI we need to reload the angular2 dependencies before
+bootstrapping our application. We do this with `importScripts`. Additionally, we need to do this in a different
+file than `app.ts` because our module loader (System.js in this example) has not been loaded yet, and `app.ts`
+will be compiled with a `System.define` call at the top.
+* The HelloWorld Component looks exactly like a normal Angular2 HelloWorld Component! The goal of WebWorker
+support was to allow as much of Angular to live in the worker as possible.
+As such, *most* angular2 components can be bootstrapped in a WebWorker with minimal to no changes required.
+
+For reference, here's the same HelloWorld example in Dart.
+```HTML
+
+
+
+
+
+
+```
+```Dart
+// index.dart
+import "angular2/core.dart";
+import "angular2/platform/worker_render.dart";
+
+main() {
+ platform([WORKER_RENDER_PLATFORM])
+ .asyncApplication(initIsolate("my_worker.dart"));
+}
+```
+```Dart
+// background_index.dart
+import "angular2/core.dart";
+import "angular2/platform/worker_app.dart";
+import "package:angular2/src/core/reflection/reflection.dart";
+import "package:angular2/src/core/reflection/reflection_capabilities.dart";
+
+@Component(
+ selector: "hello-world"
+)
+@View(
+ template: "Hello {{name}} "
+)
+class HelloWorld {
+ String name = "Jane";
+}
+
+main(List args, SendPort replyTo) {
+ reflector.reflectionCapabilities = new ReflectionCapabilities();
+ platform([WORKER_APP_PLATFORM, new Provider(RENDER_SEND_PORT, useValue: replyTo)])
+ .application([WORKER_APP_APPLICATION])
+ .bootstrap(RootComponent);
+}
+
+```
+This code is nearly the same as the TypeScript version with just a couple key differences:
+* We don't have a `loader.js` file. Dart applications don't need this file because you don't need a module loader.
+* We provide a `SendPort` through DI using the token `RENDER_SEND_PORT`. Dart applications use the Isolate API, which communicates via
+Dart's Port abstraction. When you call `setupIsolate` from the UI thread, angular starts a new Isolate to run
+your application logic. When Dart starts a new Isolate it passes a `SendPort` to that Isolate so that it
+can communicate with the Isolate that spawned it. You need to provide this `SendPort` through DI
+so that Angular can communicate with the UI.
+* You need to set up `ReflectionCapabilities` on both the UI and Worker. Just like writing non-concurrent
+Angular2 Dart applications you need to set up the reflector. You should not use Reflection in production,
+but should use the angular 2 transformer to remove it in your final JS code. Note there's currently a bug
+with running the transformer on your UI code (#3971). You can (and should) pass the file where you call
+`bootstrap` as an entry point to the transformer, but you should not pass your UI index file
+to the transformer until that bug is fixed.
+* In dart we call `asyncApplication` instead of `application` from the render thread because starting an isolate in Dart is asyncronous
+ whereas starting a new WebWorker in JavaScript is a synchronous operation.
+
+## Writing WebWorker Compatible Components
+You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component.
+The main exception is that there is **no** DOM access from a WebWorker component. In Dart this means you can't
+import anything from `dart:html` and in JavaScript it means you can't use `document` or `window`. Instead you
+should use data bindings and if needed you can inject the `Renderer` along with your component's `ElementRef`
+directly into your component and use methods such as `setElementProperty`, `setElementAttribute`,
+`setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Note that you **cannot** call
+`getNativeElementSync`. Doing so will always return `null` when running in a WebWorker.
+If you need DOM access see [Running Code on the UI](#running-code-on-the-ui).
+
+## WebWorker Design Overview
+When running your application in a WebWorker, the majority of the angular core along with your application logic
+runs on the worker. The two main components that run on the UI are the `Renderer` and the `RenderCompiler`. When
+running angular in a WebWorker the bindings for these two components are replaced by the `WebWorkerRenderer` and
+the `WebWorkerRenderCompiler`. When these components are used at runtime, they pass messages through the
+[MessageBroker](#messagebroker) instructing the UI to run the actual method and return the result. The
+[MessageBroker](#messagebroker) abstraction allows either side of the WebWorker boundary to schedule code to run
+on the opposite side and receive the result. You can use the [MessageBroker](#messagebroker)
+Additionally, the [MessageBroker](#messagebroker) sits on top of the [MessageBus](#messagebus).
+MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary such as `WebWorker <--> UI` communication, `UI <--> Server` communication,
+or `Window <--> Window` communication.
+
+See the diagram below for a high level overview of how this code is structured:
+
+
+
+## Running Code on the UI
+If your application needs to run code on the UI, there are a few options. The easiest way is to use a
+CustomElement in your view. You can then register this custom element from your html file and run code in response
+to the element's lifecycle hooks. Note, Custom Elements are still experimental. See
+[MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements) for the latest details on how
+to use them.
+
+If you require more robust communication between the WebWorker and the UI you can use the [MessageBroker](#using-the-messagebroker-in-your-application) or
+[MessageBus](#using-the-messagebus-in-your-application) directly.
+
+## MessageBus
+The MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary. It supports multiplex communication through the use of a channel
+abstraction.
+
+Angular currently includes two stable MessageBus implementations, which are used by default when you run your
+application inside a WebWorker.
+
+1. The `PostMessageBus` is used by JavaScript applications to communicate between a WebWorker and the UI.
+2. The `IsolateMessageBus` is used by Dart applications to communicate between a background Isolate and the UI.
+
+Angular also includes three experimental MessageBus implementations:
+
+1. The `WebSocketMessageBus` is a Dart MessageBus that lives on the UI and communicates with an angular
+application running on a server. It's intended to be used with either the `SingleClientServerMessageBus` or the
+`MultiClientServerMessageBus`.
+2. The `SingleClientServerMessageBus` is a Dart MessageBus that lives on a Dart Server. It allows an angular
+application to run on a server and communicate with a single browser that's running the `WebSocketMessageBus`.
+3. The `MultiClientServerMessageBus` is like the `SingleClientServerMessageBus` except it allows an arbitrary
+number of clients to connect to the server. It keeps all connected browsers in sync and if an event fires in
+any connected browser it propagates the result to all connected clients. This can be especially useful as a
+debugging tool, by allowing you to connect multiple browsers / devices to the same angular application,
+change the state of that application, and ensure that all the clients render the view correctly. Using these tools
+can make it easy to catch tricky browser compatibility issues.
+
+### Using the MessageBus in Your Application
+**Note**: If you want to pass custom messages between the UI and WebWorker, it's recommended you use the
+[MessageBroker](#using-the-messagebroker-in-your-application). However, if you want to control the messaging
+protocol yourself you can use the MessageBus directly.
+
+You can obtain the MessageBus on both the render and worker thread through DI.
+To use the MessageBus you need to initialize a new channel on both the UI and WebWorker.
+In TypeScript that would look like this:
+```TypeScript
+// index.ts, which is running on the UI.
+import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, MessageBus} from "angular2/platform/worker_render";
+import {platform} from "angular2/core";
+
+let appRef = platform([WORKER_RENDER_PLATFORM])
+.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
+let bus = appRef.injector.get(MessageBus);
+bus.initChannel("My Custom Channel");
+```
+```TypeScript
+// background_index.ts, which is running on the WebWorker
+import {MessageBus} from 'angular2/platform/worker_app';
+@Component({...})
+@View({...})
+export class MyComponent {
+ constructor (bus: MessageBus) {
+ bus.initChannel("My Custom Channel");
+ }
+}
+```
+
+Once the channel has been initialized either side can use the `from` and `to` methods on the MessageBus to send
+and receive messages. Both methods return EventEmitter. Expanding on the example from earlier:
+```TypeScript
+// index.ts, which is running on the UI.
+import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, MessageBus} from "angular2/platform/worker_render";
+import {platform} from "angular2/core";
+
+let appRef = platform([WORKER_RENDER_PLATFORM])
+.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
+let bus = appRef.injector.get(MessageBus);
+bus.initChannel("My Custom Channel");
+bus.to("My Custom Channel").emit("Hello from the UI");
+```
+```TypeScript
+// background_index.ts, which is running on the WebWorker
+import {Component, View} from 'angular2/core';
+import {MessageBus} from 'angular2/platform/worker_app';
+@Component({...})
+@View({...})
+export class MyComponent {
+ constructor (bus: MessageBus) {
+ bus.initChannel("My Custom Channel");
+ bus.from("My Custom Channel").observer((message) => {
+ console.log(message); // will print "hello from the UI"
+ });
+ }
+}
+```
+
+This example is nearly identical in Dart, and is included below for reference:
+```Dart
+// index.dart, which is running on the UI.
+import 'package:angular2/web_workers/ui.dart';
+
+main() {
+ import "angular2/core.dart";
+ import "angular2/platform/worker_render.dart";
+
+ platform([WORKER_RENDER_PLATFORM])
+ .asyncApplication(initIsolate("my_worker.dart")).then((ref) {
+ var bus = ref.injector.get(MessageBus);
+ bus.initChannel("My Custom Channel");
+ bus.to("My Custom Channel").add("hello from the UI");
+ });
+}
+
+```
+```Dart
+// background_index.dart, which is running on the WebWorker
+import 'package:angular2/platform/worker_app.dart';
+@Component(...)
+@View(...)
+class MyComponent {
+ MyComponent (MessageBus bus) {
+ bus.initChannel("My Custom Channel");
+ bus.from("My Custom Channel").listen((message) {
+ print(message); // will print "hello from the UI"
+ });
+ }
+}
+```
+The only substantial difference between these APIs in Dart and TypeScript is the different APIs for the
+`EventEmitter`.
+
+**Note:** Because the messages passed through the MessageBus cross a WebWorker boundary, they must be serializable.
+If you use the MessageBus directly, you are responsible for serializing your messages.
+In JavaScript / TypeScript this means they must be serializable via JavaScript's
+[structured cloning algorithim](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
+
+In Dart this means they must be valid messages that can be passed through a
+[SendPort](https://api.dartlang.org/1.12.1/dart-isolate/SendPort/send.html).
+
+
+### MessageBus and Zones
+The MessageBus API includes support for [zones](http://www.github.com/angular/zone.js).
+A MessageBus can be attached to a specific zone (by calling `attachToZone`). Then specific channels can be
+specified to run in the zone when they are initialized.
+If a channel is running in the zone, that means that any events emitted from that channel will be executed within
+the given zone. For example, by default angular runs the EventDispatch channel inside the angular zone. That means
+when an event is fired from the DOM and received on the WebWorker the event handler automatically runs inside the
+angular zone. This is desired because after the event handler exits we want to exit the zone so that we trigger
+change detection. Generally, you want your channels to run inside the zone unless you have a good reason for why
+they need to run outside the zone.
+
+### Implementing and Using a Custom MessageBus
+**Note:** Implementing and using a Custom MessageBus is experimental and the APIs may change.
+
+If you want to drive your application from something other than a WebWorker you can implement a custom message
+bus. Implementing a custom message bus just means creating a class that fulfills the API specified by the
+abstract MessageBus class.
+
+If you're implementing your MessageBus in Dart you can extend the `GenericMessageBus` class included in angular.
+if you do this, you don't need to implement zone or channel support yourself. You only need to implement a
+`MessageBusSink` that extends `GenericMessageBusSink` and a `MessageBusSource` that extends
+`GenericMessageBusSource`. The `MessageBusSink` must override the `sendMessages` method. This method is
+given a list of serialized messages that it is required to send through the sink.
+The `MessageBusSource` needs to provide a [Stream](https://api.dartlang.org/1.12.1/dart-async/Stream-class.html)
+of incoming messages (either by passing the stream to `GenericMessageBusSource's` constructor or by calling
+attachTo() with the stream). It also needs to override the abstract `decodeMessages` method. This method is
+given a List of serialized messages received by the source and should perform any decoding work that needs to be
+done before the application can read the messages.
+
+For example, if your MessageBus sends and receives JSON data you would do the following:
+```Dart
+import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
+import 'dart:convert';
+
+class JsonMessageBusSink extends GenericMessageBusSink {
+ @override
+ void sendMessages(List messages) {
+ String encodedMessages = JSON.encode(messages);
+ // Send encodedMessages here
+ }
+}
+
+class JsonMessageBusSource extends GenericMessageBuSource {
+ JsonMessageBusSource(Stream incomingMessages) : super (incomingMessages);
+
+ @override
+ List decodeMessages(dynamic messages) {
+ return JSON.decode(messages);
+ }
+}
+```
+
+Once you've implemented your custom MessageBus in either TypeScript or Dart, you must provide it through DI
+during bootstrap like so:
+
+In TypeScript:
+```TypeScript
+// index.ts, running on the UI side
+import {platform, Provider, APP_INITIALIZER, Injector} from 'angular2/core';
+import {
+ WORKER_RENDER_PLATFORM,
+ WORKER_RENDER_APP_COMMON,
+ initializeGenericWorkerRenderer,
+ MessageBus
+} from 'angular2/platform/worker_render';
+
+var bus = new MyAwesomeMessageBus();
+platform([WORKER_RENDER_PLATFORM])
+.application([WORKER_RENDER_APP_COMMON, new Provider(MessageBus, {useValue: bus}),
+ new Provider(APP_INITIALIZER, {
+ useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
+ deps: [Injector],
+ multi: true
+ })
+]);
+```
+```TypeScript
+// background_index.ts, running on the application side
+import {WORKER_APP_PLATFORM, genericWorkerAppProviders} from "angular2/platform/worker_app";
+import {NgZone, platform, Provider} from "angular/core";
+import {MyApp} from './app';
+
+/**
+ * Do initialization work here to set up the app thread and MessageBus;
+ * Once you have a working MessageBus you should bootstrap your app.
+ */
+
+platform([WORKER_APP_PLATFORM_PROVIDERS])
+.application([WORKER_APP_APPLICATION_COMMON, new Provider(MessageBus, {useValue: bus}),
+new Provider(APP_INITIALIZER, {useFactory: (zone, bus) => () => initAppThread(zone, bus), multi: true, deps: [NgZone, MessageBus]})])
+.bootstrap(MyApp);
+
+function initAppThread(zone: NgZone, bus: MyAwesomeMessageBus): void{
+ /**
+ * Here you can do any initilization work that requires the app providers to be initialized.
+ * At a minimum, you must attach your bus to the zone and setup a DOM adapter.
+ * Depending on your environment you may choose to do more work here.
+ */
+}
+```
+In Dart:
+```Dart
+// index.dart, running on the UI side
+import 'package:angular2/core.dart';
+import 'package:angular2/platform/worker_render.dart';
+
+main() {
+ var bus = new MyAwesomeMessageBus();
+ platform([WORKER_RENDER_PLATFORM])
+ .application([WORKER_RENDER_APP_COMMON, new Provider(MessageBus, useValue: bus),
+ new Provider(APP_INITIALIZER,
+ useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
+ deps: [Injector],
+ multi: true
+ )
+ ]);
+}
+
+```
+```Dart
+// background_index.dart, running on the application side
+import "package:angular2/platform/worker_app.dart";
+import "package:angular2/core.dart";
+import "./app.dart" show MyApp;
+
+main() {
+ /**
+ * Do initialization work here to set up the app thread and MessageBus;
+ * Once you have a working MessageBus you should bootstrap your app.
+ */
+ reflector.reflectionCapabilities = new ReflectionCapabilities();
+ platform([WORKER_APP_PLATFORM_PROVIDERS])
+ .application([WORKER_APP_APPLICATION_COMMON, new Provider(MessageBus, useValue: bus),
+new Provider(APP_INITIALIZER, useFactory: (zone, bus) => () => initAppThread(zone, bus), multi: true, deps: [NgZone, MessageBus])])
+ .bootstrap(MyApp);
+}
+
+
+void initAppThread(NgZone zone) {
+ /**
+ * Here you can do any initilization work that requires the app providers to be initialized.
+ * At a minimum, you must attach your bus to the zone and setup a DOM adapter.
+ * Depending on your environment you may choose to do more work here.
+ */
+}
+```
+Notice how we use the `WORKER_RENDER_APP_COMMON` providers instead of the `WORKER_RENDER_APP` providers on the render thread.
+This is because the `WORKER_RENDER_APP` providers include an application initializer that starts a new WebWorker/Isolate.
+The `WORKER_RENDER_APP_COMMON` providers make no assumption about where your application code lives.
+However, we now need to provide our own app initializer. At the very least this initializer needs to call `initializeGenericWorkerRenderer`.
+
+## MessageBroker
+The MessageBroker is a higher level messaging abstraction that sits on top of the MessageBus. It is used when you
+want to execute code on the other side of a runtime boundary and may want to receive the result.
+There are two types of MessageBrokers:
+
+1. The `ServiceMessageBroker` is used by the side that actually performs
+an operation and may return a result;
+2. The `ClientMessageBroker` is used by the side that requests that
+an operation be performed and may want to receive the result.
+
+### Using the MessageBroker In Your Application
+To use MessageBrokers in your application you must initialize both a `ClientMessageBroker` and a
+`ServiceMessageBroker` on the same channel. You can then register methods with the `ServiceMessageBroker` and
+instruct the `ClientMessageBroker` to run those methods. Below is a lightweight example of using
+MessageBrokers in an application. For a more complete example, check out the `WebWorkerRenderer` and
+`MessageBasedRenderer` inside the Angular WebWorker code.
+
+#### Using the MessageBroker in TypeScript
+```TypeScript
+// index.ts, which is running on the UI with a method that we want to expose to a WebWorker
+import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, ServiceMessageBrokerFactory} from "angular2/platform/worker_render";
+import {platform} from "angular2/core";
+
+let appRef = platform([WORKER_RENDER_PLATFORM])
+.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
+let injector = instance.injector;
+var broker = injector.get(ServiceMessageBrokerFactory).createMessageBroker("My Broker Channel");
+
+// assume we have some function doCoolThings that takes a string argument and returns a Promise
+broker.registerMethod("awesomeMethod", [PRIMITIVE], (arg1: string) => doCoolThing(arg1), PRIMITIVE);
+```
+```TypeScript
+// background.ts, which is running on a WebWorker and wants to execute a method on the UI
+import {Component, View} from 'angular2/core';
+import {ClientMessageBrokerFactory, PRIMITIVE, UiArguments, FnArgs} from 'angular2/platform/worker_app';
+
+@Component(...)
+@View(...)
+export class MyComponent {
+ constructor(brokerFactory: ClientMessageBrokerFactory) {
+ var broker = brokerFactory.createMessageBroker("My Broker Channel");
+
+ var arguments = [new FnArg(value, PRIMITIVE)];
+ var methodInfo = new UiArguments("awesomeMethod", arguments);
+ broker.runOnService(methodInfo, PRIMTIVE).then((result: string) => {
+ // result will be equal to the return value of doCoolThing(value) that ran on the UI.
+ });
+ }
+}
+```
+#### Using the MessageBroker in Dart
+```Dart
+// index.dart, which is running on the UI with a method that we want to expose to a WebWorker
+import "angular2/core.dart";
+import "angular2/platform/worker_render.dart";
+
+main() {
+ platform([WORKER_RENDER_PLATFORM])
+ .asyncApplication(initIsolate("my_worker.dart")).then((ref) {
+ var broker = ref.injector.get(ServiceMessageBrokerFactory).createMessageBroker("My Broker Channel");
+
+ // assume we have some function doCoolThings that takes a String argument and returns a Future
+ broker.registerMethod("awesomeMethod", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);
+ });
+}
+
+```
+```Dart
+// background.dart, which is running on a WebWorker and wants to execute a method on the UI
+import 'package:angular2/core.dart';
+import 'package:angular2/platform/worker_app.dart';
+
+@Component(...)
+@View(...)
+class MyComponent {
+ MyComponent(ClientMessageBrokerFactory brokerFactory) {
+ var broker = brokerFactory.createMessageBroker("My Broker Channel");
+
+ var arguments = [new FnArg(value, PRIMITIVE)];
+ var methodInfo = new UiArguments("awesomeMethod", arguments);
+ broker.runOnService(methodInfo, PRIMTIVE).then((String result) {
+ // result will be equal to the return value of doCoolThing(value) that ran on the UI.
+ });
+ }
+}
+```
+Both the client and the service create new MessageBrokers and attach them to the same channel.
+The service then calls `registerMethod` to register the method that it wants to listen to. Register method takes
+four arguments. The first is the name of the method, the second is the Types of that method's parameters, the
+third is the method itself, and the fourth (which is optional) is the return Type of that method.
+The MessageBroker handles serializing / deserializing your parameters and return types using angular's serializer.
+However, at the moment the serializer only knows how to serialize angular classes like those used by the Renderer.
+If you're passing anything other than those types around in your application you can handle serialization yourself
+and then use the `PRIMITIVE` type to tell the MessageBroker to avoid serializing your data.
+
+The last thing that happens is that the client calls `runOnService` with the name of the method it wants to run,
+a list of that method's arguments and their types, and (optionally) the expected return type.
diff --git a/modules/angular2/examples/README.md b/modules/angular2/examples/README.md
new file mode 100644
index 000000000000..855bc0b7a3ed
--- /dev/null
+++ b/modules/angular2/examples/README.md
@@ -0,0 +1,6 @@
+# API Examples
+
+This folder contains small example apps that get in-lined into our API docs.
+These examples are written with idiomatic TypeScript, and are not transpiled to Dart.
+Each example contains tests for application behavior (as opposed to testing Angular's
+behavior) just like an Angular application developer would write.
diff --git a/modules/angular2/examples/animate/ts/.gitkeep b/modules/angular2/examples/animate/ts/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/angular2/examples/common/forms/ts/validators/validators.ts b/modules/angular2/examples/common/forms/ts/validators/validators.ts
new file mode 100644
index 000000000000..6744c8b5ed92
--- /dev/null
+++ b/modules/angular2/examples/common/forms/ts/validators/validators.ts
@@ -0,0 +1,30 @@
+import {Component} from 'angular2/core';
+import {MinLengthValidator, MaxLengthValidator} from 'angular2/common';
+
+// #docregion min
+@Component({
+ selector: 'min-cmp',
+ directives: [MinLengthValidator],
+ template: `
+
+`
+})
+class MinLengthTestComponent {
+}
+// #enddocregion
+
+// #docregion max
+@Component({
+ selector: 'max-cmp',
+ directives: [MaxLengthValidator],
+ template: `
+
+`
+})
+class MaxLengthTestComponent {
+}
+// #enddocregion
diff --git a/modules/angular2/examples/compiler/ts/url_resolver/url_resolver.ts b/modules/angular2/examples/compiler/ts/url_resolver/url_resolver.ts
new file mode 100644
index 000000000000..3910a38a1a82
--- /dev/null
+++ b/modules/angular2/examples/compiler/ts/url_resolver/url_resolver.ts
@@ -0,0 +1,19 @@
+import {provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {UrlResolver} from 'angular2/compiler';
+
+var MyApp;
+
+// #docregion url_resolver
+class MyUrlResolver extends UrlResolver {
+ resolve(baseUrl: string, url: string): string {
+ // Serve CSS files from a special CDN.
+ if (url.substr(-4) === '.css') {
+ return super.resolve('http://cdn.myapp.com/css/', url);
+ }
+ return super.resolve(baseUrl, url);
+ }
+}
+
+bootstrap(MyApp, [provide(UrlResolver, {useClass: MyUrlResolver})]);
+// #enddocregion
diff --git a/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts b/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts
new file mode 100644
index 000000000000..ebda75d05c34
--- /dev/null
+++ b/modules/angular2/examples/core/debug/ts/debug_element/debug_element.ts
@@ -0,0 +1,16 @@
+import {DebugElement, Scope} from 'angular2/core';
+
+var debugElement: DebugElement;
+var predicate;
+
+// #docregion scope_all
+debugElement.query(predicate, Scope.all);
+// #enddocregion
+
+// #docregion scope_light
+debugElement.query(predicate, Scope.light);
+// #enddocregion
+
+// #docregion scope_view
+debugElement.query(predicate, Scope.view);
+// #enddocregion
diff --git a/modules/angular2/examples/core/di/ts/forward_ref/forward_ref.ts b/modules/angular2/examples/core/di/ts/forward_ref/forward_ref.ts
new file mode 100644
index 000000000000..a9b524b80fca
--- /dev/null
+++ b/modules/angular2/examples/core/di/ts/forward_ref/forward_ref.ts
@@ -0,0 +1,26 @@
+import {Inject, Injector, forwardRef, resolveForwardRef, ForwardRefFn} from 'angular2/core';
+
+// #docregion forward_ref_fn
+var ref = forwardRef(() => Lock);
+// #enddocregion
+
+// #docregion forward_ref
+class Door {
+ lock: Lock;
+ constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; }
+}
+
+// Only at this point Lock is defined.
+class Lock {}
+
+var injector = Injector.resolveAndCreate([Door, Lock]);
+var door = injector.get(Door);
+expect(door instanceof Door).toBe(true);
+expect(door.lock instanceof Lock).toBe(true);
+// #enddocregion
+
+// #docregion resolve_forward_ref
+var ref = forwardRef(() => "refValue");
+expect(resolveForwardRef(ref)).toEqual("refValue");
+expect(resolveForwardRef("regularValue")).toEqual("regularValue");
+// #enddocregion
\ No newline at end of file
diff --git a/modules/angular2/examples/core/forms/ts/ng_validators/ng_validators.ts b/modules/angular2/examples/core/forms/ts/ng_validators/ng_validators.ts
new file mode 100644
index 000000000000..e0206403bad0
--- /dev/null
+++ b/modules/angular2/examples/core/forms/ts/ng_validators/ng_validators.ts
@@ -0,0 +1,10 @@
+import {bootstrap} from 'angular2/bootstrap';
+import {NG_VALIDATORS} from 'angular2/common';
+import {Provider} from 'angular2/core';
+
+let MyApp = null;
+let myValidator = null;
+
+// #docregion ng_validators
+bootstrap(MyApp, [new Provider(NG_VALIDATORS, {useValue: myValidator, multi: true})]);
+// #enddocregion
diff --git a/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.dart b/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.dart
new file mode 100644
index 000000000000..2bc22b2b2a1e
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.dart
@@ -0,0 +1,2 @@
+library angular2.examples.core.pipes.ts.async_pipe;
+// TODO(alxhub): Implement an example for Dart.
\ No newline at end of file
diff --git a/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.ts
new file mode 100644
index 000000000000..879ff08455a5
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/async_pipe/async_pipe_example.ts
@@ -0,0 +1,58 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {Observable} from 'rxjs/Observable';
+
+// #docregion AsyncPipe
+@Component({
+ selector: 'async-example',
+ template: `
+
Wait for it... {{ greeting | async }}
+
{{ arrived ? 'Reset' : 'Resolve' }}
+
`
+})
+export class AsyncPipeExample {
+ greeting: Promise = null;
+ arrived: boolean = false;
+
+ private resolve: Function = null;
+
+ constructor() { this.reset(); }
+
+ reset() {
+ this.arrived = false;
+ this.greeting = new Promise((resolve, reject) => { this.resolve = resolve; });
+ }
+
+ clicked() {
+ if (this.arrived) {
+ this.reset();
+ } else {
+ this.resolve("hi there!");
+ this.arrived = true;
+ }
+ }
+}
+// #enddocregion
+
+// #docregion AsyncPipeObservable
+@Component({selector: "task-cmp", template: "Time: {{ time | async }}"})
+class Task {
+ time = new Observable(
+ observer => { setInterval(_ => observer.next(new Date().getTime()), 500); });
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [AsyncPipeExample],
+ template: `
+ AsyncPipe Example
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/pipes/ts/async_pipe/index.html b/modules/angular2/examples/core/pipes/ts/async_pipe/index.html
new file mode 100644
index 000000000000..3b4b0e30c7ac
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/async_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ AsyncPipe Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.dart b/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.dart
new file mode 100644
index 000000000000..40832aac0623
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.dart
@@ -0,0 +1,2 @@
+library angular2.examples.core.pipes.ts.date_pipe;
+// TODO(alxhub): Implement an example for Dart.
diff --git a/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.ts
new file mode 100644
index 000000000000..5523dfdcc865
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/date_pipe/date_pipe_example.ts
@@ -0,0 +1,31 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+
+// #docregion DatePipe
+@Component({
+ selector: 'date-example',
+ template: `
+
Today is {{today | date}}
+
Or if you prefer, {{today | date:'fullDate'}}
+
The time is {{today | date:'jmZ'}}
+
`
+})
+export class DatePipeExample {
+ today: number = Date.now();
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [DatePipeExample],
+ template: `
+ DatePipe Example
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/pipes/ts/date_pipe/index.html b/modules/angular2/examples/core/pipes/ts/date_pipe/index.html
new file mode 100644
index 000000000000..43ccdf1337c7
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/date_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ DatePipe Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/json_pipe/index.html b/modules/angular2/examples/core/pipes/ts/json_pipe/index.html
new file mode 100644
index 000000000000..4d4ece7ab143
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/json_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ JsonPipe Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/json_pipe/json_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/json_pipe/json_pipe_example.ts
new file mode 100644
index 000000000000..8d9a62614338
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/json_pipe/json_pipe_example.ts
@@ -0,0 +1,32 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+
+// #docregion JsonPipe
+@Component({
+ selector: 'json-example',
+ template: `
+
Without JSON pipe:
+
{{object}}
+
With JSON pipe:
+
{{object | json}}
+
`
+})
+export class JsonPipeExample {
+ object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}}
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [JsonPipeExample],
+ template: `
+ JsonPipe Example
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/index.html b/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/index.html
new file mode 100644
index 000000000000..74d5242b7110
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ LowercasePipe & UppercasePipe Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts
new file mode 100644
index 000000000000..2d891111a1d1
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts
@@ -0,0 +1,32 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+
+// #docregion LowerUpperPipe
+@Component({
+ selector: 'lowerupper-example',
+ template: `
+
Name:
+
In lowercase:
'{{value | lowercase}}'
+
In uppercase:
'{{value | uppercase}}'
+
`
+})
+export class LowerUpperPipeExample {
+ value: string;
+ change(value) { this.value = value; }
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [LowerUpperPipeExample],
+ template: `
+ LowercasePipe & UppercasePipe Example
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/pipes/ts/number_pipe/index.html b/modules/angular2/examples/core/pipes/ts/number_pipe/index.html
new file mode 100644
index 000000000000..c208aeb66191
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/number_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Numeric Pipe Examples
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/number_pipe/number_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/number_pipe/number_pipe_example.ts
new file mode 100644
index 000000000000..08f1b4efb0b0
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/number_pipe/number_pipe_example.ts
@@ -0,0 +1,66 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+
+// #docregion NumberPipe
+@Component({
+ selector: 'number-example',
+ template: `
+
e (no formatting): {{e}}
+
e (3.1-5): {{e | number:'3.1-5'}}
+
pi (no formatting): {{pi}}
+
pi (3.5-5): {{pi | number:'3.5-5'}}
+
`
+})
+export class NumberPipeExample {
+ pi: number = 3.141;
+ e: number = 2.718281828459045;
+}
+// #enddocregion
+
+// #docregion PercentPipe
+@Component({
+ selector: 'percent-example',
+ template: `
+
A: {{a | percent}}
+
B: {{b | percent:'4.3-5'}}
+
`
+})
+export class PercentPipeExample {
+ a: number = 0.259;
+ b: number = 1.3495;
+}
+// #enddocregion
+
+// #docregion CurrencyPipe
+@Component({
+ selector: 'currency-example',
+ template: `
+
A: {{a | currency:'USD':false}}
+
B: {{b | currency:'USD':true:'4.2-2'}}
+
`
+})
+export class CurrencyPipeExample {
+ a: number = 0.259;
+ b: number = 1.3495;
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [NumberPipeExample, PercentPipeExample, CurrencyPipeExample],
+ template: `
+ Numeric Pipe Examples
+ NumberPipe Example
+
+ PercentPipe Example
+
+ CurrencyPipeExample
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/pipes/ts/slice_pipe/index.html b/modules/angular2/examples/core/pipes/ts/slice_pipe/index.html
new file mode 100644
index 000000000000..e77619bebfc7
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/slice_pipe/index.html
@@ -0,0 +1,24 @@
+
+
+
+ SlicePipe Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/core/pipes/ts/slice_pipe/slice_pipe_example.ts b/modules/angular2/examples/core/pipes/ts/slice_pipe/slice_pipe_example.ts
new file mode 100644
index 000000000000..2504b9da2f9a
--- /dev/null
+++ b/modules/angular2/examples/core/pipes/ts/slice_pipe/slice_pipe_example.ts
@@ -0,0 +1,47 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+
+// #docregion SlicePipe_string
+@Component({
+ selector: 'slice-string-example',
+ template: `
+
{{str}}[0:4]: '{{str | slice:0:4}}' - output is expected to be 'abcd'
+
{{str}}[4:0]: '{{str | slice:4:0}}' - output is expected to be ''
+
{{str}}[-4]: '{{str | slice:-4}}' - output is expected to be 'ghij'
+
{{str}}[-4:-2]: '{{str | slice:-4:-2}}' - output is expected to be 'gh'
+
{{str}}[-100]: '{{str | slice:-100}}' - output is expected to be 'abcdefghij'
+
{{str}}[100]: '{{str | slice:100}}' - output is expected to be ''
+
`
+})
+export class SlicePipeStringExample {
+ str: string = 'abcdefghij';
+}
+// #enddocregion
+
+// #docregion SlicePipe_list
+@Component({
+ selector: 'slice-list-example',
+ template: `
+
{{i}}
+
`
+})
+export class SlicePipeListExample {
+ collection: string[] = ['a', 'b', 'c', 'd'];
+}
+// #enddocregion
+
+@Component({
+ selector: 'example-app',
+ directives: [SlicePipeListExample, SlicePipeStringExample],
+ template: `
+ SlicePipe Examples
+
+
+ `
+})
+export class AppCmp {
+}
+
+export function main() {
+ bootstrap(AppCmp);
+}
diff --git a/modules/angular2/examples/core/ts/.gitkeep b/modules/angular2/examples/core/ts/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/angular2/examples/core/ts/bootstrap/bootstrap.ts b/modules/angular2/examples/core/ts/bootstrap/bootstrap.ts
new file mode 100644
index 000000000000..1e999bf76409
--- /dev/null
+++ b/modules/angular2/examples/core/ts/bootstrap/bootstrap.ts
@@ -0,0 +1,13 @@
+import {Component} from 'angular2/core';
+import {bootstrap} from 'angular2/platform/browser';
+
+// #docregion bootstrap
+@Component({selector: 'my-app', template: 'Hello {{ name }}!'})
+class MyApp {
+ name: string = 'World';
+}
+
+function main() {
+ return bootstrap(MyApp);
+}
+// #enddocregion
diff --git a/modules/angular2/examples/core/ts/metadata/metadata.ts b/modules/angular2/examples/core/ts/metadata/metadata.ts
new file mode 100644
index 000000000000..0924ad4e8daf
--- /dev/null
+++ b/modules/angular2/examples/core/ts/metadata/metadata.ts
@@ -0,0 +1,43 @@
+import {Component, Attribute, Directive, Pipe} from 'angular2/core';
+
+var CustomDirective;
+
+// #docregion component
+@Component({selector: 'greet', template: 'Hello {{name}}!', directives: [CustomDirective]})
+class Greet {
+ name: string = 'World';
+}
+// #enddocregion
+
+// #docregion attributeFactory
+@Component({selector: 'page', template: 'Title: {{title}}'})
+class Page {
+ title: string;
+ constructor(@Attribute('title') title: string) { this.title = title; }
+}
+// #enddocregion
+
+// #docregion attributeMetadata
+@Directive({selector: 'input'})
+class InputAttrDirective {
+ constructor(@Attribute('type') type) {
+ // type would be 'text' in this example
+ }
+}
+// #enddocregion
+
+// #docregion directive
+@Directive({selector: 'input'})
+class InputDirective {
+ constructor() {
+ // Add some logic.
+ }
+}
+// #enddocregion
+
+// #docregion pipe
+@Pipe({name: 'lowercase'})
+class Lowercase {
+ transform(v, args) { return v.toLowerCase(); }
+}
+// #enddocregion
diff --git a/modules/angular2/examples/core/ts/platform/platform.ts b/modules/angular2/examples/core/ts/platform/platform.ts
new file mode 100644
index 000000000000..98a916c9d3ec
--- /dev/null
+++ b/modules/angular2/examples/core/ts/platform/platform.ts
@@ -0,0 +1,13 @@
+import {Component, platform} from 'angular2/core';
+import {BROWSER_PROVIDERS, BROWSER_APP_PROVIDERS} from 'angular2/platform/browser';
+
+var appProviders: any[] = [];
+
+// #docregion longform
+@Component({selector: 'my-app', template: 'Hello World'})
+class MyApp {
+}
+
+var app = platform(BROWSER_PROVIDERS).application([BROWSER_APP_PROVIDERS, appProviders]);
+app.bootstrap(MyApp);
+// #enddocregion
diff --git a/modules/angular2/examples/core/ts/prod_mode/my_component.ts b/modules/angular2/examples/core/ts/prod_mode/my_component.ts
new file mode 100644
index 000000000000..e265b80648d3
--- /dev/null
+++ b/modules/angular2/examples/core/ts/prod_mode/my_component.ts
@@ -0,0 +1,5 @@
+import {Component} from 'angular2/core';
+
+@Component({selector: 'my-component', template: 'My Component '})
+export class MyComponent {
+}
diff --git a/modules/angular2/examples/core/ts/prod_mode/prod_mode_example.ts b/modules/angular2/examples/core/ts/prod_mode/prod_mode_example.ts
new file mode 100644
index 000000000000..76c3e15f4afa
--- /dev/null
+++ b/modules/angular2/examples/core/ts/prod_mode/prod_mode_example.ts
@@ -0,0 +1,8 @@
+// #docregion enableProdMode
+import {enableProdMode} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {MyComponent} from 'my_component';
+
+enableProdMode();
+bootstrap(MyComponent);
+// #enddocregion
diff --git a/modules/angular2/examples/facade/ts/async/observable.ts b/modules/angular2/examples/facade/ts/async/observable.ts
new file mode 100644
index 000000000000..56ce718a74fc
--- /dev/null
+++ b/modules/angular2/examples/facade/ts/async/observable.ts
@@ -0,0 +1,8 @@
+// #docregion Observable
+import {Observable} from 'rxjs/Observable';
+var obs = new Observable(obs => {
+ var i = 0;
+ setInterval(_ => { obs.next(++i); }, 1000);
+});
+obs.subscribe(i => console.log(`${i} seconds elapsed`));
+// #enddocregion
diff --git a/modules/angular2/examples/facade/ts/async/observable_all.ts b/modules/angular2/examples/facade/ts/async/observable_all.ts
new file mode 100644
index 000000000000..6c1b2ae1c0dc
--- /dev/null
+++ b/modules/angular2/examples/facade/ts/async/observable_all.ts
@@ -0,0 +1,3 @@
+// #docregion Observable
+import 'rxjs';
+// #enddocregion
diff --git a/modules/angular2/examples/facade/ts/async/observable_patched.ts b/modules/angular2/examples/facade/ts/async/observable_patched.ts
new file mode 100644
index 000000000000..b63ae01e8887
--- /dev/null
+++ b/modules/angular2/examples/facade/ts/async/observable_patched.ts
@@ -0,0 +1,10 @@
+// #docregion Observable
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+
+var obs = new Observable(obs => {
+ var i = 0;
+ setInterval(_ => obs.next(++i), 1000);
+});
+obs.map(i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
+// #enddocregion
diff --git a/modules/angular2/examples/facade/ts/async/observable_pure.ts b/modules/angular2/examples/facade/ts/async/observable_pure.ts
new file mode 100644
index 000000000000..a73bfb4d0b5c
--- /dev/null
+++ b/modules/angular2/examples/facade/ts/async/observable_pure.ts
@@ -0,0 +1,10 @@
+// #docregion Observable
+import {Observable} from 'rxjs/Observable';
+import {map} from 'rxjs/operator/map';
+
+var obs = new Observable(obs => {
+ var i = 0;
+ setInterval(_ => obs.next(++i), 1000);
+});
+map.call(obs, i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
+// #enddocregion
diff --git a/modules/angular2/examples/http/ts/.gitkeep b/modules/angular2/examples/http/ts/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/angular2/examples/platform/dom/debug/ts/by/by.ts b/modules/angular2/examples/platform/dom/debug/ts/by/by.ts
new file mode 100644
index 000000000000..58c888328cc4
--- /dev/null
+++ b/modules/angular2/examples/platform/dom/debug/ts/by/by.ts
@@ -0,0 +1,17 @@
+import {By} from 'angular2/platform/browser';
+import {DebugElement, Scope} from 'angular2/core';
+
+var debugElement: DebugElement;
+class MyDirective {}
+
+// #docregion by_all
+debugElement.query(By.all(), Scope.all);
+// #enddocregion
+
+// #docregion by_css
+debugElement.query(By.css('[attribute]'), Scope.all);
+// #enddocregion
+
+// #docregion by_directive
+debugElement.query(By.directive(MyDirective), Scope.all);
+// #enddocregion
diff --git a/modules/angular2/examples/platform/dom/debug/ts/debug_element_view_listener/providers.ts b/modules/angular2/examples/platform/dom/debug/ts/debug_element_view_listener/providers.ts
new file mode 100644
index 000000000000..b9dec70aaeac
--- /dev/null
+++ b/modules/angular2/examples/platform/dom/debug/ts/debug_element_view_listener/providers.ts
@@ -0,0 +1,10 @@
+import {Component} from 'angular2/core';
+import {bootstrap, ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/browser';
+
+@Component({selector: 'my-component'})
+class MyAppComponent {
+}
+
+// #docregion providers
+bootstrap(MyAppComponent, [ELEMENT_PROBE_PROVIDERS]);
+// #enddocregion
\ No newline at end of file
diff --git a/modules/angular2/examples/router/ts/.gitkeep b/modules/angular2/examples/router/ts/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/angular2/examples/router/ts/can_activate/can_activate_example.ts b/modules/angular2/examples/router/ts/can_activate/can_activate_example.ts
new file mode 100644
index 000000000000..cd0ccc66e2df
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_activate/can_activate_example.ts
@@ -0,0 +1,57 @@
+import {provide, Component} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {
+ CanActivate,
+ RouteConfig,
+ ComponentInstruction,
+ APP_BASE_HREF,
+ ROUTER_DIRECTIVES
+} from 'angular2/router';
+
+function checkIfWeHavePermission(instruction: ComponentInstruction) {
+ return instruction.params['id'] == '1';
+}
+
+// #docregion canActivate
+@Component({selector: 'control-panel-cmp', template: `Settings: ...
`})
+@CanActivate(checkIfWeHavePermission)
+class ControlPanelCmp {
+}
+// #enddocregion
+
+
+@Component({
+ selector: 'home-cmp',
+ template: `
+ Welcome Home!
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+class HomeCmp {
+}
+
+
+@Component({
+ selector: 'example-app',
+ template: `
+ My App
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+@RouteConfig([
+ {path: '/user-settings/:id', component: ControlPanelCmp, name: 'ControlPanelCmp'},
+ {path: '/', component: HomeCmp, name: 'HomeCmp'}
+])
+class AppCmp {
+}
+
+
+export function main() {
+ return bootstrap(
+ AppCmp, [provide(APP_BASE_HREF, {useValue: '/angular2/examples/router/ts/can_activate'})]);
+}
diff --git a/modules/angular2/examples/router/ts/can_activate/can_activate_spec.ts b/modules/angular2/examples/router/ts/can_activate/can_activate_spec.ts
new file mode 100644
index 000000000000..3f5728bb7afa
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_activate/can_activate_spec.ts
@@ -0,0 +1,36 @@
+import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
+import {Promise} from 'angular2/src/facade/async';
+
+function waitForElement(selector) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
+
+describe('reuse example app', function() {
+
+ afterEach(verifyNoBrowserErrors);
+
+ var URL = 'angular2/examples/router/ts/can_activate/';
+
+ it('should navigate to user 1', function() {
+ browser.get(URL);
+ waitForElement('home-cmp');
+
+ element(by.css('#user-1-link')).click();
+ waitForElement('control-panel-cmp');
+ expect(browser.getCurrentUrl()).toMatch(/\/user-settings\/1$/);
+
+ expect(element(by.css('control-panel-cmp')).getText()).toContain('Settings');
+ });
+
+ it('should not navigate to user 2', function() {
+ browser.get(URL);
+ waitForElement('home-cmp');
+
+ element(by.css('#user-2-link')).click();
+ waitForElement('home-cmp');
+
+ expect(element(by.css('home-cmp')).getText()).toContain('Welcome Home!');
+ });
+});
diff --git a/modules/angular2/examples/router/ts/can_activate/index.html b/modules/angular2/examples/router/ts/can_activate/index.html
new file mode 100644
index 000000000000..13c8bdab8493
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_activate/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Routing canActivate Lifecycle Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_example.ts b/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_example.ts
new file mode 100644
index 000000000000..41de37b4b9b8
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_example.ts
@@ -0,0 +1,67 @@
+import {provide, Component} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {
+ CanDeactivate,
+ RouteConfig,
+ RouteParams,
+ ComponentInstruction,
+ ROUTER_DIRECTIVES,
+ APP_BASE_HREF
+} from 'angular2/router';
+
+// #docregion routerCanDeactivate
+@Component({
+ selector: 'note-cmp',
+ template: `
+
+
id: {{id}}
+
+ `
+})
+class NoteCmp implements CanDeactivate {
+ id: string;
+
+ constructor(params: RouteParams) { this.id = params.get('id'); }
+
+ routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
+ return confirm('Are you sure you want to leave?');
+ }
+}
+// #enddocregion
+
+
+@Component({
+ selector: 'note-index-cmp',
+ template: `
+ Your Notes
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+class NoteIndexCmp {
+}
+
+
+@Component({
+ selector: 'example-app',
+ template: `
+ My App
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+@RouteConfig([
+ {path: '/note/:id', component: NoteCmp, name: 'NoteCmp'},
+ {path: '/', component: NoteIndexCmp, name: 'NoteIndexCmp'}
+])
+class AppCmp {
+}
+
+
+export function main() {
+ return bootstrap(
+ AppCmp, [provide(APP_BASE_HREF, {useValue: '/angular2/examples/router/ts/can_deactivate'})]);
+}
diff --git a/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_spec.ts b/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_spec.ts
new file mode 100644
index 000000000000..b92fd26332fc
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_deactivate/can_deactivate_spec.ts
@@ -0,0 +1,52 @@
+import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
+import {Promise} from 'angular2/src/facade/async';
+
+function waitForElement(selector) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
+
+function waitForAlert() {
+ var EC = (protractor).ExpectedConditions;
+ browser.wait(EC.alertIsPresent(), 1000);
+}
+
+describe('can deactivate example app', function() {
+
+ afterEach(verifyNoBrowserErrors);
+
+ var URL = 'angular2/examples/router/ts/can_deactivate/';
+
+ it('should not navigate away when prompt is cancelled', function() {
+ browser.get(URL);
+ waitForElement('note-index-cmp');
+
+ element(by.css('#note-1-link')).click();
+ waitForElement('note-cmp');
+
+ browser.navigate().back();
+ waitForAlert();
+
+ browser.switchTo().alert().dismiss(); // Use to simulate cancel button
+
+ expect(element(by.css('note-cmp')).getText()).toContain('id: 1');
+ });
+
+ it('should navigate away when prompt is confirmed', function() {
+ browser.get(URL);
+ waitForElement('note-index-cmp');
+
+ element(by.css('#note-1-link')).click();
+ waitForElement('note-cmp');
+
+ browser.navigate().back();
+ waitForAlert();
+
+ browser.switchTo().alert().accept();
+
+ waitForElement('note-index-cmp');
+
+ expect(element(by.css('note-index-cmp')).getText()).toContain('Your Notes');
+ });
+});
diff --git a/modules/angular2/examples/router/ts/can_deactivate/index.html b/modules/angular2/examples/router/ts/can_deactivate/index.html
new file mode 100644
index 000000000000..05a7324d38d4
--- /dev/null
+++ b/modules/angular2/examples/router/ts/can_deactivate/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Routing routerCanDeactivate Lifecycle Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/router/ts/on_activate/index.html b/modules/angular2/examples/router/ts/on_activate/index.html
new file mode 100644
index 000000000000..12badf9ade7f
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_activate/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Routing Reuse Lifecycle Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/router/ts/on_activate/on_activate_example.ts b/modules/angular2/examples/router/ts/on_activate/on_activate_example.ts
new file mode 100644
index 000000000000..2a0451391221
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_activate/on_activate_example.ts
@@ -0,0 +1,47 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {
+ OnActivate,
+ ComponentInstruction,
+ RouteConfig,
+ ROUTER_DIRECTIVES,
+ APP_BASE_HREF
+} from 'angular2/router';
+
+
+// #docregion routerOnActivate
+@Component({selector: 'my-cmp', template: `routerOnActivate: {{log}}
`})
+class MyCmp implements OnActivate {
+ log: string = '';
+
+ routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) {
+ this.log = `Finished navigating from "${prev ? prev.urlPath : 'null'}" to "${next.urlPath}"`;
+ }
+}
+// #enddocregion
+
+
+@Component({
+ selector: 'example-app',
+ template: `
+ My App
+
+ Navigate Home |
+ Navigate with a Param
+
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+@RouteConfig([
+ {path: '/', component: MyCmp, name: 'HomeCmp'},
+ {path: '/:param', component: MyCmp, name: 'ParamCmp'}
+])
+class AppCmp {
+}
+
+
+export function main() {
+ return bootstrap(
+ AppCmp, [provide(APP_BASE_HREF, {useValue: '/angular2/examples/router/ts/on_activate'})]);
+}
diff --git a/modules/angular2/examples/router/ts/on_activate/on_activate_spec.ts b/modules/angular2/examples/router/ts/on_activate/on_activate_spec.ts
new file mode 100644
index 000000000000..d6e57e6fba79
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_activate/on_activate_spec.ts
@@ -0,0 +1,34 @@
+import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
+import {Promise} from 'angular2/src/facade/async';
+
+function waitForElement(selector) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
+
+describe('on activate example app', function() {
+ afterEach(verifyNoBrowserErrors);
+
+ var URL = 'angular2/examples/router/ts/on_activate/';
+
+ it('should update the text when navigating between routes', function() {
+ browser.get(URL);
+ waitForElement('my-cmp');
+
+ expect(element(by.css('my-cmp')).getText())
+ .toContain('routerOnActivate: Finished navigating from "null" to ""');
+
+ element(by.css('#param-link')).click();
+ waitForElement('my-cmp');
+
+ expect(element(by.css('my-cmp')).getText())
+ .toContain('routerOnActivate: Finished navigating from "" to "1"');
+
+ browser.navigate().back();
+ waitForElement('my-cmp');
+
+ expect(element(by.css('my-cmp')).getText())
+ .toContain('routerOnActivate: Finished navigating from "1" to ""');
+ });
+});
diff --git a/modules/angular2/examples/router/ts/on_deactivate/index.html b/modules/angular2/examples/router/ts/on_deactivate/index.html
new file mode 100644
index 000000000000..13439d3d5417
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_deactivate/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Routing Reuse Lifecycle Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_example.ts b/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_example.ts
new file mode 100644
index 000000000000..036a0a47a27a
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_example.ts
@@ -0,0 +1,63 @@
+import {Component, Injectable, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {
+ OnDeactivate,
+ ComponentInstruction,
+ RouteConfig,
+ ROUTER_DIRECTIVES,
+ APP_BASE_HREF
+} from 'angular2/router';
+
+
+@Injectable()
+class LogService {
+ logs: string[] = [];
+
+ addLog(message: string): void { this.logs.push(message); }
+}
+
+
+// #docregion routerOnDeactivate
+@Component({selector: 'my-cmp', template: `hello
`})
+class MyCmp implements OnDeactivate {
+ constructor(private logService: LogService) {}
+
+ routerOnDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
+ this.logService.addLog(
+ `Navigating from "${prev ? prev.urlPath : 'null'}" to "${next.urlPath}"`);
+ }
+}
+// #enddocregion
+
+
+@Component({
+ selector: 'example-app',
+ template: `
+ My App
+
+ Navigate Home |
+ Navigate with a Param
+
+
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+@RouteConfig([
+ {path: '/', component: MyCmp, name: 'HomeCmp'},
+ {path: '/:param', component: MyCmp, name: 'ParamCmp'}
+])
+class AppCmp {
+ constructor(public logService: LogService) {}
+}
+
+
+export function main() {
+ return bootstrap(AppCmp, [
+ provide(APP_BASE_HREF, {useValue: '/angular2/examples/router/ts/on_deactivate'}),
+ LogService
+ ]);
+}
diff --git a/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_spec.ts b/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_spec.ts
new file mode 100644
index 000000000000..5f48717d0cba
--- /dev/null
+++ b/modules/angular2/examples/router/ts/on_deactivate/on_deactivate_spec.ts
@@ -0,0 +1,32 @@
+import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
+import {Promise} from 'angular2/src/facade/async';
+
+function waitForElement(selector) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
+
+describe('on activate example app', function() {
+ afterEach(verifyNoBrowserErrors);
+
+ var URL = 'angular2/examples/router/ts/on_deactivate/';
+
+ it('should update the text when navigating between routes', function() {
+ browser.get(URL);
+ waitForElement('my-cmp');
+
+ expect(element(by.css('#log')).getText()).toEqual('Log:');
+
+ element(by.css('#param-link')).click();
+ waitForElement('my-cmp');
+
+ expect(element(by.css('#log')).getText()).toEqual('Log:\nNavigating from "" to "1"');
+
+ browser.navigate().back();
+ waitForElement('my-cmp');
+
+ expect(element(by.css('#log')).getText())
+ .toEqual('Log:\nNavigating from "" to "1"\nNavigating from "1" to ""');
+ });
+});
diff --git a/modules/angular2/examples/router/ts/reuse/index.html b/modules/angular2/examples/router/ts/reuse/index.html
new file mode 100644
index 000000000000..6db5c421c69b
--- /dev/null
+++ b/modules/angular2/examples/router/ts/reuse/index.html
@@ -0,0 +1,24 @@
+
+
+
+ Routing Reuse Lifecycle Example
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/modules/angular2/examples/router/ts/reuse/reuse_example.ts b/modules/angular2/examples/router/ts/reuse/reuse_example.ts
new file mode 100644
index 000000000000..4e34e31d791e
--- /dev/null
+++ b/modules/angular2/examples/router/ts/reuse/reuse_example.ts
@@ -0,0 +1,58 @@
+import {Component, provide} from 'angular2/core';
+import {bootstrap} from 'angular2/bootstrap';
+import {
+ CanActivate,
+ RouteConfig,
+ ComponentInstruction,
+ ROUTER_DIRECTIVES,
+ APP_BASE_HREF,
+ CanReuse,
+ RouteParams,
+ OnReuse
+} from 'angular2/router';
+
+
+// #docregion reuseCmp
+@Component({
+ selector: 'my-cmp',
+ template: `
+ hello {{name}}!
+ message:
+ `
+})
+class MyCmp implements CanReuse,
+ OnReuse {
+ name: string;
+ constructor(params: RouteParams) { this.name = params.get('name') || 'NOBODY'; }
+
+ routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) { return true; }
+
+ routerOnReuse(next: ComponentInstruction, prev: ComponentInstruction) {
+ this.name = next.params['name'];
+ }
+}
+// #enddocregion
+
+
+@Component({
+ selector: 'example-app',
+ template: `
+ Say hi to...
+ Naomi |
+ Brad
+
+ `,
+ directives: [ROUTER_DIRECTIVES]
+})
+@RouteConfig([
+ {path: '/', component: MyCmp, name: 'HomeCmp'},
+ {path: '/:name', component: MyCmp, name: 'HomeCmp'}
+])
+class AppCmp {
+}
+
+
+export function main() {
+ return bootstrap(AppCmp,
+ [provide(APP_BASE_HREF, {useValue: '/angular2/examples/router/ts/reuse'})]);
+}
diff --git a/modules/angular2/examples/router/ts/reuse/reuse_spec.ts b/modules/angular2/examples/router/ts/reuse/reuse_spec.ts
new file mode 100644
index 000000000000..e1f24547227a
--- /dev/null
+++ b/modules/angular2/examples/router/ts/reuse/reuse_spec.ts
@@ -0,0 +1,36 @@
+import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
+import {Promise} from 'angular2/src/facade/async';
+
+function waitForElement(selector) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
+
+describe('reuse example app', function() {
+
+ afterEach(verifyNoBrowserErrors);
+
+ var URL = 'angular2/examples/router/ts/reuse/';
+
+ it('should build a link which points to the detail page', function() {
+ browser.get(URL);
+ waitForElement('my-cmp');
+
+ element(by.css('#naomi-link')).click();
+ waitForElement('my-cmp');
+ expect(browser.getCurrentUrl()).toMatch(/\/naomi$/);
+
+ // type something into input
+ element(by.css('#message')).sendKeys('long time no see!');
+
+ // navigate to Brad
+ element(by.css('#brad-link')).click();
+ waitForElement('my-cmp');
+ expect(browser.getCurrentUrl()).toMatch(/\/brad$/);
+
+ // check that typed input is the same
+ expect(element(by.css('#message')).getAttribute('value')).toEqual('long time no see!');
+ });
+
+});
diff --git a/modules/angular2/examples/testing/ts/fake_async.ts b/modules/angular2/examples/testing/ts/fake_async.ts
new file mode 100644
index 000000000000..55bb766b91af
--- /dev/null
+++ b/modules/angular2/examples/testing/ts/fake_async.ts
@@ -0,0 +1,29 @@
+import {describe, it, fakeAsync, expect, tick, clearPendingTimers} from 'angular2/testing';
+
+// #docregion basic
+describe('this test', () => {
+ it('looks async but is synchronous', fakeAsync((): void => {
+ var flag = false;
+ setTimeout(() => { flag = true; }, 100);
+ expect(flag).toBe(false);
+ tick(50);
+ expect(flag).toBe(false);
+ tick(50);
+ expect(flag).toBe(true);
+ }));
+});
+// #enddocregion
+
+// #docregion pending
+describe('this test', () => {
+ it('aborts a timer', fakeAsync((): void => {
+ // This timer is scheduled but doesn't need to complete for the
+ // test to pass (maybe it's a timeout for some operation).
+ // Leaving it will cause the test to fail...
+ setTimeout(() => {}, 100);
+
+ // Unless we clean it up first.
+ clearPendingTimers();
+ }));
+});
+// #enddocregion
\ No newline at end of file
diff --git a/modules/angular2/examples/testing/ts/matchers.ts b/modules/angular2/examples/testing/ts/matchers.ts
new file mode 100644
index 000000000000..f0bf7d2818dc
--- /dev/null
+++ b/modules/angular2/examples/testing/ts/matchers.ts
@@ -0,0 +1,40 @@
+import {expect} from 'angular2/testing';
+
+var value: any;
+var element: any;
+var exception: any;
+
+abstract class OtherClass {}
+class SomeClass {}
+
+// #docregion toBePromise
+expect(value).toBePromise();
+// #enddocregion
+
+// #docregion toBeAnInstanceOf
+expect(value).toBeAnInstanceOf(SomeClass);
+// #enddocregion
+
+// #docregion toHaveText
+expect(element).toHaveText('Hello world!');
+// #enddocregion
+
+// #docregion toHaveCssClass
+expect(element).toHaveCssClass('current');
+// #enddocregion
+
+// #docregion toHaveCssStyle
+expect(element).toHaveCssStyle({width: '100px', height: 'auto'});
+// #enddocregion
+
+// #docregion toContainError
+expect(exception).toContainError('Failed to load');
+// #enddocregion
+
+// #docregion toThrowErrorWith
+expect(() => { throw 'Failed to load'; }).toThrowErrorWith('Failed to load');
+// #enddocregion
+
+// #docregion toImplement
+expect(SomeClass).toImplement(OtherClass);
+// #enddocregion
diff --git a/modules/angular2/examples/testing/ts/testing.ts b/modules/angular2/examples/testing/ts/testing.ts
new file mode 100644
index 000000000000..f46a2e00799d
--- /dev/null
+++ b/modules/angular2/examples/testing/ts/testing.ts
@@ -0,0 +1,90 @@
+import {
+ describe,
+ fdescribe,
+ xdescribe,
+ it,
+ fit,
+ xit,
+ beforeEach,
+ afterEach,
+ beforeEachProviders,
+ inject
+} from 'angular2/testing';
+import {provide} from 'angular2/core';
+
+var db: any;
+class MyService {}
+class MyMockService implements MyService {}
+
+// #docregion describeIt
+describe('some component', () => {
+ it('does something', () => {
+ // This is a test.
+ });
+});
+// #enddocregion
+
+// #docregion fdescribe
+fdescribe('some component', () => {
+ it('has a test', () => {
+ // This test will run.
+ });
+});
+describe('another component',
+ () => { it('also has a test', () => { throw 'This test will not run.'; }); });
+// #enddocregion
+
+// #docregion xdescribe
+xdescribe('some component', () => { it('has a test', () => {throw 'This test will not run.'}); });
+describe('another component', () => {
+ it('also has a test', () => {
+ // This test will run.
+ });
+});
+// #enddocregion
+
+// #docregion fit
+describe('some component', () => {
+ fit('has a test', () => {
+ // This test will run.
+ });
+ it('has another test', () => { throw 'This test will not run.'; });
+});
+// #enddocregion
+
+// #docregion xit
+describe('some component', () => {
+ xit('has a test', () => { throw 'This test will not run.'; });
+ it('has another test', () => {
+ // This test will run.
+ });
+});
+// #enddocregion
+
+// #docregion beforeEach
+describe('some component', () => {
+ beforeEach(() => { db.connect(); });
+ it('uses the db', () => {
+ // Database is connected.
+ });
+});
+// #enddocregion
+
+// #docregion beforeEachProviders
+describe('some component', () => {
+ beforeEachProviders(() => [provide(MyService, {useClass: MyMockService})]);
+ it('uses MyService', inject([MyService], (service) => {
+ // service is an instance of MyMockService.
+ }));
+});
+// #enddocregion
+
+// #docregion afterEach
+describe('some component', () => {
+ afterEach((done) => { db.reset().then((_) => done()); });
+ it('uses the db', () => {
+ // This test can leave the database in a dirty state.
+ // The afterEach will ensure it gets reset.
+ });
+});
+// #enddocregion
diff --git a/modules/angular2/examples/web_workers/ts/.gitkeep b/modules/angular2/examples/web_workers/ts/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/angular2/forms.js b/modules/angular2/forms.js
deleted file mode 100644
index e943dd2d9649..000000000000
--- a/modules/angular2/forms.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './src/forms/model';
-export * from './src/forms/directives';
-export * from './src/forms/validators';
-export * from './src/forms/validator_directives';
\ No newline at end of file
diff --git a/modules/angular2/http.ts b/modules/angular2/http.ts
new file mode 100644
index 000000000000..5341e23191a0
--- /dev/null
+++ b/modules/angular2/http.ts
@@ -0,0 +1,300 @@
+/**
+ * @module
+ * @description
+ * The http module provides services to perform http requests. To get started, see the {@link Http}
+ * class.
+ */
+import {provide, Provider} from 'angular2/core';
+import {Http, Jsonp} from './src/http/http';
+import {XHRBackend, XHRConnection} from './src/http/backends/xhr_backend';
+import {JSONPBackend, JSONPBackend_, JSONPConnection} from './src/http/backends/jsonp_backend';
+import {BrowserXhr} from './src/http/backends/browser_xhr';
+import {BrowserJsonp} from './src/http/backends/browser_jsonp';
+import {BaseRequestOptions, RequestOptions} from './src/http/base_request_options';
+import {ConnectionBackend} from './src/http/interfaces';
+import {BaseResponseOptions, ResponseOptions} from './src/http/base_response_options';
+export {Request} from './src/http/static_request';
+export {Response} from './src/http/static_response';
+
+export {
+ RequestOptionsArgs,
+ ResponseOptionsArgs,
+ Connection,
+ ConnectionBackend
+} from './src/http/interfaces';
+
+export {BrowserXhr} from './src/http/backends/browser_xhr';
+export {BaseRequestOptions, RequestOptions} from './src/http/base_request_options';
+export {BaseResponseOptions, ResponseOptions} from './src/http/base_response_options';
+export {XHRBackend, XHRConnection} from './src/http/backends/xhr_backend';
+export {JSONPBackend, JSONPConnection} from './src/http/backends/jsonp_backend';
+export {Http, Jsonp} from './src/http/http';
+
+export {Headers} from './src/http/headers';
+
+export {ResponseType, ReadyState, RequestMethod} from './src/http/enums';
+export {URLSearchParams} from './src/http/url_search_params';
+
+/**
+ * Provides a basic set of injectables to use the {@link Http} service in any application.
+ *
+ * The `HTTP_PROVIDERS` should be included either in a component's injector,
+ * or in the root injector when bootstrapping an application.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/snj7Nv?p=preview))
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {bootstrap} from 'angular2/platform/browser';
+ * import {NgFor} from 'angular2/common';
+ * import {HTTP_PROVIDERS, Http} from 'angular2/http';
+ *
+ * @Component({
+ * selector: 'app',
+ * providers: [HTTP_PROVIDERS],
+ * template: `
+ *
+ *
People
+ *
+ *
+ * {{person.name}}
+ *
+ *
+ *
+ * `,
+ * directives: [NgFor]
+ * })
+ * export class App {
+ * people: Object[];
+ * constructor(http:Http) {
+ * http.get('people.json').subscribe(res => {
+ * this.people = res.json();
+ * });
+ * }
+ * active:boolean = false;
+ * toggleActiveState() {
+ * this.active = !this.active;
+ * }
+ * }
+ *
+ * bootstrap(App)
+ * .catch(err => console.error(err));
+ * ```
+ *
+ * The primary public API included in `HTTP_PROVIDERS` is the {@link Http} class.
+ * However, other providers required by `Http` are included,
+ * which may be beneficial to override in certain cases.
+ *
+ * The providers included in `HTTP_PROVIDERS` include:
+ * * {@link Http}
+ * * {@link XHRBackend}
+ * * `BrowserXHR` - Private factory to create `XMLHttpRequest` instances
+ * * {@link RequestOptions} - Bound to {@link BaseRequestOptions} class
+ * * {@link ResponseOptions} - Bound to {@link BaseResponseOptions} class
+ *
+ * There may be cases where it makes sense to extend the base request options,
+ * such as to add a search string to be appended to all URLs.
+ * To accomplish this, a new provider for {@link RequestOptions} should
+ * be added in the same injector as `HTTP_PROVIDERS`.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/aCMEXi?p=preview))
+ *
+ * ```
+ * import {provide} from 'angular2/core';
+ * import {bootstrap} from 'angular2/platform/browser';
+ * import {HTTP_PROVIDERS, BaseRequestOptions, RequestOptions} from 'angular2/http';
+ *
+ * class MyOptions extends BaseRequestOptions {
+ * search: string = 'coreTeam=true';
+ * }
+ *
+ * bootstrap(App, [HTTP_PROVIDERS, provide(RequestOptions, {useClass: MyOptions})])
+ * .catch(err => console.error(err));
+ * ```
+ *
+ * Likewise, to use a mock backend for unit tests, the {@link XHRBackend}
+ * provider should be bound to {@link MockBackend}.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/7LWALD?p=preview))
+ *
+ * ```
+ * import {provide} from 'angular2/core';
+ * import {bootstrap} from 'angular2/platform/browser';
+ * import {HTTP_PROVIDERS, Http, Response, XHRBackend} from 'angular2/http';
+ * import {MockBackend} from 'angular2/http/testing';
+ *
+ * var people = [{name: 'Jeff'}, {name: 'Tobias'}];
+ *
+ * var injector = Injector.resolveAndCreate([
+ * HTTP_PROVIDERS,
+ * MockBackend,
+ * provide(XHRBackend, {useExisting: MockBackend})
+ * ]);
+ * var http = injector.get(Http);
+ * var backend = injector.get(MockBackend);
+ *
+ * // Listen for any new requests
+ * backend.connections.observer({
+ * next: connection => {
+ * var response = new Response({body: people});
+ * setTimeout(() => {
+ * // Send a response to the request
+ * connection.mockRespond(response);
+ * });
+ * });
+ *
+ * http.get('people.json').observer({
+ * next: res => {
+ * // Response came from mock backend
+ * console.log('first person', res.json()[0].name);
+ * }
+ * });
+ * ```
+ */
+export const HTTP_PROVIDERS: any[] = [
+ // TODO(pascal): use factory type annotations once supported in DI
+ // issue: https://github.com/angular/angular/issues/3183
+ provide(Http,
+ {
+ useFactory: (xhrBackend, requestOptions) => new Http(xhrBackend, requestOptions),
+ deps: [XHRBackend, RequestOptions]
+ }),
+ BrowserXhr,
+ provide(RequestOptions, {useClass: BaseRequestOptions}),
+ provide(ResponseOptions, {useClass: BaseResponseOptions}),
+ XHRBackend
+];
+
+/**
+ * See {@link HTTP_PROVIDERS} instead.
+ *
+ * @deprecated
+ */
+export const HTTP_BINDINGS = HTTP_PROVIDERS;
+
+/**
+ * Provides a basic set of providers to use the {@link Jsonp} service in any application.
+ *
+ * The `JSONP_PROVIDERS` should be included either in a component's injector,
+ * or in the root injector when bootstrapping an application.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/vmeN4F?p=preview))
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {NgFor} from 'angular2/common';
+ * import {JSONP_PROVIDERS, Jsonp} from 'angular2/http';
+ *
+ * @Component({
+ * selector: 'app',
+ * providers: [JSONP_PROVIDERS],
+ * template: `
+ *
+ *
People
+ *
+ *
+ * {{person.name}}
+ *
+ *
+ *
+ * `,
+ * directives: [NgFor]
+ * })
+ * export class App {
+ * people: Array;
+ * constructor(jsonp:Jsonp) {
+ * jsonp.request('people.json').subscribe(res => {
+ * this.people = res.json();
+ * })
+ * }
+ * }
+ * ```
+ *
+ * The primary public API included in `JSONP_PROVIDERS` is the {@link Jsonp} class.
+ * However, other providers required by `Jsonp` are included,
+ * which may be beneficial to override in certain cases.
+ *
+ * The providers included in `JSONP_PROVIDERS` include:
+ * * {@link Jsonp}
+ * * {@link JSONPBackend}
+ * * `BrowserJsonp` - Private factory
+ * * {@link RequestOptions} - Bound to {@link BaseRequestOptions} class
+ * * {@link ResponseOptions} - Bound to {@link BaseResponseOptions} class
+ *
+ * There may be cases where it makes sense to extend the base request options,
+ * such as to add a search string to be appended to all URLs.
+ * To accomplish this, a new provider for {@link RequestOptions} should
+ * be added in the same injector as `JSONP_PROVIDERS`.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/TFug7x?p=preview))
+ *
+ * ```
+ * import {provide} from 'angular2/core';
+ * import {bootstrap} from 'angular2/platform/browser';
+ * import {JSONP_PROVIDERS, BaseRequestOptions, RequestOptions} from 'angular2/http';
+ *
+ * class MyOptions extends BaseRequestOptions {
+ * search: string = 'coreTeam=true';
+ * }
+ *
+ * bootstrap(App, [JSONP_PROVIDERS, provide(RequestOptions, {useClass: MyOptions})])
+ * .catch(err => console.error(err));
+ * ```
+ *
+ * Likewise, to use a mock backend for unit tests, the {@link JSONPBackend}
+ * provider should be bound to {@link MockBackend}.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/HDqZWL?p=preview))
+ *
+ * ```
+ * import {provide, Injector} from 'angular2/core';
+ * import {JSONP_PROVIDERS, Jsonp, Response, JSONPBackend} from 'angular2/http';
+ * import {MockBackend} from 'angular2/http/testing';
+ *
+ * var people = [{name: 'Jeff'}, {name: 'Tobias'}];
+ * var injector = Injector.resolveAndCreate([
+ * JSONP_PROVIDERS,
+ * MockBackend,
+ * provide(JSONPBackend, {useExisting: MockBackend})
+ * ]);
+ * var jsonp = injector.get(Jsonp);
+ * var backend = injector.get(MockBackend);
+ *
+ * // Listen for any new requests
+ * backend.connections.observer({
+ * next: connection => {
+ * var response = new Response({body: people});
+ * setTimeout(() => {
+ * // Send a response to the request
+ * connection.mockRespond(response);
+ * });
+ * });
+
+ * jsonp.get('people.json').observer({
+ * next: res => {
+ * // Response came from mock backend
+ * console.log('first person', res.json()[0].name);
+ * }
+ * });
+ * ```
+ */
+export const JSONP_PROVIDERS: any[] = [
+ // TODO(pascal): use factory type annotations once supported in DI
+ // issue: https://github.com/angular/angular/issues/3183
+ provide(Jsonp,
+ {
+ useFactory: (jsonpBackend, requestOptions) => new Jsonp(jsonpBackend, requestOptions),
+ deps: [JSONPBackend, RequestOptions]
+ }),
+ BrowserJsonp,
+ provide(RequestOptions, {useClass: BaseRequestOptions}),
+ provide(ResponseOptions, {useClass: BaseResponseOptions}),
+ provide(JSONPBackend, {useClass: JSONPBackend_})
+];
+
+/**
+ * See {@link JSONP_PROVIDERS} instead.
+ *
+ * @deprecated
+ */
+export const JSON_BINDINGS = JSONP_PROVIDERS;
diff --git a/modules/angular2/http/testing.ts b/modules/angular2/http/testing.ts
new file mode 100644
index 000000000000..4b0e8a88c3fd
--- /dev/null
+++ b/modules/angular2/http/testing.ts
@@ -0,0 +1 @@
+export * from 'angular2/src/http/backends/mock_backend';
\ No newline at end of file
diff --git a/modules/angular2/instrumentation.ts b/modules/angular2/instrumentation.ts
new file mode 100644
index 000000000000..67049babaf8e
--- /dev/null
+++ b/modules/angular2/instrumentation.ts
@@ -0,0 +1,7 @@
+export {
+ wtfCreateScope,
+ wtfLeave,
+ wtfStartTimeRange,
+ wtfEndTimeRange,
+ WtfScopeFn
+} from './src/core/profile/profile';
diff --git a/modules/angular2/manual_typings/globals-es6.d.ts b/modules/angular2/manual_typings/globals-es6.d.ts
new file mode 100644
index 000000000000..b1292515c2e0
--- /dev/null
+++ b/modules/angular2/manual_typings/globals-es6.d.ts
@@ -0,0 +1,37 @@
+/**
+ * Declarations angular depends on for compilation to ES6.
+ * This file is also used to propagate our transitive typings
+ * to users.
+ */
+
+///
+///
+///
+///
+
+// TODO: ideally the node.d.ts reference should be scoped only for files that need and not to all
+// the code including client code
+///
+
+declare var assert: any;
+
+
+interface BrowserNodeGlobal {
+ 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(condition: any): void;
+ Reflect: any;
+ zone: Zone;
+ getAngularTestability: Function;
+ getAllAngularTestabilities: Function;
+ setTimeout: Function;
+ clearTimeout: Function;
+ setInterval: Function;
+ clearInterval: Function;
+}
diff --git a/modules/angular2/manual_typings/globals.d.ts b/modules/angular2/manual_typings/globals.d.ts
new file mode 100644
index 000000000000..164d17e7a6ba
--- /dev/null
+++ b/modules/angular2/manual_typings/globals.d.ts
@@ -0,0 +1,7 @@
+/**
+ * Declarations angular depends on for compilation to ES6.
+ * This file is also used to propagate our transitive typings
+ * to users.
+ */
+///
+///
diff --git a/modules/angular2/package.json b/modules/angular2/package.json
index 28689dbc92b0..e4f2d5afd591 100644
--- a/modules/angular2/package.json
+++ b/modules/angular2/package.json
@@ -6,10 +6,13 @@
"bugs": "<%= packageJson.bugs %>",
"contributors": <%= JSON.stringify(packageJson.contributors) %>,
"license": "<%= packageJson.license %>",
- "dependencies": {
- "traceur": "<%= packageJson.dependencies.traceur %>",
- "rtts_assert": "<%= packageJson.version %>",
+ "repository": <%= JSON.stringify(packageJson.repository) %>,
+ "devDependencies": <%= JSON.stringify(packageJson.defaultDevDependencies) %>,
+ "peerDependencies": {
+ "es6-promise": "<%= packageJson.dependencies['es6-promise'] %>",
+ "es6-shim": "<%= packageJson.dependencies['es6-shim'] %>",
+ "reflect-metadata": "<%= packageJson.dependencies['reflect-metadata'] %>",
+ "rxjs": "<%= packageJson.dependencies['rxjs'] %>",
"zone.js": "<%= packageJson.dependencies['zone.js'] %>"
- },
- "devDependencies": <%= JSON.stringify(packageJson.devDependencies) %>
+ }
}
diff --git a/modules/angular2/platform/browser.ts b/modules/angular2/platform/browser.ts
new file mode 100644
index 000000000000..741f1f65e98b
--- /dev/null
+++ b/modules/angular2/platform/browser.ts
@@ -0,0 +1,112 @@
+export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
+export {
+ BROWSER_PROVIDERS,
+ ELEMENT_PROBE_BINDINGS,
+ ELEMENT_PROBE_PROVIDERS,
+ inspectNativeElement,
+ BrowserDomAdapter,
+ By,
+ Title,
+ DOCUMENT,
+ enableDebugTools,
+ disableDebugTools
+} from 'angular2/src/platform/browser_common';
+
+import {Type, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
+import {Promise} from 'angular2/src/facade/promise';
+import {
+ BROWSER_PROVIDERS,
+ BROWSER_APP_COMMON_PROVIDERS
+} from 'angular2/src/platform/browser_common';
+import {COMPILER_PROVIDERS} from 'angular2/compiler';
+import {ComponentRef, platform, reflector} from 'angular2/core';
+import {ReflectionCapabilities} from 'angular2/src/core/reflection/reflection_capabilities';
+import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";
+import {XHR} from 'angular2/compiler';
+import {Provider} from 'angular2/src/core/di';
+
+/**
+ * An array of providers that should be passed into `application()` when bootstrapping a component.
+ */
+export const BROWSER_APP_PROVIDERS: Array = CONST_EXPR([
+ BROWSER_APP_COMMON_PROVIDERS,
+ COMPILER_PROVIDERS,
+ new Provider(XHR, {useClass: XHRImpl}),
+]);
+
+/**
+ * Bootstrapping for Angular applications.
+ *
+ * You instantiate an Angular application by explicitly specifying a component to use
+ * as the root component for your application via the `bootstrap()` method.
+ *
+ * ## Simple Example
+ *
+ * Assuming this `index.html`:
+ *
+ * ```html
+ *
+ *
+ *
+ * loading...
+ *
+ *
+ * ```
+ *
+ * An application is bootstrapped inside an existing browser DOM, typically `index.html`.
+ * Unlike Angular 1, Angular 2 does not compile/process providers 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
+ * providers. Bindings can thus use double-curly `{{ syntax }}` without collision from
+ * Angular 2 component double-curly `{{ syntax }}`.
+ *
+ * We can use this script code:
+ *
+ * {@example core/ts/bootstrap/bootstrap.ts region='bootstrap'}
+ *
+ * When the app developer invokes `bootstrap()` with the root component `MyApp` as its
+ * argument, Angular performs the following tasks:
+ *
+ * 1. It uses the component's `selector` property to locate the DOM element which needs
+ * to be upgraded into the angular component.
+ * 2. It creates a new child injector (from the platform injector). 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 an emulated or shadow DOM on the selected component's host element and loads the
+ * template into it.
+ * 5. It instantiates the specified component.
+ * 6. Finally, Angular performs change detection to apply the initial data providers for the
+ * application.
+ *
+ *
+ * ## Bootstrapping Multiple Applications
+ *
+ * When working within a browser window, there are many singleton resources: cookies, title,
+ * location, and others. Angular services that represent these resources must likewise be
+ * shared across all Angular applications that occupy the same browser window. For this
+ * reason, Angular creates exactly one global platform object which stores all shared
+ * services, and each angular application injector has the platform injector as its parent.
+ *
+ * Each application has its own private injector as well. When there are multiple
+ * applications on a page, Angular treats each application injector's services as private
+ * to that application.
+ *
+ * ## API
+ *
+ * - `appComponentType`: The root component which should act as the application. This is
+ * a reference to a `Type` which is annotated with `@Component(...)`.
+ * - `customProviders`: An additional set of providers that can be added to the
+ * app injector to override default injection behavior.
+ *
+ * Returns a `Promise` of {@link ComponentRef}.
+ */
+export function bootstrap(
+ appComponentType: Type,
+ customProviders?: Array ): Promise {
+ reflector.reflectionCapabilities = new ReflectionCapabilities();
+ let appProviders =
+ isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS;
+ return platform(BROWSER_PROVIDERS).application(appProviders).bootstrap(appComponentType);
+}
diff --git a/modules/angular2/platform/browser_static.ts b/modules/angular2/platform/browser_static.ts
new file mode 100644
index 000000000000..732b08bf1156
--- /dev/null
+++ b/modules/angular2/platform/browser_static.ts
@@ -0,0 +1,43 @@
+export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
+export {
+ BROWSER_PROVIDERS,
+ ELEMENT_PROBE_BINDINGS,
+ ELEMENT_PROBE_PROVIDERS,
+ inspectNativeElement,
+ BrowserDomAdapter,
+ By,
+ Title,
+ enableDebugTools,
+ disableDebugTools
+} from 'angular2/src/platform/browser_common';
+
+import {Type, isPresent} from 'angular2/src/facade/lang';
+import {Promise} from 'angular2/src/facade/promise';
+import {
+ BROWSER_PROVIDERS,
+ BROWSER_APP_COMMON_PROVIDERS
+} from 'angular2/src/platform/browser_common';
+import {ComponentRef, platform} from 'angular2/core';
+
+/**
+ * An array of providers that should be passed into `application()` when bootstrapping a component
+ * when all templates
+ * have been precompiled offline.
+ */
+export const BROWSER_APP_PROVIDERS: Array =
+ BROWSER_APP_COMMON_PROVIDERS;
+
+/**
+ * See {@link bootstrap} for more information.
+ */
+export function bootstrapStatic(appComponentType: Type,
+ customProviders?: Array ,
+ initReflector?: Function): Promise {
+ if (isPresent(initReflector)) {
+ initReflector();
+ }
+
+ let appProviders =
+ isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS;
+ return platform(BROWSER_PROVIDERS).application(appProviders).bootstrap(appComponentType);
+}
diff --git a/modules/angular2/platform/common_dom.ts b/modules/angular2/platform/common_dom.ts
new file mode 100644
index 000000000000..c74e5ee84bf6
--- /dev/null
+++ b/modules/angular2/platform/common_dom.ts
@@ -0,0 +1,15 @@
+/**
+ * This is a set of classes and objects that can be used both in the browser and on the server.
+ */
+export {DOM, setRootDomAdapter, DomAdapter} from 'angular2/src/platform/dom/dom_adapter';
+export {DomRenderer} from 'angular2/src/platform/dom/dom_renderer';
+export {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
+export {SharedStylesHost, DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
+export {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
+export {
+ EVENT_MANAGER_PLUGINS,
+ EventManager,
+ EventManagerPlugin
+} from 'angular2/src/platform/dom/events/event_manager';
+export * from 'angular2/src/platform/dom/debug/by';
+export * from 'angular2/src/platform/dom/debug/debug_element_view_listener';
\ No newline at end of file
diff --git a/modules/angular2/platform/server.dart b/modules/angular2/platform/server.dart
new file mode 100644
index 000000000000..61ae1b680956
--- /dev/null
+++ b/modules/angular2/platform/server.dart
@@ -0,0 +1,2 @@
+// TODO: vsavkin add SERVER_PROVIDERS and SERVER_APP_PROVIDERS
+export 'package:angular2/src/platform/server/html_adapter.dart';
diff --git a/modules/angular2/platform/server.ts b/modules/angular2/platform/server.ts
new file mode 100644
index 000000000000..9270dfbef16e
--- /dev/null
+++ b/modules/angular2/platform/server.ts
@@ -0,0 +1,2 @@
+// TODO: vsavkin add SERVER_PROVIDERS and SERVER_APP_PROVIDERS
+export {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
\ No newline at end of file
diff --git a/modules/angular2/platform/worker_app.dart b/modules/angular2/platform/worker_app.dart
new file mode 100644
index 000000000000..e2ff19ea65c8
--- /dev/null
+++ b/modules/angular2/platform/worker_app.dart
@@ -0,0 +1,14 @@
+library angular2.platform.worker_app;
+
+export "package:angular2/src/platform/worker_app_common.dart"
+ show WORKER_APP_PLATFORM, WORKER_APP_APPLICATION_COMMON;
+export "package:angular2/src/core/angular_entrypoint.dart"
+ show AngularEntrypoint;
+export "package:angular2/src/platform/worker_app.dart"
+ show WORKER_APP_APPLICATION, RENDER_SEND_PORT;
+export 'package:angular2/src/web_workers/shared/client_message_broker.dart'
+ show ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments;
+export 'package:angular2/src/web_workers/shared/service_message_broker.dart'
+ show ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory;
+export 'package:angular2/src/web_workers/shared/serializer.dart' show PRIMITIVE;
+export 'package:angular2/src/web_workers/shared/message_bus.dart';
diff --git a/modules/angular2/platform/worker_app.ts b/modules/angular2/platform/worker_app.ts
new file mode 100644
index 000000000000..7e6957be967e
--- /dev/null
+++ b/modules/angular2/platform/worker_app.ts
@@ -0,0 +1,19 @@
+export {
+ WORKER_APP_PLATFORM,
+ WORKER_APP_APPLICATION_COMMON
+} from 'angular2/src/platform/worker_app_common';
+export {WORKER_APP_APPLICATION} from 'angular2/src/platform/worker_app';
+export {
+ ClientMessageBroker,
+ ClientMessageBrokerFactory,
+ FnArg,
+ UiArguments
+} from '../src/web_workers/shared/client_message_broker';
+export {
+ ReceivedMessage,
+ ServiceMessageBroker,
+ ServiceMessageBrokerFactory
+} from '../src/web_workers/shared/service_message_broker';
+export {PRIMITIVE} from '../src/web_workers/shared/serializer';
+export * from '../src/web_workers/shared/message_bus';
+export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
diff --git a/modules/angular2/platform/worker_render.dart b/modules/angular2/platform/worker_render.dart
new file mode 100644
index 000000000000..1b689cc2440b
--- /dev/null
+++ b/modules/angular2/platform/worker_render.dart
@@ -0,0 +1,20 @@
+library angular2.platform.worker_render;
+
+export 'package:angular2/src/platform/worker_render_common.dart'
+ show
+ WORKER_SCRIPT,
+ WORKER_RENDER_PLATFORM,
+ WORKER_RENDER_APP_COMMON,
+ initializeGenericWorkerRenderer;
+
+export 'package:angular2/src/platform/worker_render.dart'
+ show WORKER_RENDER_APP, initIsolate, WebWorkerInstance;
+
+export '../src/web_workers/shared/client_message_broker.dart'
+ show ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments;
+
+export '../src/web_workers/shared/service_message_broker.dart'
+ show ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory;
+
+export '../src/web_workers/shared/serializer.dart' show PRIMITIVE;
+export '../src/web_workers/shared/message_bus.dart';
diff --git a/modules/angular2/platform/worker_render.ts b/modules/angular2/platform/worker_render.ts
new file mode 100644
index 000000000000..7a2e7ab579ea
--- /dev/null
+++ b/modules/angular2/platform/worker_render.ts
@@ -0,0 +1,20 @@
+export {
+ WORKER_SCRIPT,
+ WORKER_RENDER_PLATFORM,
+ initializeGenericWorkerRenderer,
+ WORKER_RENDER_APP_COMMON
+} from 'angular2/src/platform/worker_render_common';
+export * from 'angular2/src/platform/worker_render';
+export {
+ ClientMessageBroker,
+ ClientMessageBrokerFactory,
+ FnArg,
+ UiArguments
+} from '../src/web_workers/shared/client_message_broker';
+export {
+ ReceivedMessage,
+ ServiceMessageBroker,
+ ServiceMessageBrokerFactory
+} from '../src/web_workers/shared/service_message_broker';
+export {PRIMITIVE} from '../src/web_workers/shared/serializer';
+export * from '../src/web_workers/shared/message_bus';
diff --git a/modules/angular2/pubspec.yaml b/modules/angular2/pubspec.yaml
index 661597cd751a..980ce1a25f73 100644
--- a/modules/angular2/pubspec.yaml
+++ b/modules/angular2/pubspec.yaml
@@ -7,13 +7,25 @@ authors:
description: Angular 2 for Dart - a web framework for modern web apps
homepage: <%= packageJson.homepage %>
environment:
- sdk: '>=1.9.0-dev.8.0'
+ sdk: '>=1.10.0 <2.0.0'
dependencies:
- analyzer: '^0.22.4'
+ analyzer: '>=0.24.4 <0.27.0'
barback: '^0.15.2+2'
- code_transformers: '^0.2.5'
- dart_style: '^0.1.3'
- html5lib: '^0.12.0'
+ code_transformers: '0.2.9+4'
+ dart_style: '>=0.1.8 <0.3.0'
+ glob: '^1.0.0'
+ html: '^0.12.0'
+ intl: '^0.12.4'
+ logging: '>=0.9.0 <0.12.0'
+ observe: '^0.13.1'
+ protobuf: '^0.5.0'
+ quiver: '^0.21.4'
+ source_span: '^1.0.0'
stack_trace: '^1.1.1'
dev_dependencies:
- guinness: "^0.1.16"
+ guinness: '^0.1.18'
+transformers:
+- angular2
+- $dart2js:
+ commandLineOptions:
+ - --show-package-warnings
diff --git a/modules/angular2/router.ts b/modules/angular2/router.ts
new file mode 100644
index 000000000000..4cea18950a6a
--- /dev/null
+++ b/modules/angular2/router.ts
@@ -0,0 +1,118 @@
+/**
+ * @module
+ * @description
+ * Maps application URLs into application states, to support deep-linking and navigation.
+ */
+
+export {Router} from './src/router/router';
+export {RouterOutlet} from './src/router/router_outlet';
+export {RouterLink} from './src/router/router_link';
+export {RouteParams, RouteData} from './src/router/instruction';
+export {PlatformLocation} from './src/router/platform_location';
+export {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './src/router/route_registry';
+export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
+export {HashLocationStrategy} from './src/router/hash_location_strategy';
+export {PathLocationStrategy} from './src/router/path_location_strategy';
+export {Location} from './src/router/location';
+export * from './src/router/route_config_decorator';
+export * from './src/router/route_definition';
+export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/router/interfaces';
+export {CanActivate} from './src/router/lifecycle_annotations';
+export {Instruction, ComponentInstruction} from './src/router/instruction';
+export {OpaqueToken} from 'angular2/core';
+
+import {PlatformLocation} from './src/router/platform_location';
+import {LocationStrategy} from './src/router/location_strategy';
+import {PathLocationStrategy} from './src/router/path_location_strategy';
+import {Router, RootRouter} from './src/router/router';
+import {RouterOutlet} from './src/router/router_outlet';
+import {RouterLink} from './src/router/router_link';
+import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './src/router/route_registry';
+import {Location} from './src/router/location';
+import {ApplicationRef, provide, OpaqueToken, Provider} from 'angular2/core';
+import {CONST_EXPR} from './src/facade/lang';
+import {BaseException} from 'angular2/src/facade/exceptions';
+
+/**
+ * A list of directives. To use the router directives like {@link RouterOutlet} and
+ * {@link RouterLink}, add this to your `directives` array in the {@link View} decorator of your
+ * component.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm))
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
+ *
+ * @Component({directives: [ROUTER_DIRECTIVES]})
+ * @RouteConfig([
+ * {...},
+ * ])
+ * class AppCmp {
+ * // ...
+ * }
+ *
+ * bootstrap(AppCmp, [ROUTER_PROVIDERS]);
+ * ```
+ */
+export const ROUTER_DIRECTIVES: any[] = CONST_EXPR([RouterOutlet, RouterLink]);
+
+/**
+ * A list of {@link Provider}s. To use the router, you must add this to your application.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm))
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {
+ * ROUTER_DIRECTIVES,
+ * ROUTER_PROVIDERS,
+ * RouteConfig
+ * } from 'angular2/router';
+ *
+ * @Component({directives: [ROUTER_DIRECTIVES]})
+ * @RouteConfig([
+ * {...},
+ * ])
+ * class AppCmp {
+ * // ...
+ * }
+ *
+ * bootstrap(AppCmp, [ROUTER_PROVIDERS]);
+ * ```
+ */
+export const ROUTER_PROVIDERS: any[] = CONST_EXPR([
+ RouteRegistry,
+ CONST_EXPR(new Provider(LocationStrategy, {useClass: PathLocationStrategy})),
+ PlatformLocation,
+ Location,
+ CONST_EXPR(new Provider(
+ Router,
+ {
+ useFactory: routerFactory,
+ deps: CONST_EXPR([RouteRegistry, Location, ROUTER_PRIMARY_COMPONENT, ApplicationRef])
+ })),
+ CONST_EXPR(new Provider(
+ ROUTER_PRIMARY_COMPONENT,
+ {useFactory: routerPrimaryComponentFactory, deps: CONST_EXPR([ApplicationRef])}))
+]);
+
+/**
+ * Use {@link ROUTER_PROVIDERS} instead.
+ *
+ * @deprecated
+ */
+export const ROUTER_BINDINGS = ROUTER_PROVIDERS;
+
+function routerFactory(registry, location, primaryComponent, appRef) {
+ var rootRouter = new RootRouter(registry, location, primaryComponent);
+ appRef.registerDisposeListener(() => rootRouter.dispose());
+ return rootRouter;
+}
+
+function routerPrimaryComponentFactory(app) {
+ if (app.componentTypes.length == 0) {
+ throw new BaseException("Bootstrap at least one component before injecting Router.");
+ }
+ return app.componentTypes[0];
+}
diff --git a/modules/angular2/router/router_link_dsl.ts b/modules/angular2/router/router_link_dsl.ts
new file mode 100644
index 000000000000..5f108b826f68
--- /dev/null
+++ b/modules/angular2/router/router_link_dsl.ts
@@ -0,0 +1,35 @@
+import {TEMPLATE_TRANSFORMS} from 'angular2/compiler';
+import {Provider} from 'angular2/core';
+import {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
+import {CONST_EXPR} from 'angular2/src/facade/lang';
+
+export {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
+
+/**
+ * Enables the router link DSL.
+ *
+ * Warning. This feature is experimental and can change.
+ *
+ * To enable the transformer pass the router link DSL provider to `bootstrap`.
+ *
+ * ## Example:
+ * ```
+ * import {bootstrap} from 'angular2/platform/browser';
+ * import {ROUTER_LINK_DSL_PROVIDER} from 'angular2/router/router_link_dsl';
+ *
+ * bootstrap(CustomApp, [ROUTER_LINK_DSL_PROVIDER]);
+ * ```
+ *
+ * The DSL allows you to express router links as follows:
+ * ```
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+export const ROUTER_LINK_DSL_PROVIDER =
+ CONST_EXPR(new Provider(TEMPLATE_TRANSFORMS, {useClass: RouterLinkTransform, multi: true}));
diff --git a/modules/angular2/router/testing.ts b/modules/angular2/router/testing.ts
new file mode 100644
index 000000000000..d565555caa44
--- /dev/null
+++ b/modules/angular2/router/testing.ts
@@ -0,0 +1,2 @@
+export * from 'angular2/src/mock/mock_location_strategy';
+export * from 'angular2/src/mock/location_mock';
diff --git a/modules/angular2/src/animate/animation.ts b/modules/angular2/src/animate/animation.ts
new file mode 100644
index 000000000000..4e7375898980
--- /dev/null
+++ b/modules/angular2/src/animate/animation.ts
@@ -0,0 +1,200 @@
+import {
+ DateWrapper,
+ StringWrapper,
+ RegExpWrapper,
+ NumberWrapper,
+ isPresent
+} from 'angular2/src/facade/lang';
+import {Math} from 'angular2/src/facade/math';
+import {camelCaseToDashCase} from 'angular2/src/platform/dom/util';
+import {StringMapWrapper} from 'angular2/src/facade/collection';
+import {DOM} from 'angular2/src/platform/dom/dom_adapter';
+
+import {BrowserDetails} from './browser_details';
+import {CssAnimationOptions} from './css_animation_options';
+
+export class Animation {
+ /** functions to be called upon completion */
+ callbacks: Function[] = [];
+
+ /** the duration (ms) of the animation (whether from CSS or manually set) */
+ computedDuration: number;
+
+ /** the animation delay (ms) (whether from CSS or manually set) */
+ computedDelay: number;
+
+ /** timestamp of when the animation started */
+ startTime: number;
+
+ /** functions for removing event listeners */
+ eventClearFunctions: Function[] = [];
+
+ /** flag used to track whether or not the animation has finished */
+ completed: boolean = false;
+
+ private _stringPrefix: string = '';
+
+ /** total amount of time that the animation should take including delay */
+ get totalTime(): number {
+ let delay = this.computedDelay != null ? this.computedDelay : 0;
+ let duration = this.computedDuration != null ? this.computedDuration : 0;
+ return delay + duration;
+ }
+
+ /**
+ * Stores the start time and starts the animation
+ * @param element
+ * @param data
+ * @param browserDetails
+ */
+ constructor(public element: HTMLElement, public data: CssAnimationOptions,
+ public browserDetails: BrowserDetails) {
+ this.startTime = DateWrapper.toMillis(DateWrapper.now());
+ this._stringPrefix = DOM.getAnimationPrefix();
+ this.setup();
+ this.wait(timestamp => this.start());
+ }
+
+ wait(callback: Function) {
+ // Firefox requires 2 frames for some reason
+ this.browserDetails.raf(callback, 2);
+ }
+
+ /**
+ * Sets up the initial styles before the animation is started
+ */
+ setup(): void {
+ if (this.data.fromStyles != null) this.applyStyles(this.data.fromStyles);
+ if (this.data.duration != null)
+ this.applyStyles({'transitionDuration': this.data.duration.toString() + 'ms'});
+ if (this.data.delay != null)
+ this.applyStyles({'transitionDelay': this.data.delay.toString() + 'ms'});
+ }
+
+ /**
+ * After the initial setup has occurred, this method adds the animation styles
+ */
+ start(): void {
+ this.addClasses(this.data.classesToAdd);
+ this.addClasses(this.data.animationClasses);
+ this.removeClasses(this.data.classesToRemove);
+ if (this.data.toStyles != null) this.applyStyles(this.data.toStyles);
+ var computedStyles = DOM.getComputedStyle(this.element);
+ this.computedDelay =
+ Math.max(this.parseDurationString(
+ computedStyles.getPropertyValue(this._stringPrefix + 'transition-delay')),
+ this.parseDurationString(
+ this.element.style.getPropertyValue(this._stringPrefix + 'transition-delay')));
+ this.computedDuration = Math.max(this.parseDurationString(computedStyles.getPropertyValue(
+ this._stringPrefix + 'transition-duration')),
+ this.parseDurationString(this.element.style.getPropertyValue(
+ this._stringPrefix + 'transition-duration')));
+ this.addEvents();
+ }
+
+ /**
+ * Applies the provided styles to the element
+ * @param styles
+ */
+ applyStyles(styles: {[key: string]: any}): void {
+ StringMapWrapper.forEach(styles, (value, key) => {
+ var dashCaseKey = camelCaseToDashCase(key);
+ if (isPresent(DOM.getStyle(this.element, dashCaseKey))) {
+ DOM.setStyle(this.element, dashCaseKey, value.toString());
+ } else {
+ DOM.setStyle(this.element, this._stringPrefix + dashCaseKey, value.toString());
+ }
+ });
+ }
+
+ /**
+ * Adds the provided classes to the element
+ * @param classes
+ */
+ addClasses(classes: string[]): void {
+ for (let i = 0, len = classes.length; i < len; i++) DOM.addClass(this.element, classes[i]);
+ }
+
+ /**
+ * Removes the provided classes from the element
+ * @param classes
+ */
+ removeClasses(classes: string[]): void {
+ for (let i = 0, len = classes.length; i < len; i++) DOM.removeClass(this.element, classes[i]);
+ }
+
+ /**
+ * Adds events to track when animations have finished
+ */
+ addEvents(): void {
+ if (this.totalTime > 0) {
+ this.eventClearFunctions.push(DOM.onAndCancel(
+ this.element, DOM.getTransitionEnd(), (event: any) => this.handleAnimationEvent(event)));
+ } else {
+ this.handleAnimationCompleted();
+ }
+ }
+
+ handleAnimationEvent(event: any): void {
+ let elapsedTime = Math.round(event.elapsedTime * 1000);
+ if (!this.browserDetails.elapsedTimeIncludesDelay) elapsedTime += this.computedDelay;
+ event.stopPropagation();
+ if (elapsedTime >= this.totalTime) this.handleAnimationCompleted();
+ }
+
+ /**
+ * Runs all animation callbacks and removes temporary classes
+ */
+ handleAnimationCompleted(): void {
+ this.removeClasses(this.data.animationClasses);
+ this.callbacks.forEach(callback => callback());
+ this.callbacks = [];
+ this.eventClearFunctions.forEach(fn => fn());
+ this.eventClearFunctions = [];
+ this.completed = true;
+ }
+
+ /**
+ * Adds animation callbacks to be called upon completion
+ * @param callback
+ * @returns {Animation}
+ */
+ onComplete(callback: Function): Animation {
+ if (this.completed) {
+ callback();
+ } else {
+ this.callbacks.push(callback);
+ }
+ return this;
+ }
+
+ /**
+ * Converts the duration string to the number of milliseconds
+ * @param duration
+ * @returns {number}
+ */
+ parseDurationString(duration: string): number {
+ var maxValue = 0;
+ // duration must have at least 2 characters to be valid. (number + type)
+ if (duration == null || duration.length < 2) {
+ return maxValue;
+ } else if (duration.substring(duration.length - 2) == 'ms') {
+ let value = NumberWrapper.parseInt(this.stripLetters(duration), 10);
+ if (value > maxValue) maxValue = value;
+ } else if (duration.substring(duration.length - 1) == 's') {
+ let ms = NumberWrapper.parseFloat(this.stripLetters(duration)) * 1000;
+ let value = Math.floor(ms);
+ if (value > maxValue) maxValue = value;
+ }
+ return maxValue;
+ }
+
+ /**
+ * Strips the letters from the duration string
+ * @param str
+ * @returns {string}
+ */
+ stripLetters(str: string): string {
+ return StringWrapper.replaceAll(str, RegExpWrapper.create('[^0-9]+$', ''), '');
+ }
+}
diff --git a/modules/angular2/src/animate/animation_builder.ts b/modules/angular2/src/animate/animation_builder.ts
new file mode 100644
index 000000000000..3b82e629ffca
--- /dev/null
+++ b/modules/angular2/src/animate/animation_builder.ts
@@ -0,0 +1,19 @@
+import {Injectable} from 'angular2/src/core/di';
+
+import {CssAnimationBuilder} from './css_animation_builder';
+import {BrowserDetails} from './browser_details';
+
+@Injectable()
+export class AnimationBuilder {
+ /**
+ * Used for DI
+ * @param browserDetails
+ */
+ constructor(public browserDetails: BrowserDetails) {}
+
+ /**
+ * Creates a new CSS Animation
+ * @returns {CssAnimationBuilder}
+ */
+ css(): CssAnimationBuilder { return new CssAnimationBuilder(this.browserDetails); }
+}
diff --git a/modules/angular2/src/animate/browser_details.ts b/modules/angular2/src/animate/browser_details.ts
new file mode 100644
index 000000000000..8c0316321ff3
--- /dev/null
+++ b/modules/angular2/src/animate/browser_details.ts
@@ -0,0 +1,54 @@
+import {Injectable} from 'angular2/src/core/di';
+import {Math} from 'angular2/src/facade/math';
+import {DOM} from 'angular2/src/platform/dom/dom_adapter';
+
+@Injectable()
+export class BrowserDetails {
+ elapsedTimeIncludesDelay = false;
+
+ constructor() { this.doesElapsedTimeIncludesDelay(); }
+
+ /**
+ * Determines if `event.elapsedTime` includes transition delay in the current browser. At this
+ * time, Chrome and Opera seem to be the only browsers that include this.
+ */
+ doesElapsedTimeIncludesDelay(): void {
+ var div = DOM.createElement('div');
+ DOM.setAttribute(div, 'style', `position: absolute; top: -9999px; left: -9999px; width: 1px;
+ height: 1px; transition: all 1ms linear 1ms;`);
+ // Firefox requires that we wait for 2 frames for some reason
+ this.raf(timestamp => {
+ DOM.on(div, 'transitionend', (event: any) => {
+ var elapsed = Math.round(event.elapsedTime * 1000);
+ this.elapsedTimeIncludesDelay = elapsed == 2;
+ DOM.remove(div);
+ });
+ DOM.setStyle(div, 'width', '2px');
+ }, 2);
+ }
+
+ raf(callback: Function, frames: number = 1): Function {
+ var queue: RafQueue = new RafQueue(callback, frames);
+ return () => queue.cancel();
+ }
+}
+
+class RafQueue {
+ currentFrameId: number;
+ constructor(public callback: Function, public frames: number) { this._raf(); }
+ private _raf() {
+ this.currentFrameId = DOM.requestAnimationFrame(timestamp => this._nextFrame(timestamp));
+ }
+ private _nextFrame(timestamp: number) {
+ this.frames--;
+ if (this.frames > 0) {
+ this._raf();
+ } else {
+ this.callback(timestamp);
+ }
+ }
+ cancel() {
+ DOM.cancelAnimationFrame(this.currentFrameId);
+ this.currentFrameId = null;
+ }
+}
diff --git a/modules/angular2/src/animate/css_animation_builder.ts b/modules/angular2/src/animate/css_animation_builder.ts
new file mode 100644
index 000000000000..502d7f64a571
--- /dev/null
+++ b/modules/angular2/src/animate/css_animation_builder.ts
@@ -0,0 +1,93 @@
+import {CssAnimationOptions} from './css_animation_options';
+import {Animation} from './animation';
+import {BrowserDetails} from './browser_details';
+
+export class CssAnimationBuilder {
+ /** @type {CssAnimationOptions} */
+ data: CssAnimationOptions = new CssAnimationOptions();
+
+ /**
+ * Accepts public properties for CssAnimationBuilder
+ */
+ constructor(public browserDetails: BrowserDetails) {}
+
+ /**
+ * Adds a temporary class that will be removed at the end of the animation
+ * @param className
+ */
+ addAnimationClass(className: string): CssAnimationBuilder {
+ this.data.animationClasses.push(className);
+ return this;
+ }
+
+ /**
+ * Adds a class that will remain on the element after the animation has finished
+ * @param className
+ */
+ addClass(className: string): CssAnimationBuilder {
+ this.data.classesToAdd.push(className);
+ return this;
+ }
+
+ /**
+ * Removes a class from the element
+ * @param className
+ */
+ removeClass(className: string): CssAnimationBuilder {
+ this.data.classesToRemove.push(className);
+ return this;
+ }
+
+ /**
+ * Sets the animation duration (and overrides any defined through CSS)
+ * @param duration
+ */
+ setDuration(duration: number): CssAnimationBuilder {
+ this.data.duration = duration;
+ return this;
+ }
+
+ /**
+ * Sets the animation delay (and overrides any defined through CSS)
+ * @param delay
+ */
+ setDelay(delay: number): CssAnimationBuilder {
+ this.data.delay = delay;
+ return this;
+ }
+
+ /**
+ * Sets styles for both the initial state and the destination state
+ * @param from
+ * @param to
+ */
+ setStyles(from: {[key: string]: any}, to: {[key: string]: any}): CssAnimationBuilder {
+ return this.setFromStyles(from).setToStyles(to);
+ }
+
+ /**
+ * Sets the initial styles for the animation
+ * @param from
+ */
+ setFromStyles(from: {[key: string]: any}): CssAnimationBuilder {
+ this.data.fromStyles = from;
+ return this;
+ }
+
+ /**
+ * Sets the destination styles for the animation
+ * @param to
+ */
+ setToStyles(to: {[key: string]: any}): CssAnimationBuilder {
+ this.data.toStyles = to;
+ return this;
+ }
+
+ /**
+ * Starts the animation and returns a promise
+ * @param element
+ */
+ start(element: HTMLElement): Animation {
+ return new Animation(element, this.data, this.browserDetails);
+ }
+}
diff --git a/modules/angular2/src/animate/css_animation_options.ts b/modules/angular2/src/animate/css_animation_options.ts
new file mode 100644
index 000000000000..eab07b893a83
--- /dev/null
+++ b/modules/angular2/src/animate/css_animation_options.ts
@@ -0,0 +1,22 @@
+export class CssAnimationOptions {
+ /** initial styles for the element */
+ fromStyles: {[key: string]: any};
+
+ /** destination styles for the element */
+ toStyles: {[key: string]: any};
+
+ /** classes to be added to the element */
+ classesToAdd: string[] = [];
+
+ /** classes to be removed from the element */
+ classesToRemove: string[] = [];
+
+ /** classes to be added for the duration of the animation */
+ animationClasses: string[] = [];
+
+ /** override the duration of the animation (in milliseconds) */
+ duration: number;
+
+ /** override the transition delay (in milliseconds) */
+ delay: number;
+}
diff --git a/modules/angular2/src/change_detection/abstract_change_detector.js b/modules/angular2/src/change_detection/abstract_change_detector.js
deleted file mode 100644
index a5274e1680f9..000000000000
--- a/modules/angular2/src/change_detection/abstract_change_detector.js
+++ /dev/null
@@ -1,62 +0,0 @@
-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';
-
-export class AbstractChangeDetector extends ChangeDetector {
- children:List;
- parent:ChangeDetector;
- mode:string;
-
- constructor() {
- super();
- this.children = [];
- this.mode = CHECK_ALWAYS;
- }
-
- addChild(cd:ChangeDetector) {
- ListWrapper.push(this.children, cd);
- cd.parent = this;
- }
-
- removeChild(cd:ChangeDetector) {
- ListWrapper.remove(this.children, cd);
- }
-
- remove() {
- this.parent.removeChild(this);
- }
-
- detectChanges() {
- this._detectChanges(false);
- }
-
- checkNoChanges() {
- this._detectChanges(true);
- }
-
- _detectChanges(throwOnChange:boolean) {
- if (this.mode === DETACHED || this.mode === CHECKED) return;
-
- this.detectChangesInRecords(throwOnChange);
- this._detectChangesInChildren(throwOnChange);
-
- if (this.mode === CHECK_ONCE) this.mode = CHECKED;
- }
-
- detectChangesInRecords(throwOnChange:boolean){}
-
- _detectChangesInChildren(throwOnChange:boolean) {
- var children = this.children;
- for(var i = 0; i < children.length; ++i) {
- children[i]._detectChanges(throwOnChange);
- }
- }
-
- markPathToRootAsCheckOnce() {
- var c = this;
- while(isPresent(c) && c.mode != DETACHED) {
- if (c.mode === CHECKED) c.mode = CHECK_ONCE;
- c = c.parent;
- }
- }
-}
diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.dart b/modules/angular2/src/change_detection/change_detection_jit_generator.dart
deleted file mode 100644
index 32905f20c9bd..000000000000
--- a/modules/angular2/src/change_detection/change_detection_jit_generator.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-library change_detectoin.change_detection_jit_generator;
-
-class ChangeDetectorJITGenerator {
- ChangeDetectorJITGenerator(typeName, records) {
- }
-
- generate() {
- throw "Not supported in Dart";
- }
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 b/modules/angular2/src/change_detection/change_detection_jit_generator.es6
deleted file mode 100644
index 2f63bb529b6c..000000000000
--- a/modules/angular2/src/change_detection/change_detection_jit_generator.es6
+++ /dev/null
@@ -1,468 +0,0 @@
-import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
-import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-
-import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
-import {AbstractChangeDetector} from './abstract_change_detector';
-import {ChangeDetectionUtil} from './change_detection_util';
-
-import {
- ProtoRecord,
- RECORD_TYPE_SELF,
- RECORD_TYPE_PROPERTY,
- RECORD_TYPE_INVOKE_METHOD,
- RECORD_TYPE_CONST,
- RECORD_TYPE_INVOKE_CLOSURE,
- RECORD_TYPE_PRIMITIVE_OP,
- RECORD_TYPE_KEYED_ACCESS,
- RECORD_TYPE_PIPE,
- RECORD_TYPE_INTERPOLATE
- } from './proto_record';
-
-/**
- * The code generator takes a list of proto records and creates a function/class
- * 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;
- *
- * temp = ChangeDetectionUtil.findContext("address", context);
- * if (temp instanceof ContextWithVariableBindings) {
- * address0 = temp.get('address');
- * } else {
- * address0 = temp.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) {
- * this.context = context;
- * }
- *
- * ChangeDetector0.prototype.dehydrate = function(context) {
- * this.context = ChangeDetectionUtil.unitialized();
- * this.address0 = ChangeDetectionUtil.unitialized();
- * this.city1 = ChangeDetectionUtil.unitialized();
- * }
- *
- * 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 CHANGE_LOCAL = "change";
-var CHANGES_LOCAL = "changes";
-var TEMP_LOCAL = "temp";
-
-function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
- return `
-${cons}
-${detectChanges}
-${setContext};
-
-return function(dispatcher, pipeRegistry) {
- return new ${type}(dispatcher, pipeRegistry, protos);
-}
-`;
-}
-
-function constructorTemplate(type:string, fieldsDefinitions:string):string {
- return `
-var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
-${ABSTRACT_CHANGE_DETECTOR}.call(this);
-${DISPATCHER_ACCESSOR} = dispatcher;
-${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
-${PROTOS_ACCESSOR} = protos;
-${fieldsDefinitions}
-}
-
-${type}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
-`;
-}
-
-function pipeOnDestroyTemplate(pipeNames:List) {
- return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
-}
-
-function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
- return `
-${type}.prototype.hydrate = function(context) {
- this.context = context;
-}
-${type}.prototype.dehydrate = function() {
- ${pipeOnDestroy}
- ${fieldsDefinitions}
-}
-${type}.prototype.hydrated = function() {
- return this.context !== ${UTIL}.unitialized();
-}
-`;
-}
-
-function detectChangesTemplate(type:string, body:string):string {
- return `
-${type}.prototype.detectChangesInRecords = function(throwOnChange) {
- ${body}
-}
-`;
-}
-
-
-function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
- return `
-${localDefinitions}
-${changeDefinitions}
-var ${TEMP_LOCAL};
-var ${CHANGE_LOCAL};
-var ${CHANGES_LOCAL} = null;
-
-context = this.context;
-${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{
- return `
-if (${pipe} === ${UTIL}.unitialized()) {
- ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
-} else if (!${pipe}.supports(${context})) {
- ${pipe}.onDestroy();
- ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
-}
-
-${CHANGE_LOCAL} = ${pipe}.transform(${context});
-if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) {
- ${value} = ${CHANGE_LOCAL};
- ${change} = true;
- ${addRecord}
-}
-${notify}
-`;
-}
-
-function referenceCheckTemplate(assignment, newValue, oldValue, change, addRecord, notify) {
- return `
-${assignment}
-if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
- ${change} = true;
- ${addRecord}
- ${oldValue} = ${newValue};
-}
-${notify}
-`;
-}
-
-function assignmentTemplate(field:string, value:string) {
- return `${field} = ${value};`;
-}
-
-function propertyReadTemplate(name:string, context:string, newValue:string) {
- return `
-${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context});
-if (${TEMP_LOCAL} instanceof ContextWithVariableBindings) {
- ${newValue} = ${TEMP_LOCAL}.get('${name}');
-} else {
- ${newValue} = ${TEMP_LOCAL}.${name};
-}
-`;
-}
-
-function invokeMethodTemplate(name:string, args:string, context:string, newValue:string) {
- return `
-${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context});
-if (${TEMP_LOCAL} instanceof ContextWithVariableBindings) {
- ${newValue} = ${TEMP_LOCAL}.get('${name}').apply(null, [${args}]);
-} else {
- ${newValue} = ${context}.${name}(${args});
-}
-`;
-}
-
-function localDefinitionsTemplate(names:List):string {
- return names.map((n) => `var ${n};`).join("\n");
-}
-
-function changeDefinitionsTemplate(names:List):string {
- return names.map((n) => `var ${n} = false;`).join("\n");
-}
-
-function fieldDefinitionsTemplate(names:List):string {
- return names.map((n) => `${n} = ${UTIL}.unitialized();`).join("\n");
-}
-
-function ifChangedGuardTemplate(changeNames:List, body:string):string {
- var cond = changeNames.join(" || ");
- return `
-if (${cond}) {
- ${body}
-}
-`;
-}
-
-function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newValue:string) {
- return `${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
- ${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`;
-}
-
-
-export class ChangeDetectorJITGenerator {
- typeName:string;
- records:List;
- localNames:List;
- changeNames:List;
- fieldNames:List;
- pipeNames:List;
-
- constructor(typeName:string, records:List) {
- this.typeName = typeName;
- this.records = records;
-
- this.localNames = this.getLocalNames(records);
- this.changeNames = this.getChangeNames(this.localNames);
- this.fieldNames = this.getFieldNames(this.localNames);
- this.pipeNames = this.getPipeNames(this.localNames);
- }
-
- getLocalNames(records:List):List {
- var index = 0;
- var names = records.map((r) => {
- var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
- return `${sanitizedName}${index++}`
- });
- return ["context"].concat(names);
- }
-
- getChangeNames(localNames:List):List {
- return localNames.map((n) => `change_${n}`);
- }
-
- getFieldNames(localNames:List):List {
- return localNames.map((n) => `this.${n}`);
- }
-
- getPipeNames(localNames:List):List {
- 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', 'ContextWithVariableBindings', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, ContextWithVariableBindings, this.records);
- }
-
- genConstructor():string {
- return constructorTemplate(this.typeName, this.genFieldDefinitions());
- }
-
- genHydrate():string {
- return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
- pipeOnDestroyTemplate(this.getnonNullPipeNames()));
- }
-
- genFieldDefinitions() {
- var fields = [];
- fields = fields.concat(this.fieldNames);
- fields = fields.concat(this.getnonNullPipeNames());
- return fieldDefinitionsTemplate(fields);
- }
-
- getnonNullPipeNames():List {
- var pipes = [];
- this.records.forEach((r) => {
- if (r.mode === RECORD_TYPE_PIPE) {
- pipes.push(this.pipeNames[r.selfIndex]);
- }
- });
- return pipes;
- }
-
- genDetectChanges():string {
- var body = this.genBody();
- return detectChangesTemplate(this.typeName, body);
- }
-
- genBody():string {
- var rec = this.records.map((r) => this.genRecord(r)).join("\n");
- return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
- }
-
- genLocalDefinitions():string {
- return localDefinitionsTemplate(this.localNames);
- }
-
- genChangeDefinitions():string {
- return changeDefinitionsTemplate(this.changeNames);
- }
-
- genRecord(r:ProtoRecord):string {
- if (r.mode === RECORD_TYPE_PIPE) {
- return this.genPipeCheck (r);
- } else {
- return this.genReferenceCheck(r);
- }
- }
-
- 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 change = this.changeNames[r.selfIndex];
-
- var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
- var notify = this.genNotify(r);
-
- return pipeCheckTemplate(context, pipe, r.name, newValue, change, addRecord, notify);
- }
-
- genReferenceCheck(r:ProtoRecord):string {
- var newValue = this.localNames[r.selfIndex];
- var oldValue = this.fieldNames[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);;
- if (r.isPureFunction()) {
- return this.ifChangedGuard(r, check);
- } else {
- return check;
- }
- }
-
- genUpdateCurrentValue(r:ProtoRecord):string {
- var context = this.localNames[r.contextIndex];
- var newValue = this.localNames[r.selfIndex];
- var args = this.genArgs(r);
-
- switch (r.mode) {
- case RECORD_TYPE_SELF:
- return assignmentTemplate(newValue, context);
-
- case RECORD_TYPE_CONST:
- return `${newValue} = ${this.genLiteral(r.funcOrValue)}`;
-
- case RECORD_TYPE_PROPERTY:
- if (r.contextIndex == 0) { // only the first property read can be a local
- return propertyReadTemplate(r.name, context, newValue);
- } else {
- return assignmentTemplate(newValue, `${context}.${r.name}`);
- }
-
- case RECORD_TYPE_INVOKE_METHOD:
- if (r.contextIndex == 0) { // only the first property read can be a local
- return invokeMethodTemplate(r.name, args, context, newValue);
- } else {
- return assignmentTemplate(newValue, `${context}.${r.name}(${args})`);
- }
-
- case RECORD_TYPE_INVOKE_CLOSURE:
- return assignmentTemplate(newValue, `${context}(${args})`);
-
- case RECORD_TYPE_PRIMITIVE_OP:
- return assignmentTemplate(newValue, `${UTIL}.${r.name}(${args})`);
-
- case RECORD_TYPE_INTERPOLATE:
- return assignmentTemplate(newValue, this.genInterpolation(r));
-
- case RECORD_TYPE_KEYED_ACCESS:
- var key = this.localNames[r.args[0]];
- return assignmentTemplate(newValue, `${context}[${key}]`);
-
- default:
- throw new BaseException(`Unknown operation ${r.mode}`);
- }
- }
-
- ifChangedGuard(r:ProtoRecord, body:string):string {
- return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
- }
-
- genInterpolation(r:ProtoRecord):string{
- var res = "";
- for (var i = 0; i < r.args.length; ++i) {
- res += this.genLiteral(r.fixedArgs[i]);
- res += " + ";
- res += this.localNames[r.args[i]];
- res += " + ";
- }
- res += this.genLiteral(r.fixedArgs[r.args.length]);
- return res;
- }
-
- genLiteral(value):string {
- return JSON.stringify(value);
- }
-
- genNotify(r):string{
- return r.lastInDirective ? notifyTemplate(r.selfIndex - 1) : '';
- }
-
- genArgs(r:ProtoRecord):string {
- return r.args.map((arg) => this.localNames[arg]).join(", ");
- }
-}
-
-
-
-
diff --git a/modules/angular2/src/change_detection/change_detection_util.js b/modules/angular2/src/change_detection/change_detection_util.js
deleted file mode 100644
index fa8bfc5a0a4b..000000000000
--- a/modules/angular2/src/change_detection/change_detection_util.js
+++ /dev/null
@@ -1,190 +0,0 @@
-import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
-import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
-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';
-
-export var uninitialized = new Object();
-
-export class SimpleChange {
- previousValue:any;
- currentValue:any;
-
- constructor(previousValue:any, currentValue:any) {
- this.previousValue = previousValue;
- this.currentValue = currentValue;
- }
-}
-
-var _simpleChangesIndex = 0;
-var _simpleChanges = [
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- new SimpleChange(null, null),
- 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;
- var s = _simpleChanges[index];
- s.previousValue = previousValue;
- s.currentValue = 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;
- }
-
- static arrayFn0() { return []; }
- static arrayFn1(a1) { return [a1]; }
- static arrayFn2(a1, a2) { return [a1, a2]; }
- static arrayFn3(a1, a2, a3) { return [a1, a2, a3]; }
- static arrayFn4(a1, a2, a3, a4) { return [a1, a2, a3, a4]; }
- static arrayFn5(a1, a2, a3, a4, a5) { return [a1, a2, a3, a4, a5]; }
- static arrayFn6(a1, a2, a3, a4, a5, a6) { return [a1, a2, a3, a4, a5, a6]; }
- static arrayFn7(a1, a2, a3, a4, a5, a6, a7) { return [a1, a2, a3, a4, a5, a6, a7]; }
- static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8) { return [a1, a2, a3, a4, a5, a6, a7, a8]; }
- static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { return [a1, a2, a3, a4, a5, a6, a7, a8, a9]; }
-
- static operation_negate(value) {return !value;}
- static operation_add(left, right) {return left + right;}
- static operation_subtract(left, right) {return left - right;}
- static operation_multiply(left, right) {return left * right;}
- static operation_divide(left, right) {return left / right;}
- static operation_remainder(left, right) {return left % right;}
- static operation_equals(left, right) {return left == right;}
- static operation_not_equals(left, right) {return left != right;}
- static operation_less_then(left, right) {return left < right;}
- static operation_greater_then(left, right) {return left > right;}
- static operation_less_or_equals_then(left, right) {return left <= right;}
- static operation_greater_or_equals_then(left, right) {return left >= right;}
- static operation_logical_and(left, right) {return left && right;}
- static operation_logical_or(left, right) {return left || right;}
- static cond(cond, trueVal, falseVal) {return cond ? trueVal : falseVal;}
-
- static mapFn(keys:List) {
- function buildMap(values) {
- var res = StringMapWrapper.create();
- for(var i = 0; i < keys.length; ++i) {
- StringMapWrapper.set(res, keys[i], values[i]);
- }
- return res;
- }
-
- switch (keys.length) {
- case 0: return () => [];
- case 1: return (a1) => buildMap([a1]);
- case 2: return (a1, a2) => buildMap([a1, a2]);
- case 3: return (a1, a2, a3) => buildMap([a1, a2, a3]);
- case 4: return (a1, a2, a3, a4) => buildMap([a1, a2, a3, a4]);
- case 5: return (a1, a2, a3, a4, a5) => buildMap([a1, a2, a3, a4, a5]);
- case 6: return (a1, a2, a3, a4, a5, a6) => buildMap([a1, a2, a3, a4, a5, a6]);
- case 7: return (a1, a2, a3, a4, a5, a6, a7) => buildMap([a1, a2, a3, a4, a5, a6, a7]);
- case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8]);
- case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8, a9]);
- default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
- }
- }
-
- static keyedAccess(obj, args) {
- return obj[args[0]];
- }
-
- static findContext(name:string, c){
- while (c instanceof ContextWithVariableBindings) {
- if (c.hasBinding(name)) {
- return c;
- }
- c = c.parent;
- }
- return c;
- }
-
- static noChangeMarker(value):boolean {
- return value === NO_CHANGE;
- }
-
- static throwOnChange(proto:ProtoRecord, change) {
- throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
- }
-
- 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);
- }
- return updatedRecords;
- }
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/coalesce.js b/modules/angular2/src/change_detection/coalesce.js
deleted file mode 100644
index a1d0e429def4..000000000000
--- a/modules/angular2/src/change_detection/coalesce.js
+++ /dev/null
@@ -1,88 +0,0 @@
-import {isPresent} from 'angular2/src/facade/lang';
-import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
-import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
-
-/**
- * Removes "duplicate" records. It assuming that record evaluation does not
- * have side-effects.
- *
- * Records that are not last in bindings are removed and all the indices
- * of the records that depend on them are updated.
- *
- * Records that are last in bindings CANNOT be removed, and instead are
- * replaced with very cheap SELF records.
- */
-export function coalesce(records:List):List {
- var res = ListWrapper.create();
- var indexMap = MapWrapper.create();
-
- for (var i = 0; i < records.length; ++i) {
- var r = records[i];
- var record = _replaceIndices(r, res.length + 1, indexMap);
- var matchingRecord = _findMatching(record, res);
-
- if (isPresent(matchingRecord) && record.lastInBinding) {
- ListWrapper.push(res, _selfRecord(record, matchingRecord.selfIndex, res.length + 1));
- MapWrapper.set(indexMap, r.selfIndex, matchingRecord.selfIndex);
-
- } else if (isPresent(matchingRecord) && !record.lastInBinding) {
- MapWrapper.set(indexMap, r.selfIndex, matchingRecord.selfIndex);
-
- } else {
- ListWrapper.push(res, record);
- MapWrapper.set(indexMap, r.selfIndex, record.selfIndex);
- }
- }
-
- return res;
-}
-
-function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):ProtoRecord {
- return new ProtoRecord(
- RECORD_TYPE_SELF,
- "self",
- null,
- [],
- r.fixedArgs,
- contextIndex,
- selfIndex,
- r.bindingMemento,
- r.directiveMemento,
- r.expressionAsString,
- r.lastInBinding,
- r.lastInDirective
- );
-}
-
-function _findMatching(r:ProtoRecord, rs:List){
- return ListWrapper.find(rs, (rr) =>
- rr.mode === r.mode &&
- rr.funcOrValue === r.funcOrValue &&
- rr.contextIndex === r.contextIndex &&
- ListWrapper.equals(rr.args, r.args)
- );
-}
-
-function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
- var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
- var contextIndex = _map(indexMap, r.contextIndex);
- return new ProtoRecord(
- r.mode,
- r.name,
- r.funcOrValue,
- args,
- r.fixedArgs,
- contextIndex,
- selfIndex,
- r.bindingMemento,
- r.directiveMemento,
- r.expressionAsString,
- r.lastInBinding,
- r.lastInDirective
- );
-}
-
-function _map(indexMap:Map, value:number) {
- var r = MapWrapper.get(indexMap, value)
- return isPresent(r) ? r : value;
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.js b/modules/angular2/src/change_detection/dynamic_change_detector.js
deleted file mode 100644
index 2243864afce5..000000000000
--- a/modules/angular2/src/change_detection/dynamic_change_detector.js
+++ /dev/null
@@ -1,272 +0,0 @@
-import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
-import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
-
-import {AbstractChangeDetector} from './abstract_change_detector';
-import {PipeRegistry} from './pipes/pipe_registry';
-import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
-
-
-import {
- ProtoRecord,
- RECORD_TYPE_SELF,
- RECORD_TYPE_PROPERTY,
- RECORD_TYPE_INVOKE_METHOD,
- RECORD_TYPE_CONST,
- RECORD_TYPE_INVOKE_CLOSURE,
- RECORD_TYPE_PRIMITIVE_OP,
- RECORD_TYPE_KEYED_ACCESS,
- RECORD_TYPE_PIPE,
- RECORD_TYPE_INTERPOLATE
- } from './proto_record';
-
-import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './exceptions';
-
-export class DynamicChangeDetector extends AbstractChangeDetector {
- dispatcher:any;
- pipeRegistry;
-
- values:List;
- changes:List;
- pipes:List;
- prevContexts:List;
-
- protos:List;
-
- constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List) {
- super();
- this.dispatcher = dispatcher;
- this.pipeRegistry = pipeRegistry;
-
- this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
- this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
- this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
- this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
-
- ListWrapper.fill(this.values, uninitialized);
- ListWrapper.fill(this.pipes, null);
- ListWrapper.fill(this.prevContexts, uninitialized);
- ListWrapper.fill(this.changes, false);
-
- this.protos = protoRecords;
- }
-
- hydrate(context:any) {
- this.values[0] = context;
- }
-
- dehydrate() {
- this._destroyPipes();
- ListWrapper.fill(this.values, uninitialized);
- ListWrapper.fill(this.changes, false);
- ListWrapper.fill(this.pipes, null);
- ListWrapper.fill(this.prevContexts, uninitialized);
- }
-
- _destroyPipes() {
- for(var i = 0; i < this.pipes.length; ++i) {
- if (isPresent(this.pipes[i])) {
- this.pipes[i].onDestroy();
- }
- }
- }
-
- hydrated():boolean {
- return this.values[0] !== uninitialized;
- }
-
- detectChangesInRecords(throwOnChange:boolean) {
- var protos:List = this.protos;
-
- var updatedRecords = null;
- for (var i = 0; i < protos.length; ++i) {
- var proto:ProtoRecord = protos[i];
- var change = this._check(proto);
-
- if (isPresent(change)) {
- var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change);
- updatedRecords = ChangeDetectionUtil.addRecord(updatedRecords, record);
- }
-
- if (proto.lastInDirective && isPresent(updatedRecords)) {
- if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, updatedRecords[0]);
-
- this.dispatcher.onRecordChange(proto.directiveMemento, updatedRecords);
- updatedRecords = null;
- }
- }
- }
-
- _check(proto:ProtoRecord) {
- try {
- if (proto.mode == RECORD_TYPE_PIPE) {
- return this._pipeCheck(proto);
- } else {
- return this._referenceCheck(proto);
- }
- } catch (e) {
- throw new ChangeDetectionError(proto, e);
- }
- }
-
- _referenceCheck(proto:ProtoRecord) {
- if (this._pureFuncAndArgsDidNotChange(proto)) {
- this._setChanged(proto, false);
- return null;
- }
-
- var prevValue = this._readSelf(proto);
- var currValue = this._calculateCurrValue(proto);
-
- if (!isSame(prevValue, currValue)) {
- this._writeSelf(proto, currValue);
- this._setChanged(proto, true);
-
- if (proto.lastInBinding) {
- return ChangeDetectionUtil.simpleChange(prevValue, currValue);
- } else {
- return null;
- }
- } else {
- this._setChanged(proto, false);
- return null;
- }
- }
-
- _calculateCurrValue(proto:ProtoRecord) {
- switch (proto.mode) {
- case RECORD_TYPE_SELF:
- return this._readContext(proto);
-
- case RECORD_TYPE_CONST:
- return proto.funcOrValue;
-
- case RECORD_TYPE_PROPERTY:
- var context = this._readContext(proto);
- var c = ChangeDetectionUtil.findContext(proto.name, context);
- if (c instanceof ContextWithVariableBindings) {
- return c.get(proto.name);
- } else {
- var propertyGetter:Function = proto.funcOrValue;
- return propertyGetter(c);
- }
- break;
-
- case RECORD_TYPE_INVOKE_METHOD:
- var context = this._readContext(proto);
- var args = this._readArgs(proto);
- var c = ChangeDetectionUtil.findContext(proto.name, context);
- if (c instanceof ContextWithVariableBindings) {
- return FunctionWrapper.apply(c.get(proto.name), args);
- } else {
- var methodInvoker:Function = proto.funcOrValue;
- return methodInvoker(c, args);
- }
- break;
-
- case RECORD_TYPE_KEYED_ACCESS:
- var arg = this._readArgs(proto)[0];
- return this._readContext(proto)[arg];
-
- case RECORD_TYPE_INVOKE_CLOSURE:
- return FunctionWrapper.apply(this._readContext(proto), this._readArgs(proto));
-
- case RECORD_TYPE_INTERPOLATE:
- case RECORD_TYPE_PRIMITIVE_OP:
- return FunctionWrapper.apply(proto.funcOrValue, this._readArgs(proto));
-
- default:
- throw new BaseException(`Unknown operation ${proto.mode}`);
- }
- }
-
- _pipeCheck(proto:ProtoRecord) {
- var context = this._readContext(proto);
- var pipe = this._pipeFor(proto, context);
-
- var newValue = pipe.transform(context);
- if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
- var prevValue = this._readSelf(proto);
- this._writeSelf(proto, newValue);
- this._setChanged(proto, true);
-
- if (proto.lastInBinding) {
- return ChangeDetectionUtil.simpleChange(prevValue, newValue);
- } else {
- return null;
- }
- } else {
- this._setChanged(proto, false);
- return null;
- }
- }
-
- _pipeFor(proto:ProtoRecord, context) {
- var storedPipe = this._readPipe(proto);
- if (isPresent(storedPipe) && storedPipe.supports(context)) {
- return storedPipe;
- }
- if (isPresent(storedPipe)) {
- storedPipe.onDestroy();
- }
- var pipe = this.pipeRegistry.get(proto.name, context);
- this._writePipe(proto, pipe);
- return pipe;
- }
-
- _readContext(proto:ProtoRecord) {
- return this.values[proto.contextIndex];
- }
-
- _readSelf(proto:ProtoRecord) {
- return this.values[proto.selfIndex];
- }
-
- _writeSelf(proto:ProtoRecord, value) {
- this.values[proto.selfIndex] = value;
- }
-
- _readPipe(proto:ProtoRecord) {
- return this.pipes[proto.selfIndex];
- }
-
- _writePipe(proto:ProtoRecord, value) {
- this.pipes[proto.selfIndex] = value;
- }
-
- _setChanged(proto:ProtoRecord, value:boolean) {
- this.changes[proto.selfIndex] = value;
- }
-
- _pureFuncAndArgsDidNotChange(proto:ProtoRecord):boolean {
- return proto.isPureFunction() && !this._argsChanged(proto);
- }
-
- _argsChanged(proto:ProtoRecord):boolean {
- var args = proto.args;
- for(var i = 0; i < args.length; ++i) {
- if (this.changes[args[i]]) {
- return true;
- }
- }
- return false;
- }
-
- _readArgs(proto:ProtoRecord) {
- var res = ListWrapper.createFixedSize(proto.args.length);
- var args = proto.args;
- for (var i = 0; i < args.length; ++i) {
- res[i] = this.values[args[i]];
- }
- return res;
- }
-}
-
-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;
-}
diff --git a/modules/angular2/src/change_detection/exceptions.js b/modules/angular2/src/change_detection/exceptions.js
deleted file mode 100644
index 34c9cc1fbe29..000000000000
--- a/modules/angular2/src/change_detection/exceptions.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import {ProtoRecord} from './proto_record';
-
-export class ExpressionChangedAfterItHasBeenChecked extends Error {
- message:string;
-
- constructor(proto:ProtoRecord, change:any) {
- super();
- this.message = `Expression '${proto.expressionAsString}' has changed after it was checked. ` +
- `Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`;
- }
-
- toString():string {
- return this.message;
- }
-}
-
-export class ChangeDetectionError extends Error {
- message:string;
- originalException:any;
- location:string;
-
- constructor(proto:ProtoRecord, originalException:any) {
- super();
- this.originalException = originalException;
- this.location = proto.expressionAsString;
- this.message = `${this.originalException} in [${this.location}]`;
- }
-
- toString():string {
- return this.message;
- }
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/interfaces.js b/modules/angular2/src/change_detection/interfaces.js
deleted file mode 100644
index 5f7bbed50dc9..000000000000
--- a/modules/angular2/src/change_detection/interfaces.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import {List} from 'angular2/src/facade/collection';
-
-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;
- }
-}
-
-
-/**
- * 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 ChangeDispatcher {
- onRecordChange(directiveMemento, records:List) {}
-}
-
-export class ChangeDetector {
- parent:ChangeDetector;
- mode:string;
-
- addChild(cd:ChangeDetector) {}
- removeChild(cd:ChangeDetector) {}
- remove() {}
- hydrate(context:any) {}
- dehydrate() {}
- markPathToRootAsCheckOnce() {}
-
- detectChanges() {}
- checkNoChanges() {}
-}
diff --git a/modules/angular2/src/change_detection/parser/ast.js b/modules/angular2/src/change_detection/parser/ast.js
deleted file mode 100644
index e2f1307a1517..000000000000
--- a/modules/angular2/src/change_detection/parser/ast.js
+++ /dev/null
@@ -1,465 +0,0 @@
-import {FIELD, autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
-import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
-import {ContextWithVariableBindings} from "./context_with_variable_bindings";
-
-export class AST {
- eval(context) {
- throw new BaseException("Not supported");
- }
-
- get isAssignable() {
- return false;
- }
-
- assign(context, value) {
- throw new BaseException("Not supported");
- }
-
- visit(visitor) {
- }
-
- toString():string {
- return "AST";
- }
-}
-
-export class EmptyExpr extends AST {
- eval(context) {
- return null;
- }
-
- visit(visitor) {
- //do nothing
- }
-}
-
-export class ImplicitReceiver extends AST {
- eval(context) {
- return context;
- }
-
- visit(visitor) {
- return visitor.visitImplicitReceiver(this);
- }
-}
-
-/**
- * Multiple expressions separated by a semicolon.
- */
-export class Chain extends AST {
- expressions:List;
- constructor(expressions:List) {
- super();
- this.expressions = expressions;
- }
-
- eval(context) {
- var result;
- for (var i = 0; i < this.expressions.length; i++) {
- var last = this.expressions[i].eval(context);
- if (isPresent(last)) result = last;
- }
- return result;
- }
-
- visit(visitor) {
- return visitor.visitChain(this);
- }
-}
-
-export class Conditional extends AST {
- condition:AST;
- trueExp:AST;
- falseExp:AST;
- constructor(condition:AST, trueExp:AST, falseExp:AST){
- super();
- this.condition = condition;
- this.trueExp = trueExp;
- this.falseExp = falseExp;
- }
-
- eval(context) {
- if(this.condition.eval(context)) {
- return this.trueExp.eval(context);
- } else {
- return this.falseExp.eval(context);
- }
- }
-
- visit(visitor) {
- return visitor.visitConditional(this);
- }
-}
-
-export class AccessMember extends AST {
- receiver:AST;
- name:string;
- getter:Function;
- setter:Function;
- constructor(receiver:AST, name:string, getter:Function, setter:Function) {
- super();
- this.receiver = receiver;
- this.name = name;
- this.getter = getter;
- this.setter = setter;
- }
-
- eval(context) {
- var evaluatedContext = this.receiver.eval(context);
-
- while (evaluatedContext instanceof ContextWithVariableBindings) {
- if (evaluatedContext.hasBinding(this.name)) {
- return evaluatedContext.get(this.name);
- }
- evaluatedContext = evaluatedContext.parent;
- }
-
- return this.getter(evaluatedContext);
- }
-
- get isAssignable() {
- return true;
- }
-
- assign(context, value) {
- var evaluatedContext = this.receiver.eval(context);
-
- while (evaluatedContext instanceof ContextWithVariableBindings) {
- if (evaluatedContext.hasBinding(this.name)) {
- throw new BaseException(`Cannot reassign a variable binding ${this.name}`)
- }
- evaluatedContext = evaluatedContext.parent;
- }
-
- return this.setter(evaluatedContext, value);
- }
-
- visit(visitor) {
- return visitor.visitAccessMember(this);
- }
-}
-
-export class KeyedAccess extends AST {
- obj:AST;
- key:AST;
- constructor(obj:AST, key:AST) {
- super();
- this.obj = obj;
- this.key = key;
- }
-
- eval(context) {
- var obj = this.obj.eval(context);
- var key = this.key.eval(context);
- return obj[key];
- }
-
- get isAssignable() {
- return true;
- }
-
- assign(context, value) {
- var obj = this.obj.eval(context);
- var key = this.key.eval(context);
- obj[key] = value;
- return value;
- }
-
- visit(visitor) {
- return visitor.visitKeyedAccess(this);
- }
-}
-
-export class Pipe extends AST {
- exp:AST;
- name:string;
- args:List;
- constructor(exp:AST, name:string, args:List) {
- super();
- this.exp = exp;
- this.name = name;
- this.args = args;
- }
-
- visit(visitor) {
- return visitor.visitPipe(this);
- }
-}
-
-export class LiteralPrimitive extends AST {
- value;
- constructor(value) {
- super();
- this.value = value;
- }
-
- eval(context) {
- return this.value;
- }
-
- visit(visitor) {
- return visitor.visitLiteralPrimitive(this);
- }
-}
-
-export class LiteralArray extends AST {
- expressions:List;
- constructor(expressions:List) {
- super();
- this.expressions = expressions;
- }
-
- eval(context) {
- return ListWrapper.map(this.expressions, (e) => e.eval(context));
- }
-
- visit(visitor) {
- return visitor.visitLiteralArray(this);
- }
-}
-
-export class LiteralMap extends AST {
- keys:List;
- values:List;
- constructor(keys:List, values:List) {
- super();
- this.keys = keys;
- this.values = values;
- }
-
- eval(context) {
- var res = StringMapWrapper.create();
- for(var i = 0; i < this.keys.length; ++i) {
- StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context));
- }
- return res;
- }
-
- visit(visitor) {
- return visitor.visitLiteralMap(this);
- }
-}
-
-export class Interpolation extends AST {
- strings:List;
- expressions:List;
- constructor(strings:List, expressions:List) {
- super();
- this.strings = strings;
- this.expressions = expressions;
- }
-
- eval(context) {
- throw new BaseException("evaluating an Interpolation is not supported");
- }
-
- visit(visitor) {
- visitor.visitInterpolation(this);
- }
-}
-
-export class Binary extends AST {
- operation:string;
- left:AST;
- right:AST;
- constructor(operation:string, left:AST, right:AST) {
- super();
- this.operation = operation;
- this.left = left;
- this.right = right;
- }
-
- eval(context) {
- var left = this.left.eval(context);
- switch (this.operation) {
- case '&&': return left && this.right.eval(context);
- case '||': return left || this.right.eval(context);
- }
- var right = this.right.eval(context);
-
- switch (this.operation) {
- case '+' : return left + right;
- case '-' : return left - right;
- case '*' : return left * right;
- case '/' : return left / right;
- case '%' : return left % right;
- case '==' : return left == right;
- case '!=' : return left != right;
- case '<' : return left < right;
- case '>' : return left > right;
- case '<=' : return left <= right;
- case '>=' : return left >= right;
- case '^' : return left ^ right;
- case '&' : return left & right;
- }
- throw 'Internal error [$operation] not handled';
- }
-
- visit(visitor) {
- return visitor.visitBinary(this);
- }
-}
-
-export class PrefixNot extends AST {
- expression:AST;
- constructor(expression:AST) {
- super();
- this.expression = expression;
- }
-
- eval(context) {
- return !this.expression.eval(context);
- }
-
- visit(visitor) {
- return visitor.visitPrefixNot(this);
- }
-}
-
-export class Assignment extends AST {
- target:AST;
- value:AST;
- constructor(target:AST, value:AST) {
- super();
- this.target = target;
- this.value = value;
- }
-
- eval(context) {
- return this.target.assign(context, this.value.eval(context));
- }
-
- visit(visitor) {
- return visitor.visitAssignment(this);
- }
-}
-
-export class MethodCall extends AST {
- receiver:AST;
- fn:Function;
- args:List;
- name:string;
- constructor(receiver:AST, name:string, fn:Function, args:List) {
- super();
- this.receiver = receiver;
- this.fn = fn;
- this.args = args;
- this.name = name;
- }
-
- eval(context) {
- var evaluatedContext = this.receiver.eval(context);
- var evaluatedArgs = evalList(context, this.args);
-
- while (evaluatedContext instanceof ContextWithVariableBindings) {
- if (evaluatedContext.hasBinding(this.name)) {
- var fn = evaluatedContext.get(this.name);
- return FunctionWrapper.apply(fn, evaluatedArgs);
- }
- evaluatedContext = evaluatedContext.parent;
- }
-
- return this.fn(evaluatedContext, evaluatedArgs);
- }
-
- visit(visitor) {
- return visitor.visitMethodCall(this);
- }
-}
-
-export class FunctionCall extends AST {
- target:AST;
- args:List;
- constructor(target:AST, args:List) {
- super();
- this.target = target;
- this.args = args;
- }
-
- eval(context) {
- var obj = this.target.eval(context);
- if (! (obj instanceof Function)) {
- throw new BaseException(`${obj} is not a function`);
- }
- return FunctionWrapper.apply(obj, evalList(context, this.args));
- }
-
- visit(visitor) {
- return visitor.visitFunctionCall(this);
- }
-}
-
-export class ASTWithSource extends AST {
- ast:AST;
- source:string;
- location:string;
- constructor(ast:AST, source:string, location:string) {
- super();
- this.source = source;
- this.location = location;
- this.ast = ast;
- }
-
- eval(context) {
- return this.ast.eval(context);
- }
-
- get isAssignable() {
- return this.ast.isAssignable;
- }
-
- assign(context, value) {
- return this.ast.assign(context, value);
- }
-
- visit(visitor) {
- return this.ast.visit(visitor);
- }
-
- toString():string {
- return `${this.source} in ${this.location}`;
- }
-}
-
-export class TemplateBinding {
- key:string;
- keyIsVar:boolean;
- 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.
- this.name = name;
- this.expression = expression;
- }
-}
-
-//INTERFACE
-export class AstVisitor {
- visitAccessMember(ast:AccessMember) {}
- visitAssignment(ast:Assignment) {}
- visitBinary(ast:Binary) {}
- visitChain(ast:Chain){}
- visitConditional(ast:Conditional) {}
- visitPipe(ast:Pipe) {}
- visitFunctionCall(ast:FunctionCall) {}
- visitImplicitReceiver(ast:ImplicitReceiver) {}
- visitKeyedAccess(ast:KeyedAccess) {}
- visitLiteralArray(ast:LiteralArray) {}
- visitLiteralMap(ast:LiteralMap) {}
- visitLiteralPrimitive(ast:LiteralPrimitive) {}
- visitMethodCall(ast:MethodCall) {}
- visitPrefixNot(ast:PrefixNot) {}
-}
-
-var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0]];
-function evalList(context, exps:List){
- var length = exps.length;
- var result = _evalListCache[length];
- for (var i = 0; i < length; i++) {
- result[i] = exps[i].eval(context);
- }
- return result;
-}
diff --git a/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js b/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js
deleted file mode 100644
index e5de7e2b0cf7..000000000000
--- a/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import {MapWrapper} from 'angular2/src/facade/collection';
-import {BaseException} from 'angular2/src/facade/lang';
-
-export class ContextWithVariableBindings {
- parent:any;
- /// varBindings' keys are read-only. adding/removing keys is not supported.
- varBindings:Map;
-
- constructor(parent:any, varBindings:Map) {
- this.parent = parent;
- this.varBindings = varBindings;
- }
-
- hasBinding(name:string):boolean {
- return MapWrapper.contains(this.varBindings, name);
- }
-
- get(name:string) {
- return MapWrapper.get(this.varBindings, name);
- }
-
- set(name:string, value) {
- // TODO(rado): consider removing this check if we can guarantee this is not
- // exposed to the public API.
- if (this.hasBinding(name)) {
- MapWrapper.set(this.varBindings, name, value);
- } else {
- throw new BaseException(
- 'VariableBindings do not support setting of new keys post-construction.');
- }
- }
-
- clearValues() {
- MapWrapper.clearValues(this.varBindings);
- }
-}
diff --git a/modules/angular2/src/change_detection/parser/lexer.js b/modules/angular2/src/change_detection/parser/lexer.js
deleted file mode 100644
index 291165063621..000000000000
--- a/modules/angular2/src/change_detection/parser/lexer.js
+++ /dev/null
@@ -1,482 +0,0 @@
-import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
-import {int, FIELD, NumberWrapper, StringJoiner, StringWrapper} from "angular2/src/facade/lang";
-
-export const TOKEN_TYPE_CHARACTER = 1;
-export const TOKEN_TYPE_IDENTIFIER = 2;
-export const TOKEN_TYPE_KEYWORD = 3;
-export const TOKEN_TYPE_STRING = 4;
-export const TOKEN_TYPE_OPERATOR = 5;
-export const TOKEN_TYPE_NUMBER = 6;
-
-export class Lexer {
- text:string;
- tokenize(text:string):List {
- var scanner = new _Scanner(text);
- var tokens = [];
- var token = scanner.scanToken();
- while (token != null) {
- ListWrapper.push(tokens, token);
- token = scanner.scanToken();
- }
- return tokens;
- }
-}
-
-export class Token {
- index:int;
- type:int;
- _numValue:number;
- _strValue:string;
- constructor(index:int, type:int, numValue:number, strValue:string) {
- /**
- * NOTE: To ensure that this constructor creates the same hidden class each time, ensure that
- * all the fields are assigned to in the exact same order in each run of this constructor.
- */
- this.index = index;
- this.type = type;
- this._numValue = numValue;
- this._strValue = strValue;
- }
-
- isCharacter(code:int):boolean {
- return (this.type == TOKEN_TYPE_CHARACTER && this._numValue == code);
- }
-
- isNumber():boolean {
- return (this.type == TOKEN_TYPE_NUMBER);
- }
-
- isString():boolean {
- return (this.type == TOKEN_TYPE_STRING);
- }
-
- isOperator(operater:string):boolean {
- return (this.type == TOKEN_TYPE_OPERATOR && this._strValue == operater);
- }
-
- isIdentifier():boolean {
- return (this.type == TOKEN_TYPE_IDENTIFIER);
- }
-
- isKeyword():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD);
- }
-
- isKeywordVar():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "var");
- }
-
- isKeywordNull():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "null");
- }
-
- isKeywordUndefined():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "undefined");
- }
-
- isKeywordTrue():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "true");
- }
-
- isKeywordFalse():boolean {
- return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "false");
- }
-
- toNumber():number {
- // -1 instead of NULL ok?
- return (this.type == TOKEN_TYPE_NUMBER) ? this._numValue : -1;
- }
-
- toString():string {
- var type:int = this.type;
- if (type >= TOKEN_TYPE_CHARACTER && type <= TOKEN_TYPE_STRING) {
- return this._strValue;
- } else if (type == TOKEN_TYPE_NUMBER) {
- return this._numValue.toString();
- } else {
- return null;
- }
- }
-}
-
-function newCharacterToken(index:int, code:int):Token {
- return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
-}
-
-function newIdentifierToken(index:int, text:string):Token {
- return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
-}
-
-function newKeywordToken(index:int, text:string):Token {
- return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
-}
-
-function newOperatorToken(index:int, text:string):Token {
- return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
-}
-
-function newStringToken(index:int, text:string):Token {
- return new Token(index, TOKEN_TYPE_STRING, 0, text);
-}
-
-function newNumberToken(index:int, n:number):Token {
- return new Token(index, TOKEN_TYPE_NUMBER, n, "");
-}
-
-
-export var EOF:Token = new Token(-1, 0, 0, "");
-
-export const $EOF = 0;
-export const $TAB = 9;
-export const $LF = 10;
-export const $VTAB = 11;
-export const $FF = 12;
-export const $CR = 13;
-export const $SPACE = 32;
-export const $BANG = 33;
-export const $DQ = 34;
-export const $HASH = 35;
-export const $$ = 36;
-export const $PERCENT = 37;
-export const $AMPERSAND = 38;
-export const $SQ = 39;
-export const $LPAREN = 40;
-export const $RPAREN = 41;
-export const $STAR = 42;
-export const $PLUS = 43;
-export const $COMMA = 44;
-export const $MINUS = 45;
-export const $PERIOD = 46;
-export const $SLASH = 47;
-export const $COLON = 58;
-export const $SEMICOLON = 59;
-export const $LT = 60;
-export const $EQ = 61;
-export const $GT = 62;
-export const $QUESTION = 63;
-
-const $0 = 48;
-const $9 = 57;
-
-const $A = 65, $B = 66, $C = 67, $D = 68, $E = 69, $F = 70, $G = 71, $H = 72,
- $I = 73, $J = 74, $K = 75, $L = 76, $M = 77, $N = 78, $O = 79, $P = 80,
- $Q = 81, $R = 82, $S = 83, $T = 84, $U = 85, $V = 86, $W = 87, $X = 88,
- $Y = 89, $Z = 90;
-
-export const $LBRACKET = 91;
-export const $BACKSLASH = 92;
-export const $RBRACKET = 93;
-const $CARET = 94;
-const $_ = 95;
-
-const $a = 97, $b = 98, $c = 99, $d = 100, $e = 101, $f = 102, $g = 103,
- $h = 104, $i = 105, $j = 106, $k = 107, $l = 108, $m = 109, $n = 110,
- $o = 111, $p = 112, $q = 113, $r = 114, $s = 115, $t = 116, $u = 117,
- $v = 118, $w = 119, $x = 120, $y = 121, $z = 122;
-
-export const $LBRACE = 123;
-export const $BAR = 124;
-export const $RBRACE = 125;
-const $TILDE = 126;
-const $NBSP = 160;
-
-
-export class ScannerError extends Error {
- message:string;
- constructor(message) {
- super();
- this.message = message;
- }
-
- toString() {
- return this.message;
- }
-}
-
-class _Scanner {
- input:string;
- length:int;
- peek:int;
- index:int;
-
- constructor(input:string) {
- this.input = input;
- this.length = input.length;
- this.peek = 0;
- this.index = -1;
- this.advance();
- }
-
- advance() {
- this.peek = ++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
- }
-
- scanToken():Token {
- var input = this.input,
- length = this.length,
- peek = this.peek,
- index = this.index;
-
- // Skip whitespace.
- while (peek <= $SPACE) {
- if (++index >= length) {
- peek = $EOF;
- break;
- } else {
- peek = StringWrapper.charCodeAt(input, index);
- }
- }
-
- this.peek = peek;
- this.index = index;
-
- if (index >= length) {
- return null;
- }
-
- // Handle identifiers and numbers.
- if (isIdentifierStart(peek)) return this.scanIdentifier();
- if (isDigit(peek)) return this.scanNumber(index);
-
- var start:int = index;
- switch (peek) {
- case $PERIOD:
- this.advance();
- return isDigit(this.peek) ? this.scanNumber(start) :
- newCharacterToken(start, $PERIOD);
- case $LPAREN: case $RPAREN:
- case $LBRACE: case $RBRACE:
- case $LBRACKET: case $RBRACKET:
- case $COMMA:
- case $COLON:
- case $SEMICOLON:
- return this.scanCharacter(start, peek);
- case $SQ:
- case $DQ:
- return this.scanString();
- case $HASH:
- return this.scanOperator(start, StringWrapper.fromCharCode(peek));
- case $PLUS:
- case $MINUS:
- case $STAR:
- case $SLASH:
- case $PERCENT:
- case $CARET:
- case $QUESTION:
- return this.scanOperator(start, StringWrapper.fromCharCode(peek));
- case $LT:
- case $GT:
- case $BANG:
- case $EQ:
- return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
- case $AMPERSAND:
- return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
- case $BAR:
- return this.scanComplexOperator(start, $BAR, '|', '|');
- case $TILDE:
- return this.scanComplexOperator(start, $SLASH, '~', '/');
- case $NBSP:
- while (isWhitespace(this.peek)) this.advance();
- return this.scanToken();
- }
-
- this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
- return null;
- }
-
- scanCharacter(start:int, code:int):Token {
- assert(this.peek == code);
- this.advance();
- return newCharacterToken(start, code);
- }
-
-
- scanOperator(start:int, str:string):Token {
- assert(this.peek == StringWrapper.charCodeAt(str, 0));
- assert(SetWrapper.has(OPERATORS, str));
- this.advance();
- return newOperatorToken(start, str);
- }
-
- scanComplexOperator(start:int, code:int, one:string, two:string):Token {
- assert(this.peek == StringWrapper.charCodeAt(one, 0));
- this.advance();
- var str:string = one;
- if (this.peek == code) {
- this.advance();
- str += two;
- }
- assert(SetWrapper.has(OPERATORS, str));
- return newOperatorToken(start, str);
- }
-
- scanIdentifier():Token {
- assert(isIdentifierStart(this.peek));
- var start:int = this.index;
- this.advance();
- while (isIdentifierPart(this.peek)) this.advance();
- var str:string = this.input.substring(start, this.index);
- if (SetWrapper.has(KEYWORDS, str)) {
- return newKeywordToken(start, str);
- } else {
- return newIdentifierToken(start, str);
- }
- }
-
- scanNumber(start:int):Token {
- assert(isDigit(this.peek));
- var simple:boolean = (this.index === start);
- this.advance(); // Skip initial digit.
- while (true) {
- if (isDigit(this.peek)) {
- // Do nothing.
- } else if (this.peek == $PERIOD) {
- simple = false;
- } else if (isExponentStart(this.peek)) {
- this.advance();
- if (isExponentSign(this.peek)) this.advance();
- if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
- simple = false;
- } else {
- break;
- }
- this.advance();
- }
- var str:string = this.input.substring(start, this.index);
- // TODO
- var value:number = simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
- return newNumberToken(start, value);
- }
-
- scanString():Token {
- assert(this.peek == $SQ || this.peek == $DQ);
- var start:int = this.index;
- var quote:int = this.peek;
- this.advance(); // Skip initial quote.
-
- var buffer:StringJoiner;
- var marker:int = this.index;
- var input:string = this.input;
-
- while (this.peek != quote) {
- if (this.peek == $BACKSLASH) {
- if (buffer == null) buffer = new StringJoiner();
- buffer.add(input.substring(marker, this.index));
- this.advance();
- var unescapedCode:int;
- if (this.peek == $u) {
- // 4 character hex code for unicode character.
- var hex:string = input.substring(this.index + 1, this.index + 5);
- try {
- unescapedCode = NumberWrapper.parseInt(hex, 16);
- } catch (e) {
- this.error(`Invalid unicode escape [\\u${hex}]`, 0);
- }
- for (var i:int = 0; i < 5; i++) {
- this.advance();
- }
- } else {
- unescapedCode = unescape(this.peek);
- this.advance();
- }
- buffer.add(StringWrapper.fromCharCode(unescapedCode));
- marker = this.index;
- } else if (this.peek == $EOF) {
- this.error('Unterminated quote', 0);
- } else {
- this.advance();
- }
- }
-
- var last:string = input.substring(marker, this.index);
- this.advance(); // Skip terminating quote.
-
- // Compute the unescaped string value.
- var unescaped:string = last;
- if (buffer != null) {
- buffer.add(last);
- unescaped = buffer.toString();
- }
- return newStringToken(start, unescaped);
- }
-
- error(message:string, offset:int) {
- var position:int = this.index + offset;
- throw new ScannerError(`Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
- }
-}
-
-function isWhitespace(code:int):boolean {
- return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
-}
-
-function isIdentifierStart(code:int):boolean {
- return ($a <= code && code <= $z) ||
- ($A <= code && code <= $Z) ||
- (code == $_) ||
- (code == $$);
-}
-
-function isIdentifierPart(code:int):boolean {
- return ($a <= code && code <= $z) ||
- ($A <= code && code <= $Z) ||
- ($0 <= code && code <= $9) ||
- (code == $_) ||
- (code == $$);
-}
-
-function isDigit(code:int):boolean {
- return $0 <= code && code <= $9;
-}
-
-function isExponentStart(code:int):boolean {
- return code == $e || code == $E;
-}
-
-function isExponentSign(code:int):boolean {
- return code == $MINUS || code == $PLUS;
-}
-
-function unescape(code:int):int {
- switch(code) {
- case $n: return $LF;
- case $f: return $FF;
- case $r: return $CR;
- case $t: return $TAB;
- case $v: return $VTAB;
- default: return code;
- }
-}
-
-var OPERATORS = SetWrapper.createFromList([
- '+',
- '-',
- '*',
- '/',
- '~/',
- '%',
- '^',
- '=',
- '==',
- '!=',
- '<',
- '>',
- '<=',
- '>=',
- '&&',
- '||',
- '&',
- '|',
- '!',
- '?',
- '#'
-]);
-
-
-var KEYWORDS = SetWrapper.createFromList([
- 'var',
- 'null',
- 'undefined',
- 'true',
- 'false',
-]);
diff --git a/modules/angular2/src/change_detection/parser/parser.js b/modules/angular2/src/change_detection/parser/parser.js
deleted file mode 100644
index 09ade4da044b..000000000000
--- a/modules/angular2/src/change_detection/parser/parser.js
+++ /dev/null
@@ -1,534 +0,0 @@
-import {FIELD, int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
-import {ListWrapper, List} from 'angular2/src/facade/collection';
-import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
- $COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
-import {reflector, Reflector} from 'angular2/src/reflection/reflection';
-import {
- AST,
- EmptyExpr,
- ImplicitReceiver,
- AccessMember,
- LiteralPrimitive,
- Expression,
- Binary,
- PrefixNot,
- Conditional,
- Pipe,
- Assignment,
- Chain,
- KeyedAccess,
- LiteralArray,
- LiteralMap,
- Interpolation,
- MethodCall,
- FunctionCall,
- TemplateBindings,
- TemplateBinding,
- ASTWithSource
- } from './ast';
-
-var _implicitReceiver = new ImplicitReceiver();
-// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
-var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
-var QUOTE_REGEXP = RegExpWrapper.create("'");
-
-export class Parser {
- _lexer:Lexer;
- _reflector:Reflector;
- constructor(lexer:Lexer, providedReflector:Reflector = null){
- this._lexer = lexer;
- this._reflector = isPresent(providedReflector) ? providedReflector : reflector;
- }
-
- parseAction(input:string, location:any):ASTWithSource {
- var tokens = this._lexer.tokenize(input);
- var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
- return new ASTWithSource(ast, input, location);
- }
-
- parseBinding(input:string, location:any):ASTWithSource {
- var tokens = this._lexer.tokenize(input);
- var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
- return new ASTWithSource(ast, input, location);
- }
-
- addPipes(bindingAst:ASTWithSource, pipes:List):ASTWithSource {
- if (ListWrapper.isEmpty(pipes)) return bindingAst;
-
- var res = ListWrapper.reduce(pipes,
- (result, currentPipeName) => new Pipe(result, currentPipeName, []),
- bindingAst.ast);
- return new ASTWithSource(res, bindingAst.source, bindingAst.location);
- }
-
- parseTemplateBindings(input:string, location:any):List {
- var tokens = this._lexer.tokenize(input);
- return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
- }
-
- parseInterpolation(input:string, location:any):ASTWithSource {
- var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
- if (parts.length <= 1) {
- return null;
- }
- var strings = [];
- var expressions = [];
-
- for (var i=0; i;
- reflector:Reflector;
- parseAction:boolean;
- index:int;
- constructor(input:string, location:any, tokens:List, reflector:Reflector, parseAction:boolean) {
- this.input = input;
- this.location = location;
- this.tokens = tokens;
- this.index = 0;
- this.reflector = reflector;
- this.parseAction = parseAction;
- }
-
- peek(offset:int):Token {
- var i = this.index + offset;
- return i < this.tokens.length ? this.tokens[i] : EOF;
- }
-
- get next():Token {
- return this.peek(0);
- }
-
- get inputIndex():int {
- return (this.index < this.tokens.length) ? this.next.index : this.input.length;
- }
-
- advance() {
- this.index ++;
- }
-
- optionalCharacter(code:int):boolean {
- if (this.next.isCharacter(code)) {
- this.advance();
- return true;
- } else {
- return false;
- }
- }
-
- optionalKeywordVar():boolean {
- if (this.peekKeywordVar()) {
- this.advance();
- return true;
- } else {
- return false;
- }
- }
-
- peekKeywordVar():boolean {
- return this.next.isKeywordVar() || this.next.isOperator('#');
- }
-
- expectCharacter(code:int) {
- if (this.optionalCharacter(code)) return;
- this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
- }
-
-
- optionalOperator(op:string):boolean {
- if (this.next.isOperator(op)) {
- this.advance();
- return true;
- } else {
- return false;
- }
- }
-
- expectOperator(operator:string) {
- if (this.optionalOperator(operator)) return;
- this.error(`Missing expected operator ${operator}`);
- }
-
- expectIdentifierOrKeyword():string {
- var n = this.next;
- if (!n.isIdentifier() && !n.isKeyword()) {
- this.error(`Unexpected token ${n}, expected identifier or keyword`)
- }
- this.advance();
- return n.toString();
- }
-
- expectIdentifierOrKeywordOrString():string {
- var n = this.next;
- if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
- this.error(`Unexpected token ${n}, expected identifier, keyword, or string`)
- }
- this.advance();
- return n.toString();
- }
-
- parseChain():AST {
- var exprs = [];
- while (this.index < this.tokens.length) {
- var expr = this.parsePipe();
- ListWrapper.push(exprs, expr);
-
- if (this.optionalCharacter($SEMICOLON)) {
- if (! this.parseAction) {
- this.error("Binding expression cannot contain chained expression");
- }
- while (this.optionalCharacter($SEMICOLON)){} //read all semicolons
- } else if (this.index < this.tokens.length) {
- this.error(`Unexpected token '${this.next}'`);
- }
- }
- if (exprs.length == 0) return new EmptyExpr();
- if (exprs.length == 1) return exprs[0];
- return new Chain(exprs);
- }
-
- parsePipe() {
- var result = this.parseExpression();
- while (this.optionalOperator("|")) {
- if (this.parseAction) {
- this.error("Cannot have a pipe in an action expression");
- }
- var name = this.expectIdentifierOrKeyword();
- var args = ListWrapper.create();
- while (this.optionalCharacter($COLON)) {
- ListWrapper.push(args, this.parseExpression());
- }
- result = new Pipe(result, name, args);
- }
- return result;
- }
-
- parseExpression() {
- var start = this.inputIndex;
- var result = this.parseConditional();
-
- while (this.next.isOperator('=')) {
- if (!result.isAssignable) {
- var end = this.inputIndex;
- var expression = this.input.substring(start, end);
- this.error(`Expression ${expression} is not assignable`);
- }
-
- if (!this.parseAction) {
- this.error("Binding expression cannot contain assignments");
- }
-
- this.expectOperator('=');
- result = new Assignment(result, this.parseConditional());
- }
-
- return result;
- }
-
- parseConditional() {
- var start = this.inputIndex;
- var result = this.parseLogicalOr();
-
- if (this.optionalOperator('?')) {
- var yes = this.parseExpression();
- if (!this.optionalCharacter($COLON)) {
- var end = this.inputIndex;
- var expression = this.input.substring(start, end);
- this.error(`Conditional expression ${expression} requires all 3 expressions`);
- }
- var no = this.parseExpression();
- return new Conditional(result, yes, no);
- } else {
- return result;
- }
- }
-
- parseLogicalOr() {
- // '||'
- var result = this.parseLogicalAnd();
- while (this.optionalOperator('||')) {
- result = new Binary('||', result, this.parseLogicalAnd());
- }
- return result;
- }
-
- parseLogicalAnd() {
- // '&&'
- var result = this.parseEquality();
- while (this.optionalOperator('&&')) {
- result = new Binary('&&', result, this.parseEquality());
- }
- return result;
- }
-
- parseEquality() {
- // '==','!='
- var result = this.parseRelational();
- while (true) {
- if (this.optionalOperator('==')) {
- result = new Binary('==', result, this.parseRelational());
- } else if (this.optionalOperator('!=')) {
- result = new Binary('!=', result, this.parseRelational());
- } else {
- return result;
- }
- }
- }
-
- parseRelational() {
- // '<', '>', '<=', '>='
- var result = this.parseAdditive();
- while (true) {
- if (this.optionalOperator('<')) {
- result = new Binary('<', result, this.parseAdditive());
- } else if (this.optionalOperator('>')) {
- result = new Binary('>', result, this.parseAdditive());
- } else if (this.optionalOperator('<=')) {
- result = new Binary('<=', result, this.parseAdditive());
- } else if (this.optionalOperator('>=')) {
- result = new Binary('>=', result, this.parseAdditive());
- } else {
- return result;
- }
- }
- }
-
- parseAdditive() {
- // '+', '-'
- var result = this.parseMultiplicative();
- while (true) {
- if (this.optionalOperator('+')) {
- result = new Binary('+', result, this.parseMultiplicative());
- } else if (this.optionalOperator('-')) {
- result = new Binary('-', result, this.parseMultiplicative());
- } else {
- return result;
- }
- }
- }
-
- parseMultiplicative() {
- // '*', '%', '/'
- var result = this.parsePrefix();
- while (true) {
- if (this.optionalOperator('*')) {
- result = new Binary('*', result, this.parsePrefix());
- } else if (this.optionalOperator('%')) {
- result = new Binary('%', result, this.parsePrefix());
- } else if (this.optionalOperator('/')) {
- result = new Binary('/', result, this.parsePrefix());
- } else {
- return result;
- }
- }
- }
-
- parsePrefix() {
- if (this.optionalOperator('+')) {
- return this.parsePrefix();
- } else if (this.optionalOperator('-')) {
- return new Binary('-', new LiteralPrimitive(0), this.parsePrefix());
- } else if (this.optionalOperator('!')) {
- return new PrefixNot(this.parsePrefix());
- } else {
- return this.parseCallChain();
- }
- }
-
- parseCallChain():AST {
- var result = this.parsePrimary();
- while (true) {
- if (this.optionalCharacter($PERIOD)) {
- result = this.parseAccessMemberOrMethodCall(result);
-
- } else if (this.optionalCharacter($LBRACKET)) {
- var key = this.parseExpression();
- this.expectCharacter($RBRACKET);
- result = new KeyedAccess(result, key);
-
- } else if (this.optionalCharacter($LPAREN)) {
- var args = this.parseCallArguments();
- this.expectCharacter($RPAREN);
- result = new FunctionCall(result, args);
-
- } else {
- return result;
- }
- }
- }
-
- parsePrimary() {
- if (this.optionalCharacter($LPAREN)) {
- var result = this.parsePipe();
- this.expectCharacter($RPAREN);
- return result;
-
- } else if (this.next.isKeywordNull() || this.next.isKeywordUndefined()) {
- this.advance();
- return new LiteralPrimitive(null);
-
- } else if (this.next.isKeywordTrue()) {
- this.advance();
- return new LiteralPrimitive(true);
-
- } else if (this.next.isKeywordFalse()) {
- this.advance();
- return new LiteralPrimitive(false);
-
- } else if (this.optionalCharacter($LBRACKET)) {
- var elements = this.parseExpressionList($RBRACKET);
- this.expectCharacter($RBRACKET);
- return new LiteralArray(elements);
-
- } else if (this.next.isCharacter($LBRACE)) {
- return this.parseLiteralMap();
-
- } else if (this.next.isIdentifier()) {
- return this.parseAccessMemberOrMethodCall(_implicitReceiver);
-
- } else if (this.next.isNumber()) {
- var value = this.next.toNumber();
- this.advance();
- return new LiteralPrimitive(value);
-
- } else if (this.next.isString()) {
- var value = this.next.toString();
- this.advance();
- return new LiteralPrimitive(value);
-
- } else if (this.index >= this.tokens.length) {
- this.error(`Unexpected end of expression: ${this.input}`);
-
- } else {
- this.error(`Unexpected token ${this.next}`);
- }
- }
-
- parseExpressionList(terminator:int):List {
- var result = [];
- if (!this.next.isCharacter(terminator)) {
- do {
- ListWrapper.push(result, this.parseExpression());
- } while (this.optionalCharacter($COMMA));
- }
- return result;
- }
-
- parseLiteralMap() {
- var keys = [];
- var values = [];
- this.expectCharacter($LBRACE);
- if (!this.optionalCharacter($RBRACE)) {
- do {
- var key = this.expectIdentifierOrKeywordOrString();
- ListWrapper.push(keys, key);
- this.expectCharacter($COLON);
- ListWrapper.push(values, this.parseExpression());
- } while (this.optionalCharacter($COMMA));
- this.expectCharacter($RBRACE);
- }
- return new LiteralMap(keys, values);
- }
-
- parseAccessMemberOrMethodCall(receiver):AST {
- var id = this.expectIdentifierOrKeyword();
-
- if (this.optionalCharacter($LPAREN)) {
- var args = this.parseCallArguments();
- this.expectCharacter($RPAREN);
- var fn = this.reflector.method(id);
- return new MethodCall(receiver, id, fn, args);
-
- } else {
- var getter = this.reflector.getter(id);
- var setter = this.reflector.setter(id);
- return new AccessMember(receiver, id, getter, setter);
- }
- }
-
- parseCallArguments() {
- if (this.next.isCharacter($RPAREN)) return [];
- var positionals = [];
- do {
- ListWrapper.push(positionals, this.parseExpression());
- } while (this.optionalCharacter($COMMA))
- return positionals;
- }
-
- /**
- * An identifier, a keyword, a string with an optional `-` inbetween.
- */
- expectTemplateBindingKey() {
- var result = '';
- var operatorFound = false;
- do {
- result += this.expectIdentifierOrKeywordOrString();
- operatorFound = this.optionalOperator('-');
- if (operatorFound) {
- result += '-';
- }
- } while (operatorFound);
-
- return result.toString();
- }
-
- parseTemplateBindings() {
- var bindings = [];
- while (this.index < this.tokens.length) {
- var keyIsVar:boolean = this.optionalKeywordVar();
- var key = this.expectTemplateBindingKey();
- this.optionalCharacter($COLON);
- var name = null;
- var expression = null;
- if (this.next !== EOF) {
- if (keyIsVar) {
- if (this.optionalOperator("=")) {
- name = this.expectTemplateBindingKey();
- } else {
- name = '\$implicit';
- }
- } else if (!this.peekKeywordVar()) {
- var start = this.inputIndex;
- var ast = this.parsePipe();
- var source = this.input.substring(start, this.inputIndex);
- expression = new ASTWithSource(ast, source, this.location);
- }
- }
- ListWrapper.push(bindings, new TemplateBinding(key, keyIsVar, name, expression));
- if (!this.optionalCharacter($SEMICOLON)) {
- this.optionalCharacter($COMMA);
- };
- }
- return bindings;
- }
-
- error(message:string, index:int = null) {
- if (isBlank(index)) index = this.index;
-
- var location = (index < this.tokens.length)
- ? `at column ${this.tokens[index].index + 1} in`
- : `at the end of the expression`;
-
- throw new BaseException(`Parser Error: ${message} ${location} [${this.input}] in ${this.location}`);
- }
-}
diff --git a/modules/angular2/src/change_detection/pipes/array_changes.js b/modules/angular2/src/change_detection/pipes/array_changes.js
deleted file mode 100644
index 10fa537553ff..000000000000
--- a/modules/angular2/src/change_detection/pipes/array_changes.js
+++ /dev/null
@@ -1,674 +0,0 @@
-import {
- isListLikeIterable,
- iterateListLike,
- ListWrapper,
- MapWrapper
-} from 'angular2/src/facade/collection';
-
-import {
- int,
- isBlank,
- isPresent,
- stringify,
- getMapKey,
- looseIdentical,
-} from 'angular2/src/facade/lang';
-
-import {NO_CHANGE, Pipe} from './pipe';
-
-export class ArrayChangesFactory {
- supports(obj):boolean {
- return ArrayChanges.supportsObj(obj);
- }
-
- create():Pipe {
- return new ArrayChanges();
- }
-}
-
-export class ArrayChanges extends Pipe {
- _collection;
- _length:int;
- _linkedRecords:_DuplicateMap;
- _unlinkedRecords:_DuplicateMap;
- _previousItHead:CollectionChangeRecord;
- _itHead:CollectionChangeRecord;
- _itTail:CollectionChangeRecord;
- _additionsHead:CollectionChangeRecord;
- _additionsTail:CollectionChangeRecord;
- _movesHead:CollectionChangeRecord;
- _movesTail:CollectionChangeRecord;
- _removalsHead:CollectionChangeRecord;
- _removalsTail:CollectionChangeRecord;
-
- constructor() {
- super();
- this._collection = null;
- this._length = null;
- /// Keeps track of the used records at any point in time (during & across `_check()` calls)
- this._linkedRecords = null;
- /// Keeps track of the removed records at any point in time during `_check()` calls.
- this._unlinkedRecords = null;
-
- this._previousItHead = null;
- this._itHead = null;
- this._itTail = null;
- this._additionsHead = null;
- this._additionsTail = null;
- this._movesHead = null;
- this._movesTail = null;
- this._removalsHead = null;
- this._removalsTail = null;
- }
-
- static supportsObj(obj):boolean {
- return isListLikeIterable(obj);
- }
-
- supports(obj):boolean {
- return ArrayChanges.supportsObj(obj);
- }
-
- get collection() {
- return this._collection;
- }
-
- get length():int {
- return this._length;
- }
-
- forEachItem(fn:Function) {
- var record:CollectionChangeRecord;
- for (record = this._itHead; record !== null; record = record._next) {
- fn(record);
- }
- }
-
- forEachPreviousItem(fn:Function) {
- var record:CollectionChangeRecord;
- for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
- fn(record);
- }
- }
-
- forEachAddedItem(fn:Function){
- var record:CollectionChangeRecord;
- for (record = this._additionsHead; record !== null; record = record._nextAdded) {
- fn(record);
- }
- }
-
- forEachMovedItem(fn:Function) {
- var record:CollectionChangeRecord;
- for (record = this._movesHead; record !== null; record = record._nextMoved) {
- fn(record);
- }
- }
-
- forEachRemovedItem(fn:Function){
- var record:CollectionChangeRecord;
- for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
- fn(record);
- }
- }
-
- transform(collection){
- if (this.check(collection)) {
- return this;
- } else {
- return NO_CHANGE;
- }
- }
-
- // todo(vicb): optim for UnmodifiableListView (frozen arrays)
- check(collection):boolean {
- this._reset();
-
- var record:CollectionChangeRecord = this._itHead;
- var mayBeDirty:boolean = false;
- var index:int, item;
-
- if (ListWrapper.isList(collection)) {
- var list = collection;
- this._length = collection.length;
-
- for (index = 0; index < this._length; index++) {
- item = list[index];
- if (record === null || !looseIdentical(record.item, item)) {
- record = this._mismatch(record, item, index);
- mayBeDirty = true;
- } else if (mayBeDirty) {
- // TODO(misko): can we limit this to duplicates only?
- record = this._verifyReinsertion(record, item, index);
- }
- record = record._next;
- }
- } else {
- index = 0;
- iterateListLike(collection, (item) => {
- if (record === null || !looseIdentical(record.item, item)) {
- record = this._mismatch(record, item, index);
- mayBeDirty = true;
- } else if (mayBeDirty) {
- // TODO(misko): can we limit this to duplicates only?
- record = this._verifyReinsertion(record, item, index);
- }
- record = record._next;
- index++
- });
- this._length = index;
- }
-
- this._truncate(record);
- this._collection = collection;
- return this.isDirty;
- }
-
- // CollectionChanges is considered dirty if it has any additions, moves or removals.
- get isDirty():boolean {
- return this._additionsHead !== null ||
- this._movesHead !== null ||
- this._removalsHead !== null;
- }
-
- /**
- * Reset the state of the change objects to show no changes. This means set previousKey to
- * currentKey, and clear all of the queues (additions, moves, removals).
- * Set the previousIndexes of moved and added items to their currentIndexes
- * Reset the list of additions, moves and removals
- */
- _reset() {
- if (this.isDirty) {
- var record:CollectionChangeRecord;
- var nextRecord:CollectionChangeRecord;
-
- for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
- record._nextPrevious = record._next;
- }
-
- for (record = this._additionsHead; record !== null; record = record._nextAdded) {
- record.previousIndex = record.currentIndex;
- }
- this._additionsHead = this._additionsTail = null;
-
- for (record = this._movesHead; record !== null; record = nextRecord) {
- record.previousIndex = record.currentIndex;
- nextRecord = record._nextMoved;
- }
- this._movesHead = this._movesTail = null;
- this._removalsHead = this._removalsTail = null;
-
- // todo(vicb) when assert gets supported
- // assert(!this.isDirty);
- }
- }
-
- /**
- * This is the core function which handles differences between collections.
- *
- * - [record] is the record which we saw at this position last time. If null then it is a new
- * item.
- * - [item] is the current item in the collection
- * - [index] is the position of the item in the collection
- */
- _mismatch(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
- // The previous record after which we will append the current one.
- var previousRecord:CollectionChangeRecord;
-
- if (record === null) {
- previousRecord = this._itTail;
- } else {
- previousRecord = record._prev;
- // Remove the record from the collection since we know it does not match the item.
- this._remove(record);
- }
-
- // Attempt to see if we have seen the item before.
- record = this._linkedRecords === null ? null : this._linkedRecords.get(item, index);
- if (record !== null) {
- // We have seen this before, we need to move it forward in the collection.
- this._moveAfter(record, previousRecord, index);
- } else {
- // Never seen it, check evicted list.
- record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(item);
- if (record !== null) {
- // It is an item which we have evicted earlier: reinsert it back into the list.
- this._reinsertAfter(record, previousRecord, index);
- } else {
- // It is a new item: add it.
- record = this._addAfter(new CollectionChangeRecord(item), previousRecord, index);
- }
- }
- return record;
- }
-
- /**
- * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty)
- *
- * Use case: `[a, a]` => `[b, a, a]`
- *
- * If we did not have this check then the insertion of `b` would:
- * 1) evict first `a`
- * 2) insert `b` at `0` index.
- * 3) leave `a` at index `1` as is. <-- this is wrong!
- * 3) reinsert `a` at index 2. <-- this is wrong!
- *
- * The correct behavior is:
- * 1) evict first `a`
- * 2) insert `b` at `0` index.
- * 3) reinsert `a` at index 1.
- * 3) move `a` at from `1` to `2`.
- *
- *
- * Double check that we have not evicted a duplicate item. We need to check if the item type may
- * have already been removed:
- * The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted
- * at the end. Which will show up as the two 'a's switching position. This is incorrect, since a
- * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
- * at the end.
- */
- _verifyReinsertion(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
- var reinsertRecord:CollectionChangeRecord = this._unlinkedRecords === null ?
- null : this._unlinkedRecords.get(item);
- if (reinsertRecord !== null) {
- record = this._reinsertAfter(reinsertRecord, record._prev, index);
- } else if (record.currentIndex != index) {
- record.currentIndex = index;
- this._addToMoves(record, index);
- }
- return record;
- }
-
- /**
- * Get rid of any excess [CollectionChangeRecord]s from the previous collection
- *
- * - [record] The first excess [CollectionChangeRecord].
- */
- _truncate(record:CollectionChangeRecord) {
- // Anything after that needs to be removed;
- while (record !== null) {
- var nextRecord:CollectionChangeRecord = record._next;
- this._addToRemovals(this._unlink(record));
- record = nextRecord;
- }
- if (this._unlinkedRecords !== null) {
- this._unlinkedRecords.clear();
- }
-
- if (this._additionsTail !== null) {
- this._additionsTail._nextAdded = null;
- }
- if (this._movesTail !== null) {
- this._movesTail._nextMoved = null;
- }
- if (this._itTail !== null) {
- this._itTail._next = null;
- }
- if (this._removalsTail !== null) {
- this._removalsTail._nextRemoved = null;
- }
- }
-
- _reinsertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
- index:int):CollectionChangeRecord {
- if (this._unlinkedRecords !== null) {
- this._unlinkedRecords.remove(record);
- }
- var prev = record._prevRemoved;
- var next = record._nextRemoved;
-
- if (prev === null) {
- this._removalsHead = next;
- } else {
- prev._nextRemoved = next;
- }
- if (next === null) {
- this._removalsTail = prev;
- } else {
- next._prevRemoved = prev;
- }
-
- this._insertAfter(record, prevRecord, index);
- this._addToMoves(record, index);
- return record;
- }
-
- _moveAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
- index:int):CollectionChangeRecord {
- this._unlink(record);
- this._insertAfter(record, prevRecord, index);
- this._addToMoves(record, index);
- return record;
- }
-
- _addAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
- index:int):CollectionChangeRecord {
- this._insertAfter(record, prevRecord, index);
-
- if (this._additionsTail === null) {
- // todo(vicb)
- //assert(this._additionsHead === null);
- this._additionsTail = this._additionsHead = record;
- } else {
- // todo(vicb)
- //assert(_additionsTail._nextAdded === null);
- //assert(record._nextAdded === null);
- this._additionsTail = this._additionsTail._nextAdded = record;
- }
- return record;
- }
-
- _insertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
- index:int):CollectionChangeRecord {
- // todo(vicb)
- //assert(record != prevRecord);
- //assert(record._next === null);
- //assert(record._prev === null);
-
- var next:CollectionChangeRecord = prevRecord === null ? this._itHead :prevRecord._next;
- // todo(vicb)
- //assert(next != record);
- //assert(prevRecord != record);
- record._next = next;
- record._prev = prevRecord;
- if (next === null) {
- this._itTail = record;
- } else {
- next._prev = record;
- }
- if (prevRecord === null) {
- this._itHead = record;
- } else {
- prevRecord._next = record;
- }
-
- if (this._linkedRecords === null) {
- this._linkedRecords = new _DuplicateMap();
- }
- this._linkedRecords.put(record);
-
- record.currentIndex = index;
- return record;
- }
-
- _remove(record:CollectionChangeRecord):CollectionChangeRecord {
- return this._addToRemovals(this._unlink(record));
- }
-
- _unlink(record:CollectionChangeRecord):CollectionChangeRecord {
- if (this._linkedRecords !== null) {
- this._linkedRecords.remove(record);
- }
-
- var prev = record._prev;
- var next = record._next;
-
- // todo(vicb)
- //assert((record._prev = null) === null);
- //assert((record._next = null) === null);
-
- if (prev === null) {
- this._itHead = next;
- } else {
- prev._next = next;
- }
- if (next === null) {
- this._itTail = prev;
- } else {
- next._prev = prev;
- }
-
- return record;
- }
-
- _addToMoves(record:CollectionChangeRecord, toIndex:int):CollectionChangeRecord {
- // todo(vicb)
- //assert(record._nextMoved === null);
-
- if (record.previousIndex === toIndex) {
- return record;
- }
-
- if (this._movesTail === null) {
- // todo(vicb)
- //assert(_movesHead === null);
- this._movesTail = this._movesHead = record;
- } else {
- // todo(vicb)
- //assert(_movesTail._nextMoved === null);
- this._movesTail = this._movesTail._nextMoved = record;
- }
-
- return record;
- }
-
- _addToRemovals(record:CollectionChangeRecord):CollectionChangeRecord {
- if (this._unlinkedRecords === null) {
- this._unlinkedRecords = new _DuplicateMap();
- }
- this._unlinkedRecords.put(record);
- record.currentIndex = null;
- record._nextRemoved = null;
-
- if (this._removalsTail === null) {
- // todo(vicb)
- //assert(_removalsHead === null);
- this._removalsTail = this._removalsHead = record;
- record._prevRemoved = null;
- } else {
- // todo(vicb)
- //assert(_removalsTail._nextRemoved === null);
- //assert(record._nextRemoved === null);
- record._prevRemoved = this._removalsTail;
- this._removalsTail = this._removalsTail._nextRemoved = record;
- }
- return record;
- }
-
- toString():string {
- var record:CollectionChangeRecord;
-
- var list = [];
- for (record = this._itHead; record !== null; record = record._next) {
- ListWrapper.push(list, record);
- }
-
- var previous = [];
- for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
- ListWrapper.push(previous, record);
- }
-
- var additions = [];
- for (record = this._additionsHead; record !== null; record = record._nextAdded) {
- ListWrapper.push(additions, record);
- }
- var moves = [];
- for (record = this._movesHead; record !== null; record = record._nextMoved) {
- ListWrapper.push(moves, record);
- }
-
- var removals = [];
- for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
- ListWrapper.push(removals, record);
- }
-
- return "collection: " + list.join(', ') + "\n" +
- "previous: " + previous.join(', ') + "\n" +
- "additions: " + additions.join(', ') + "\n" +
- "moves: " + moves.join(', ') + "\n" +
- "removals: " + removals.join(', ') + "\n";
- }
-}
-
-export class CollectionChangeRecord {
- currentIndex:int;
- previousIndex:int;
- item;
-
- _nextPrevious:CollectionChangeRecord;
- _prev:CollectionChangeRecord; _next:CollectionChangeRecord;
- _prevDup:CollectionChangeRecord; _nextDup:CollectionChangeRecord;
- _prevRemoved:CollectionChangeRecord; _nextRemoved:CollectionChangeRecord;
- _nextAdded:CollectionChangeRecord;
- _nextMoved:CollectionChangeRecord;
-
- constructor(item) {
- this.currentIndex = null;
- this.previousIndex = null;
- this.item = item;
-
- this._nextPrevious = null;
- this._prev = null;
- this._next = null;
- this._prevDup = null;
- this._nextDup = null;
- this._prevRemoved = null;
- this._nextRemoved = null;
- this._nextAdded = null;
- this._nextMoved = null;
- }
-
- toString():string {
- return this.previousIndex === this.currentIndex ?
- stringify(this.item) :
- stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
- stringify(this.currentIndex) + ']';
- }
-}
-
-// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
-class _DuplicateItemRecordList {
- _head:CollectionChangeRecord;
- _tail:CollectionChangeRecord;
-
- constructor() {
- this._head = null;
- this._tail = null;
- }
-
- /**
- * Append the record to the list of duplicates.
- *
- * Note: by design all records in the list of duplicates hold the same value in record.item.
- */
- add(record:CollectionChangeRecord) {
- if (this._head === null) {
- this._head = this._tail = record;
- record._nextDup = null;
- record._prevDup = null;
- } else {
- // todo(vicb)
- //assert(record.item == _head.item ||
- // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
- this._tail._nextDup = record;
- record._prevDup = this._tail;
- record._nextDup = null;
- this._tail = record;
- }
- }
-
- // Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
- // CollectionChangeRecord.currentIndex >= afterIndex
- get(item, afterIndex:int):CollectionChangeRecord {
- var record:CollectionChangeRecord;
- for (record = this._head; record !== null; record = record._nextDup) {
- if ((afterIndex === null || afterIndex < record.currentIndex) &&
- looseIdentical(record.item, item)) {
- return record;
- }
- }
- return null;
- }
-
- /**
- * Remove one [CollectionChangeRecord] from the list of duplicates.
- *
- * Returns whether the list of duplicates is empty.
- */
- remove(record:CollectionChangeRecord):boolean {
- // todo(vicb)
- //assert(() {
- // // verify that the record being removed is in the list.
- // for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
- // if (identical(cursor, record)) return true;
- // }
- // return false;
- //});
-
- var prev:CollectionChangeRecord = record._prevDup;
- var next:CollectionChangeRecord = record._nextDup;
- if (prev === null) {
- this._head = next;
- } else {
- prev._nextDup = next;
- }
- if (next === null) {
- this._tail = prev;
- } else {
- next._prevDup = prev;
- }
- return this._head === null;
- }
-}
-
-class _DuplicateMap {
- map:Map;
- constructor() {
- this.map = MapWrapper.create();
- }
-
- put(record:CollectionChangeRecord) {
- // todo(vicb) handle corner cases
- var key = getMapKey(record.item);
-
- var duplicates = MapWrapper.get(this.map, key);
- if (!isPresent(duplicates)) {
- duplicates = new _DuplicateItemRecordList();
- MapWrapper.set(this.map, key, duplicates);
- }
- duplicates.add(record);
- }
-
- /**
- * Retrieve the `value` using key. Because the CollectionChangeRecord value maybe one which we
- * have already iterated over, we use the afterIndex to pretend it is not there.
- *
- * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
- * have any more `a`s needs to return the last `a` not the first or second.
- */
- get(value, afterIndex = null):CollectionChangeRecord {
- var key = getMapKey(value);
-
- var recordList = MapWrapper.get(this.map, key);
- return isBlank(recordList) ? null : recordList.get(value, afterIndex);
- }
-
- /**
- * Removes an [CollectionChangeRecord] from the list of duplicates.
- *
- * The list of duplicates also is removed from the map if it gets empty.
- */
- remove(record:CollectionChangeRecord):CollectionChangeRecord {
- var key = getMapKey(record.item);
- // todo(vicb)
- //assert(this.map.containsKey(key));
- var recordList:_DuplicateItemRecordList = MapWrapper.get(this.map, key);
- // Remove the list of duplicates when it gets empty
- if (recordList.remove(record)) {
- MapWrapper.delete(this.map, key);
- }
- return record;
- }
-
- get isEmpty():boolean {
- return MapWrapper.size(this.map) === 0;
- }
-
- clear() {
- MapWrapper.clear(this.map);
- }
-
- toString():string {
- return '_DuplicateMap(' + stringify(this.map) + ')';
- }
-}
diff --git a/modules/angular2/src/change_detection/pipes/keyvalue_changes.js b/modules/angular2/src/change_detection/pipes/keyvalue_changes.js
deleted file mode 100644
index fa3d04e1f2b8..000000000000
--- a/modules/angular2/src/change_detection/pipes/keyvalue_changes.js
+++ /dev/null
@@ -1,383 +0,0 @@
-import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {stringify, looseIdentical, isJsObject} from 'angular2/src/facade/lang';
-
-import {NO_CHANGE, Pipe} from './pipe';
-
-export class KeyValueChangesFactory {
- supports(obj):boolean {
- return KeyValueChanges.supportsObj(obj);
- }
-
- create():Pipe {
- return new KeyValueChanges();
- }
-}
-
-export class KeyValueChanges extends Pipe {
- _records:Map;
-
- _mapHead:KVChangeRecord;
- _previousMapHead:KVChangeRecord;
- _changesHead:KVChangeRecord;
- _changesTail:KVChangeRecord;
- _additionsHead:KVChangeRecord;
- _additionsTail:KVChangeRecord;
- _removalsHead:KVChangeRecord;
- _removalsTail:KVChangeRecord;
-
- constructor() {
- super();
- this._records = MapWrapper.create();
- this._mapHead = null;
- this._previousMapHead = null;
- this._changesHead = null;
- this._changesTail = null;
- this._additionsHead = null;
- this._additionsTail = null;
- this._removalsHead = null;
- this._removalsTail = null;
- }
-
- static supportsObj(obj):boolean {
- return obj instanceof Map || isJsObject(obj);
- }
-
- supports(obj):boolean {
- return KeyValueChanges.supportsObj(obj);
- }
-
- transform(map){
- if (this.check(map)) {
- return this;
- } else {
- return NO_CHANGE;
- }
- }
-
- get isDirty():boolean {
- return this._additionsHead !== null ||
- this._changesHead !== null ||
- this._removalsHead !== null;
- }
-
- forEachItem(fn:Function) {
- var record:KVChangeRecord;
- for (record = this._mapHead; record !== null; record = record._next) {
- fn(record);
- }
- }
-
- forEachPreviousItem(fn:Function) {
- var record:KVChangeRecord;
- for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
- fn(record);
- }
- }
-
- forEachChangedItem(fn:Function) {
- var record:KVChangeRecord;
- for (record = this._changesHead; record !== null; record = record._nextChanged) {
- fn(record);
- }
- }
-
- forEachAddedItem(fn:Function){
- var record:KVChangeRecord;
- for (record = this._additionsHead; record !== null; record = record._nextAdded) {
- fn(record);
- }
- }
-
- forEachRemovedItem(fn:Function){
- var record:KVChangeRecord;
- for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
- fn(record);
- }
- }
-
- check(map):boolean {
- this._reset();
- var records = this._records;
- var oldSeqRecord:KVChangeRecord = this._mapHead;
- var lastOldSeqRecord:KVChangeRecord = null;
- var lastNewSeqRecord:KVChangeRecord = null;
- var seqChanged:boolean = false;
-
- this._forEach(map, (value, key) => {
- var newSeqRecord;
- if (oldSeqRecord !== null && key === oldSeqRecord.key) {
- newSeqRecord = oldSeqRecord;
- if (!looseIdentical(value, oldSeqRecord._currentValue)) {
- oldSeqRecord._previousValue = oldSeqRecord._currentValue;
- oldSeqRecord._currentValue = value;
- this._addToChanges(oldSeqRecord);
- }
- } else {
- seqChanged = true;
- if (oldSeqRecord !== null) {
- oldSeqRecord._next = null;
- this._removeFromSeq(lastOldSeqRecord, oldSeqRecord);
- this._addToRemovals(oldSeqRecord);
- }
- if (MapWrapper.contains(records, key)) {
- newSeqRecord = MapWrapper.get(records, key);
- } else {
- newSeqRecord = new KVChangeRecord(key);
- MapWrapper.set(records, key, newSeqRecord);
- newSeqRecord._currentValue = value;
- this._addToAdditions(newSeqRecord);
- }
- }
-
- if (seqChanged) {
- if (this._isInRemovals(newSeqRecord)) {
- this._removeFromRemovals(newSeqRecord);
- }
- if (lastNewSeqRecord == null) {
- this._mapHead = newSeqRecord;
- } else {
- lastNewSeqRecord._next = newSeqRecord;
- }
- }
- lastOldSeqRecord = oldSeqRecord;
- lastNewSeqRecord = newSeqRecord;
- oldSeqRecord = oldSeqRecord === null ? null : oldSeqRecord._next;
- });
- this._truncate(lastOldSeqRecord, oldSeqRecord);
- return this.isDirty;
- }
-
- _reset() {
- if (this.isDirty) {
- var record:KVChangeRecord;
- // Record the state of the mapping
- for (record = this._previousMapHead = this._mapHead;
- record !== null;
- record = record._next) {
- record._nextPrevious = record._next;
- }
-
- for (record = this._changesHead; record !== null; record = record._nextChanged) {
- record._previousValue = record._currentValue;
- }
-
- for (record = this._additionsHead; record != null; record = record._nextAdded) {
- record._previousValue = record._currentValue;
- }
-
- // todo(vicb) once assert is supported
- //assert(() {
- // var r = _changesHead;
- // while (r != null) {
- // var nextRecord = r._nextChanged;
- // r._nextChanged = null;
- // r = nextRecord;
- // }
- //
- // r = _additionsHead;
- // while (r != null) {
- // var nextRecord = r._nextAdded;
- // r._nextAdded = null;
- // r = nextRecord;
- // }
- //
- // r = _removalsHead;
- // while (r != null) {
- // var nextRecord = r._nextRemoved;
- // r._nextRemoved = null;
- // r = nextRecord;
- // }
- //
- // return true;
- //});
- this._changesHead = this._changesTail = null;
- this._additionsHead = this._additionsTail = null;
- this._removalsHead = this._removalsTail = null;
- }
- }
-
- _truncate(lastRecord:KVChangeRecord, record:KVChangeRecord) {
- while (record !== null) {
- if (lastRecord === null) {
- this._mapHead = null;
- } else {
- lastRecord._next = null;
- }
- var nextRecord = record._next;
- // todo(vicb) assert
- //assert((() {
- // record._next = null;
- // return true;
- //}));
- this._addToRemovals(record);
- lastRecord = record;
- record = nextRecord;
- }
-
- for (var rec:KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
- rec._previousValue = rec._currentValue;
- rec._currentValue = null;
- MapWrapper.delete(this._records, rec.key);
- }
- }
-
- _isInRemovals(record:KVChangeRecord) {
- return record === this._removalsHead ||
- record._nextRemoved !== null ||
- record._prevRemoved !== null;
- }
-
- _addToRemovals(record:KVChangeRecord) {
- // todo(vicb) assert
- //assert(record._next == null);
- //assert(record._nextAdded == null);
- //assert(record._nextChanged == null);
- //assert(record._nextRemoved == null);
- //assert(record._prevRemoved == null);
- if (this._removalsHead === null) {
- this._removalsHead = this._removalsTail = record;
- } else {
- this._removalsTail._nextRemoved = record;
- record._prevRemoved = this._removalsTail;
- this._removalsTail = record;
- }
- }
-
- _removeFromSeq(prev:KVChangeRecord, record:KVChangeRecord) {
- var next = record._next;
- if (prev === null) {
- this._mapHead = next;
- } else {
- prev._next = next;
- }
- // todo(vicb) assert
- //assert((() {
- // record._next = null;
- // return true;
- //})());
- }
-
- _removeFromRemovals(record:KVChangeRecord) {
- // todo(vicb) assert
- //assert(record._next == null);
- //assert(record._nextAdded == null);
- //assert(record._nextChanged == null);
-
- var prev = record._prevRemoved;
- var next = record._nextRemoved;
- if (prev === null) {
- this._removalsHead = next;
- } else {
- prev._nextRemoved = next;
- }
- if (next === null) {
- this._removalsTail = prev;
- } else {
- next._prevRemoved = prev;
- }
- record._prevRemoved = record._nextRemoved = null;
- }
-
- _addToAdditions(record:KVChangeRecord) {
- // todo(vicb): assert
- //assert(record._next == null);
- //assert(record._nextAdded == null);
- //assert(record._nextChanged == null);
- //assert(record._nextRemoved == null);
- //assert(record._prevRemoved == null);
- if (this._additionsHead === null) {
- this._additionsHead = this._additionsTail = record;
- } else {
- this._additionsTail._nextAdded = record;
- this._additionsTail = record;
- }
- }
-
- _addToChanges(record:KVChangeRecord) {
- // todo(vicb) assert
- //assert(record._nextAdded == null);
- //assert(record._nextChanged == null);
- //assert(record._nextRemoved == null);
- //assert(record._prevRemoved == null);
- if (this._changesHead === null) {
- this._changesHead = this._changesTail = record;
- } else {
- this._changesTail._nextChanged = record;
- this._changesTail = record;
- }
- }
-
- toString():string {
- var items = [];
- var previous = [];
- var changes = [];
- var additions = [];
- var removals = [];
- var record:KVChangeRecord;
-
- for (record = this._mapHead; record !== null; record = record._next) {
- ListWrapper.push(items, stringify(record));
- }
- for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
- ListWrapper.push(previous, stringify(record));
- }
- for (record = this._changesHead; record !== null; record = record._nextChanged) {
- ListWrapper.push(changes, stringify(record));
- }
- for (record = this._additionsHead; record !== null; record = record._nextAdded) {
- ListWrapper.push(additions, stringify(record));
- }
- for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
- ListWrapper.push(removals, stringify(record));
- }
-
- return "map: " + items.join(', ') + "\n" +
- "previous: " + previous.join(', ') + "\n" +
- "additions: " + additions.join(', ') + "\n" +
- "changes: " + changes.join(', ') + "\n" +
- "removals: " + removals.join(', ') + "\n";
- }
-
- _forEach(obj, fn:Function) {
- if (obj instanceof Map) {
- MapWrapper.forEach(obj, fn);
- } else {
- StringMapWrapper.forEach(obj, fn);
- }
- }
-}
-
-
-
-export class KVChangeRecord {
- key;
- _previousValue;
- _currentValue;
-
- _nextPrevious:KVChangeRecord;
- _next:KVChangeRecord;
- _nextAdded:KVChangeRecord;
- _nextRemoved:KVChangeRecord;
- _prevRemoved:KVChangeRecord;
- _nextChanged:KVChangeRecord;
-
- constructor(key) {
- this.key = key;
- this._previousValue = null;
- this._currentValue = null;
-
- this._nextPrevious = null;
- this._next = null;
- this._nextAdded = null;
- this._nextRemoved = null;
- this._prevRemoved = null;
- this._nextChanged = null;
- }
-
- toString():string {
- return looseIdentical(this._previousValue, this._currentValue) ?
- stringify(this.key) :
- (stringify(this.key) + '[' + stringify(this._previousValue) + '->' +
- stringify(this._currentValue) + ']');
- }
-}
diff --git a/modules/angular2/src/change_detection/pipes/null_pipe.js b/modules/angular2/src/change_detection/pipes/null_pipe.js
deleted file mode 100644
index 1055619ff3c4..000000000000
--- a/modules/angular2/src/change_detection/pipes/null_pipe.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {isBlank} from 'angular2/src/facade/lang';
-import {Pipe, NO_CHANGE} from './pipe';
-
-export class NullPipeFactory {
- supports(obj):boolean {
- return NullPipe.supportsObj(obj);
- }
-
- create():Pipe {
- return new NullPipe();
- }
-}
-
-export class NullPipe extends Pipe {
- called:boolean;
- constructor() {
- super();
- this.called = false;
- }
-
- static supportsObj(obj):boolean {
- return isBlank(obj);
- }
-
- supports(obj) {
- return NullPipe.supportsObj(obj);
- }
-
- transform(value) {
- if (! this.called) {
- this.called = true;
- return null;
- } else {
- return NO_CHANGE;
- }
- }
-}
diff --git a/modules/angular2/src/change_detection/pipes/pipe.js b/modules/angular2/src/change_detection/pipes/pipe.js
deleted file mode 100644
index c1213ce0afab..000000000000
--- a/modules/angular2/src/change_detection/pipes/pipe.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export var NO_CHANGE = new Object();
-
-export class Pipe {
- supports(obj):boolean {return false;}
- onDestroy() {}
- transform(value:any):any {return null;}
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/pipes/pipe_registry.js b/modules/angular2/src/change_detection/pipes/pipe_registry.js
deleted file mode 100644
index f4d1a7b2c3d4..000000000000
--- a/modules/angular2/src/change_detection/pipes/pipe_registry.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import {List, ListWrapper} from 'angular2/src/facade/collection';
-import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
-import {Pipe} from './pipe';
-
-export class PipeRegistry {
- config;
-
- constructor(config){
- this.config = config;
- }
-
- get(type:string, obj):Pipe {
- var listOfConfigs = this.config[type];
- if (isBlank(listOfConfigs)) {
- throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
- }
-
- var matchingConfig = ListWrapper.find(listOfConfigs,
- (pipeConfig) => pipeConfig.supports(obj));
-
- if (isBlank(matchingConfig)) {
- throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
- }
-
- return matchingConfig.create();
- }
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/proto_change_detector.js b/modules/angular2/src/change_detection/proto_change_detector.js
deleted file mode 100644
index dee42b9f226c..000000000000
--- a/modules/angular2/src/change_detection/proto_change_detector.js
+++ /dev/null
@@ -1,341 +0,0 @@
-import {isPresent, isBlank, BaseException, Type, isString} from 'angular2/src/facade/lang';
-import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-
-import {
- AccessMember,
- Assignment,
- AST,
- ASTWithSource,
- AstVisitor,
- Binary,
- Chain,
- Conditional,
- Pipe,
- FunctionCall,
- ImplicitReceiver,
- Interpolation,
- KeyedAccess,
- LiteralArray,
- LiteralMap,
- LiteralPrimitive,
- MethodCall,
- PrefixNot
- } from './parser/ast';
-
-import {ChangeRecord, ChangeDispatcher, ChangeDetector} 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 {coalesce} from './coalesce';
-
-import {
- ProtoRecord,
- RECORD_TYPE_SELF,
- RECORD_TYPE_PROPERTY,
- RECORD_TYPE_INVOKE_METHOD,
- RECORD_TYPE_CONST,
- RECORD_TYPE_INVOKE_CLOSURE,
- RECORD_TYPE_PRIMITIVE_OP,
- RECORD_TYPE_KEYED_ACCESS,
- RECORD_TYPE_PIPE,
- RECORD_TYPE_INTERPOLATE
- } from './proto_record';
-
-export class ProtoChangeDetector {
- addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
- instantiate(dispatcher:any):ChangeDetector{
- return null;
- }
-}
-
-export class DynamicProtoChangeDetector extends ProtoChangeDetector {
- _records:List;
- _recordBuilder:ProtoRecordBuilder;
- _pipeRegistry:PipeRegistry;
-
- constructor(pipeRegistry:PipeRegistry) {
- super();
- this._pipeRegistry = pipeRegistry;
- this._records = null;
- this._recordBuilder = new ProtoRecordBuilder();
- }
-
- addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
- this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
- }
-
- instantiate(dispatcher:any) {
- this._createRecordsIfNecessary();
- return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
- }
-
- _createRecordsIfNecessary() {
- if (isBlank(this._records)) {
- var records = this._recordBuilder.records;
- this._records = coalesce(records);
- }
- }
-}
-
-var _jitProtoChangeDetectorClassCounter:number = 0;
-export class JitProtoChangeDetector extends ProtoChangeDetector {
- _factory:Function;
- _recordBuilder:ProtoRecordBuilder;
- _pipeRegistry;
-
- constructor(pipeRegistry) {
- super();
- this._pipeRegistry = pipeRegistry;
- this._factory = null;
- this._recordBuilder = new ProtoRecordBuilder();
- }
-
- addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
- this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
- }
-
- instantiate(dispatcher:any) {
- this._createFactoryIfNecessary();
- return this._factory(dispatcher, this._pipeRegistry);
- }
-
- _createFactoryIfNecessary() {
- if (isBlank(this._factory)) {
- var c = _jitProtoChangeDetectorClassCounter++;
- var records = coalesce(this._recordBuilder.records);
- var typeName = `ChangeDetector${c}`;
- this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
- }
- }
-}
-
-class ProtoRecordBuilder {
- records:List;
-
- constructor() {
- this.records = [];
- }
-
- addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) {
- var last = ListWrapper.last(this.records);
- if (isPresent(last) && last.directiveMemento == directiveMemento) {
- last.lastInDirective = false;
- }
-
- var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length);
- if (! ListWrapper.isEmpty(pr)) {
- var last = ListWrapper.last(pr);
- last.lastInBinding = true;
- last.lastInDirective = true;
-
- this.records = ListWrapper.concat(this.records, pr);
- }
- }
-}
-
-class _ConvertAstIntoProtoRecords {
- protoRecords:List;
- bindingMemento:any;
- directiveMemento:any;
- contextIndex:number;
- expressionAsString:string;
-
- constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string) {
- this.protoRecords = [];
- this.bindingMemento = bindingMemento;
- this.directiveMemento = directiveMemento;
- this.contextIndex = contextIndex;
- this.expressionAsString = expressionAsString;
- }
-
- static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number) {
- var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString());
- ast.visit(c);
- return c.protoRecords;
- }
-
- visitImplicitReceiver(ast:ImplicitReceiver) {
- return 0;
- }
-
- visitInterpolation(ast:Interpolation) {
- var args = this._visitAll(ast.expressions);
- return this._addRecord(RECORD_TYPE_INTERPOLATE, "interpolate", _interpolationFn(ast.strings),
- args, ast.strings, 0);
- }
-
- visitLiteralPrimitive(ast:LiteralPrimitive) {
- return this._addRecord(RECORD_TYPE_CONST, "literal", ast.value, [], null, 0);
- }
-
- visitAccessMember(ast:AccessMember) {
- var receiver = ast.receiver.visit(this);
- return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
- }
-
- visitMethodCall(ast:MethodCall) {
- var receiver = ast.receiver.visit(this);
- var args = this._visitAll(ast.args);
- return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
- }
-
- visitFunctionCall(ast:FunctionCall) {
- var target = ast.target.visit(this);
- var args = this._visitAll(ast.args);
- return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
- }
-
- visitLiteralArray(ast:LiteralArray) {
- var primitiveName = `arrayFn${ast.expressions.length}`;
- return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, primitiveName, _arrayFn(ast.expressions.length),
- this._visitAll(ast.expressions), null, 0);
- }
-
- visitLiteralMap(ast:LiteralMap) {
- return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _mapPrimitiveName(ast.keys),
- ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null, 0);
- }
-
- visitBinary(ast:Binary) {
- var left = ast.left.visit(this);
- var right = ast.right.visit(this);
- return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _operationToPrimitiveName(ast.operation),
- _operationToFunction(ast.operation), [left, right], null, 0);
- }
-
- visitPrefixNot(ast:PrefixNot) {
- var exp = ast.expression.visit(this)
- return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "operation_negate",
- ChangeDetectionUtil.operation_negate, [exp], null, 0);
- }
-
- visitConditional(ast:Conditional) {
- var c = ast.condition.visit(this);
- var t = ast.trueExp.visit(this);
- var f = ast.falseExp.visit(this);
- return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "cond",
- ChangeDetectionUtil.cond, [c,t,f], null, 0);
- }
-
- visitPipe(ast:Pipe) {
- var value = ast.exp.visit(this);
- return this._addRecord(RECORD_TYPE_PIPE, ast.name, ast.name, [], null, value);
- }
-
- visitKeyedAccess(ast:KeyedAccess) {
- var obj = ast.obj.visit(this);
- var key = ast.key.visit(this);
- return this._addRecord(RECORD_TYPE_KEYED_ACCESS, "keyedAccess",
- ChangeDetectionUtil.keyedAccess, [key], null, obj);
- }
-
- _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;
- }
-
- _addRecord(type, name, funcOrValue, args, fixedArgs, context) {
- var selfIndex = ++ this.contextIndex;
- ListWrapper.push(this.protoRecords,
- new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
- this.bindingMemento, this.directiveMemento, this.expressionAsString, false, false));
- return selfIndex;
- }
-}
-
-
-function _arrayFn(length:number):Function {
- switch (length) {
- case 0: return ChangeDetectionUtil.arrayFn0;
- case 1: return ChangeDetectionUtil.arrayFn1;
- case 2: return ChangeDetectionUtil.arrayFn2;
- case 3: return ChangeDetectionUtil.arrayFn3;
- case 4: return ChangeDetectionUtil.arrayFn4;
- case 5: return ChangeDetectionUtil.arrayFn5;
- case 6: return ChangeDetectionUtil.arrayFn6;
- case 7: return ChangeDetectionUtil.arrayFn7;
- case 8: return ChangeDetectionUtil.arrayFn8;
- case 9: return ChangeDetectionUtil.arrayFn9;
- default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
- }
-}
-
-function _mapPrimitiveName(keys:List) {
- var stringifiedKeys = ListWrapper.join(
- ListWrapper.map(keys, (k) => isString(k) ? `"${k}"` : `${k}`),
- ", ");
- return `mapFn([${stringifiedKeys}])`;
-}
-
-function _operationToPrimitiveName(operation:string):string {
- switch(operation) {
- case '+' : return "operation_add";
- case '-' : return "operation_subtract";
- case '*' : return "operation_multiply";
- case '/' : return "operation_divide";
- case '%' : return "operation_remainder";
- case '==' : return "operation_equals";
- case '!=' : return "operation_not_equals";
- case '<' : return "operation_less_then";
- case '>' : return "operation_greater_then";
- case '<=' : return "operation_less_or_equals_then";
- case '>=' : return "operation_greater_or_equals_then";
- case '&&' : return "operation_logical_and";
- case '||' : return "operation_logical_or";
- default: throw new BaseException(`Unsupported operation ${operation}`);
- }
-}
-
-function _operationToFunction(operation:string):Function {
- switch(operation) {
- case '+' : return ChangeDetectionUtil.operation_add;
- case '-' : return ChangeDetectionUtil.operation_subtract;
- case '*' : return ChangeDetectionUtil.operation_multiply;
- case '/' : return ChangeDetectionUtil.operation_divide;
- case '%' : return ChangeDetectionUtil.operation_remainder;
- case '==' : return ChangeDetectionUtil.operation_equals;
- case '!=' : return ChangeDetectionUtil.operation_not_equals;
- case '<' : return ChangeDetectionUtil.operation_less_then;
- case '>' : return ChangeDetectionUtil.operation_greater_then;
- case '<=' : return ChangeDetectionUtil.operation_less_or_equals_then;
- case '>=' : return ChangeDetectionUtil.operation_greater_or_equals_then;
- case '&&' : return ChangeDetectionUtil.operation_logical_and;
- case '||' : return ChangeDetectionUtil.operation_logical_or;
- default: throw new BaseException(`Unsupported operation ${operation}`);
- }
-}
-
-function s(v) {
- return isPresent(v) ? `${v}` : '';
-}
-
-function _interpolationFn(strings:List) {
- var length = strings.length;
- var c0 = length > 0 ? strings[0] : null;
- var c1 = length > 1 ? strings[1] : null;
- var c2 = length > 2 ? strings[2] : null;
- var c3 = length > 3 ? strings[3] : null;
- var c4 = length > 4 ? strings[4] : null;
- var c5 = length > 5 ? strings[5] : null;
- var c6 = length > 6 ? strings[6] : null;
- var c7 = length > 7 ? strings[7] : null;
- var c8 = length > 8 ? strings[8] : null;
- var c9 = length > 9 ? strings[9] : null;
- switch (length - 1) {
- case 1: return (a1) => c0 + s(a1) + c1;
- case 2: return (a1, a2) => c0 + s(a1) + c1 + s(a2) + c2;
- case 3: return (a1, a2, a3) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3;
- case 4: return (a1, a2, a3, a4) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4;
- case 5: return (a1, a2, a3, a4, a5) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5;
- case 6: return (a1, a2, a3, a4, a5, a6) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6;
- case 7: return (a1, a2, a3, a4, a5, a6, a7) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7;
- case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8;
- case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8 + s(a9) + c9;
- default: throw new BaseException(`Does not support more than 9 expressions`);
- }
-}
\ No newline at end of file
diff --git a/modules/angular2/src/change_detection/proto_record.js b/modules/angular2/src/change_detection/proto_record.js
deleted file mode 100644
index d421d39d97ef..000000000000
--- a/modules/angular2/src/change_detection/proto_record.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import {List} from 'angular2/src/facade/collection';
-
-export const RECORD_TYPE_SELF = 0;
-export const RECORD_TYPE_CONST = 1;
-export const RECORD_TYPE_PRIMITIVE_OP = 2;
-export const RECORD_TYPE_PROPERTY = 3;
-export const RECORD_TYPE_INVOKE_METHOD = 4;
-export const RECORD_TYPE_INVOKE_CLOSURE = 5;
-export const RECORD_TYPE_KEYED_ACCESS = 6;
-export const RECORD_TYPE_PIPE = 8;
-export const RECORD_TYPE_INTERPOLATE = 9;
-
-export class ProtoRecord {
- mode:number;
- name:string;
- funcOrValue:any;
- args:List;
- fixedArgs:List;
- contextIndex:number;
- selfIndex:number;
- bindingMemento:any;
- directiveMemento:any;
- lastInBinding:boolean;
- lastInDirective:boolean;
- expressionAsString:string;
-
- constructor(mode:number,
- name:string,
- funcOrValue,
- args:List,
- fixedArgs:List,
- contextIndex:number,
- selfIndex:number,
- bindingMemento:any,
- directiveMemento:any,
- expressionAsString:string,
- lastInBinding:boolean,
- lastInDirective:boolean) {
-
- this.mode = mode;
- this.name = name;
- this.funcOrValue = funcOrValue;
- this.args = args;
- this.fixedArgs = fixedArgs;
- this.contextIndex = contextIndex;
- this.selfIndex = selfIndex;
- this.bindingMemento = bindingMemento;
- this.directiveMemento = directiveMemento;
- this.lastInBinding = lastInBinding;
- this.lastInDirective = lastInDirective;
- this.expressionAsString = expressionAsString;
- }
-
- isPureFunction():boolean {
- return this.mode === RECORD_TYPE_INTERPOLATE ||
- this.mode === RECORD_TYPE_PRIMITIVE_OP;
- }
-}
diff --git a/modules/angular2/src/common/common_directives.ts b/modules/angular2/src/common/common_directives.ts
new file mode 100644
index 000000000000..089395fe2acd
--- /dev/null
+++ b/modules/angular2/src/common/common_directives.ts
@@ -0,0 +1,49 @@
+import {CONST_EXPR, Type} from 'angular2/src/facade/lang';
+
+import {FORM_DIRECTIVES} from './forms';
+import {CORE_DIRECTIVES} from './directives';
+
+/**
+ * A collection of Angular core directives that are likely to be used in each and every Angular
+ * application. This includes core directives (e.g., NgIf and NgFor), and forms directives (e.g.,
+ * NgModel).
+ *
+ * This collection can be used to quickly enumerate all the built-in directives in the `directives`
+ * property of the `@Component` or `@View` decorators.
+ *
+ * ### Example
+ *
+ * Instead of writing:
+ *
+ * ```typescript
+ * import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm} from
+ * 'angular2/common';
+ * import {OtherDirective} from './myDirectives';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * templateUrl: 'myComponent.html',
+ * directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm,
+ * OtherDirective]
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ * ```
+ * one could import all the common directives at once:
+ *
+ * ```typescript
+ * import {COMMON_DIRECTIVES} from 'angular2/common';
+ * import {OtherDirective} from './myDirectives';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * templateUrl: 'myComponent.html',
+ * directives: [COMMON_DIRECTIVES, OtherDirective]
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ * ```
+ */
+export const COMMON_DIRECTIVES: Type[][] = CONST_EXPR([CORE_DIRECTIVES, FORM_DIRECTIVES]);
diff --git a/modules/angular2/src/common/directives.ts b/modules/angular2/src/common/directives.ts
new file mode 100644
index 000000000000..c777bae2dd3b
--- /dev/null
+++ b/modules/angular2/src/common/directives.ts
@@ -0,0 +1,12 @@
+/**
+ * @module
+ * @description
+ * Common directives shipped with Angular.
+ */
+export {NgClass} from './directives/ng_class';
+export {NgFor} from './directives/ng_for';
+export {NgIf} from './directives/ng_if';
+export {NgStyle} from './directives/ng_style';
+export {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './directives/ng_switch';
+export * from './directives/observable_list_diff';
+export {CORE_DIRECTIVES} from './directives/core_directives';
\ No newline at end of file
diff --git a/modules/angular2/src/common/directives/core_directives.ts b/modules/angular2/src/common/directives/core_directives.ts
new file mode 100644
index 000000000000..61f9e776127e
--- /dev/null
+++ b/modules/angular2/src/common/directives/core_directives.ts
@@ -0,0 +1,49 @@
+import {CONST_EXPR, Type} from 'angular2/src/facade/lang';
+import {NgClass} from './ng_class';
+import {NgFor} from './ng_for';
+import {NgIf} from './ng_if';
+import {NgStyle} from './ng_style';
+import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
+
+/**
+ * A collection of Angular core directives that are likely to be used in each and every Angular
+ * application.
+ *
+ * This collection can be used to quickly enumerate all the built-in directives in the `directives`
+ * property of the `@View` annotation.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
+ *
+ * Instead of writing:
+ *
+ * ```typescript
+ * import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault} from 'angular2/common';
+ * import {OtherDirective} from './myDirectives';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * templateUrl: 'myComponent.html',
+ * directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, OtherDirective]
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ * ```
+ * one could import all the core directives at once:
+ *
+ * ```typescript
+ * import {CORE_DIRECTIVES} from 'angular2/common';
+ * import {OtherDirective} from './myDirectives';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * templateUrl: 'myComponent.html',
+ * directives: [CORE_DIRECTIVES, OtherDirective]
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ * ```
+ */
+export const CORE_DIRECTIVES: Type[] =
+ CONST_EXPR([NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchWhen, NgSwitchDefault]);
diff --git a/modules/angular2/src/common/directives/ng_class.ts b/modules/angular2/src/common/directives/ng_class.ts
new file mode 100644
index 000000000000..2f77b0767f85
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_class.ts
@@ -0,0 +1,179 @@
+import {isPresent, isString, StringWrapper, isBlank, isArray} from 'angular2/src/facade/lang';
+import {
+ DoCheck,
+ OnDestroy,
+ Directive,
+ ElementRef,
+ IterableDiffer,
+ IterableDiffers,
+ KeyValueDiffer,
+ KeyValueDiffers,
+ Renderer
+} from 'angular2/core';
+import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
+
+/**
+ * The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
+ * an expression's evaluation result.
+ *
+ * The result of an expression evaluation is interpreted differently depending on type of
+ * the expression evaluation result:
+ * - `string` - all the CSS classes listed in a string (space delimited) are added
+ * - `Array` - all the CSS classes (Array elements) are added
+ * - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
+ * evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
+ * is added - otherwise it is removed.
+ *
+ * While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
+ * or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
+ * all the CSS class names in a template.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {NgClass} from 'angular2/common';
+ *
+ * @Component({
+ * selector: 'toggle-button',
+ * inputs: ['isDisabled'],
+ * template: `
+ *
+ * Click me!
+ *
`,
+ * styles: [`
+ * .button {
+ * width: 120px;
+ * border: medium solid black;
+ * }
+ *
+ * .active {
+ * background-color: red;
+ * }
+ *
+ * .disabled {
+ * color: gray;
+ * border: medium solid gray;
+ * }
+ * `]
+ * directives: [NgClass]
+ * })
+ * class ToggleButton {
+ * isOn = false;
+ * isDisabled = false;
+ *
+ * toggle(newState) {
+ * if (!this.isDisabled) {
+ * this.isOn = newState;
+ * }
+ * }
+ * }
+ * ```
+ */
+@Directive({selector: '[ngClass]', inputs: ['rawClass: ngClass', 'initialClasses: class']})
+export class NgClass implements DoCheck, OnDestroy {
+ private _differ: any;
+ private _mode: string;
+ private _initialClasses = [];
+ private _rawClass;
+
+ constructor(private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
+ private _ngEl: ElementRef, private _renderer: Renderer) {}
+
+ set initialClasses(v) {
+ this._applyInitialClasses(true);
+ this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
+ this._applyInitialClasses(false);
+ this._applyClasses(this._rawClass, false);
+ }
+
+ set rawClass(v) {
+ this._cleanupClasses(this._rawClass);
+
+ if (isString(v)) {
+ v = v.split(' ');
+ }
+
+ this._rawClass = v;
+ if (isPresent(v)) {
+ if (isListLikeIterable(v)) {
+ this._differ = this._iterableDiffers.find(v).create(null);
+ this._mode = 'iterable';
+ } else {
+ this._differ = this._keyValueDiffers.find(v).create(null);
+ this._mode = 'keyValue';
+ }
+ } else {
+ this._differ = null;
+ }
+ }
+
+ ngDoCheck(): void {
+ if (isPresent(this._differ)) {
+ var changes = this._differ.diff(this._rawClass);
+ if (isPresent(changes)) {
+ if (this._mode == 'iterable') {
+ this._applyIterableChanges(changes);
+ } else {
+ this._applyKeyValueChanges(changes);
+ }
+ }
+ }
+ }
+
+ ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
+
+ private _cleanupClasses(rawClassVal): void {
+ this._applyClasses(rawClassVal, true);
+ this._applyInitialClasses(false);
+ }
+
+ private _applyKeyValueChanges(changes: any): void {
+ changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
+ changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
+ changes.forEachRemovedItem((record) => {
+ if (record.previousValue) {
+ this._toggleClass(record.key, false);
+ }
+ });
+ }
+
+ private _applyIterableChanges(changes: any): void {
+ changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
+ changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
+ }
+
+ private _applyInitialClasses(isCleanup: boolean) {
+ this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
+ }
+
+ private _applyClasses(rawClassVal: string[] | Set| {[key: string]: string},
+ isCleanup: boolean) {
+ if (isPresent(rawClassVal)) {
+ if (isArray(rawClassVal)) {
+ (rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
+ } else if (rawClassVal instanceof Set) {
+ (>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
+ } else {
+ StringMapWrapper.forEach(<{[k: string]: string}>rawClassVal, (expVal, className) => {
+ if (expVal) this._toggleClass(className, !isCleanup);
+ });
+ }
+ }
+ }
+
+ private _toggleClass(className: string, enabled): void {
+ className = className.trim();
+ if (className.length > 0) {
+ if (className.indexOf(' ') > -1) {
+ var classes = className.split(/\s+/g);
+ for (var i = 0, len = classes.length; i < len; i++) {
+ this._renderer.setElementClass(this._ngEl, classes[i], enabled);
+ }
+ } else {
+ this._renderer.setElementClass(this._ngEl, className, enabled);
+ }
+ }
+ }
+}
diff --git a/modules/angular2/src/common/directives/ng_for.ts b/modules/angular2/src/common/directives/ng_for.ts
new file mode 100644
index 000000000000..7529f14f0893
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_for.ts
@@ -0,0 +1,162 @@
+import {
+ DoCheck,
+ Directive,
+ ChangeDetectorRef,
+ IterableDiffer,
+ IterableDiffers,
+ ViewContainerRef,
+ TemplateRef,
+ ViewRef
+} from 'angular2/core';
+import {isPresent, isBlank} from 'angular2/src/facade/lang';
+
+/**
+ * The `NgFor` directive instantiates a template once per item from an iterable. The context for
+ * each instantiated template inherits from the outer context with the given loop variable set
+ * to the current item from the iterable.
+ *
+ * # Local Variables
+ *
+ * `NgFor` provides several exported values that can be aliased to local variables:
+ *
+ * * `index` will be set to the current loop iteration for each template context.
+ * * `last` will be set to a boolean value indicating whether the item is the last one in the
+ * iteration.
+ * * `even` will be set to a boolean value indicating whether this item has an even index.
+ * * `odd` will be set to a boolean value indicating whether this item has an odd index.
+ *
+ * # Change Propagation
+ *
+ * When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
+ *
+ * * When an item is added, a new instance of the template is added to the DOM.
+ * * When an item is removed, its template instance is removed from the DOM.
+ * * When items are reordered, their respective templates are reordered in the DOM.
+ * * Otherwise, the DOM element for that item will remain the same.
+ *
+ * Angular uses object identity to track insertions and deletions within the iterator and reproduce
+ * those changes in the DOM. This has important implications for animations and any stateful
+ * controls
+ * (such as ` ` elements which accept user input) that are present. Inserted rows can be
+ * animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such
+ * as user input.
+ *
+ * It is possible for the identities of elements in the iterator to change while the data does not.
+ * This can happen, for example, if the iterator produced from an RPC to the server, and that
+ * RPC is re-run. Even if the data hasn't changed, the second response will produce objects with
+ * different identities, and Angular will tear down the entire DOM and rebuild it (as if all old
+ * elements were deleted and all new elements inserted). This is an expensive operation and should
+ * be avoided if possible.
+ *
+ * # Syntax
+ *
+ * - `... `
+ * - `... `
+ * - `... `
+ *
+ * ### Example
+ *
+ * See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
+ * example.
+ */
+@Directive({selector: '[ngFor][ngForOf]', inputs: ['ngForOf', 'ngForTemplate']})
+export class NgFor implements DoCheck {
+ /** @internal */
+ _ngForOf: any;
+ private _differ: IterableDiffer;
+
+ constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef,
+ private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
+
+ set ngForOf(value: any) {
+ this._ngForOf = value;
+ if (isBlank(this._differ) && isPresent(value)) {
+ this._differ = this._iterableDiffers.find(value).create(this._cdr);
+ }
+ }
+
+ set ngForTemplate(value: TemplateRef) {
+ if (isPresent(value)) {
+ this._templateRef = value;
+ }
+ }
+
+ ngDoCheck() {
+ if (isPresent(this._differ)) {
+ var changes = this._differ.diff(this._ngForOf);
+ if (isPresent(changes)) this._applyChanges(changes);
+ }
+ }
+
+ private _applyChanges(changes) {
+ // TODO(rado): check if change detection can produce a change record that is
+ // easier to consume than current.
+ var recordViewTuples = [];
+ changes.forEachRemovedItem((removedRecord) =>
+ recordViewTuples.push(new RecordViewTuple(removedRecord, null)));
+
+ changes.forEachMovedItem((movedRecord) =>
+ recordViewTuples.push(new RecordViewTuple(movedRecord, null)));
+
+ var insertTuples = this._bulkRemove(recordViewTuples);
+
+ changes.forEachAddedItem((addedRecord) =>
+ insertTuples.push(new RecordViewTuple(addedRecord, null)));
+
+ this._bulkInsert(insertTuples);
+
+ for (var i = 0; i < insertTuples.length; i++) {
+ this._perViewChange(insertTuples[i].view, insertTuples[i].record);
+ }
+
+ for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
+ this._viewContainer.get(i).setLocal('last', i === ilen - 1);
+ }
+ }
+
+ private _perViewChange(view, record) {
+ view.setLocal('\$implicit', record.item);
+ view.setLocal('index', record.currentIndex);
+ view.setLocal('even', (record.currentIndex % 2 == 0));
+ view.setLocal('odd', (record.currentIndex % 2 == 1));
+ }
+
+ private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
+ tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
+ var movedTuples = [];
+ for (var i = tuples.length - 1; i >= 0; i--) {
+ var tuple = tuples[i];
+ // separate moved views from removed views.
+ if (isPresent(tuple.record.currentIndex)) {
+ tuple.view = this._viewContainer.detach(tuple.record.previousIndex);
+ movedTuples.push(tuple);
+ } else {
+ this._viewContainer.remove(tuple.record.previousIndex);
+ }
+ }
+ return movedTuples;
+ }
+
+ private _bulkInsert(tuples: RecordViewTuple[]): RecordViewTuple[] {
+ tuples.sort((a, b) => a.record.currentIndex - b.record.currentIndex);
+ for (var i = 0; i < tuples.length; i++) {
+ var tuple = tuples[i];
+ if (isPresent(tuple.view)) {
+ this._viewContainer.insert(tuple.view, tuple.record.currentIndex);
+ } else {
+ tuple.view =
+ this._viewContainer.createEmbeddedView(this._templateRef, tuple.record.currentIndex);
+ }
+ }
+ return tuples;
+ }
+}
+
+class RecordViewTuple {
+ view: ViewRef;
+ record: any;
+ constructor(record, view) {
+ this.record = record;
+ this.view = view;
+ }
+}
diff --git a/modules/angular2/src/common/directives/ng_if.ts b/modules/angular2/src/common/directives/ng_if.ts
new file mode 100644
index 000000000000..f4bbfea1c484
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_if.ts
@@ -0,0 +1,41 @@
+import {Directive, ViewContainerRef, TemplateRef} from 'angular2/core';
+import {isBlank} from 'angular2/src/facade/lang';
+
+/**
+ * Removes or recreates a portion of the DOM tree based on an {expression}.
+ *
+ * If the expression assigned to `ngIf` evaluates to a false value then the element
+ * is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
+ *
+ * ```
+ * 0" class="error">
+ *
+ * {{errorCount}} errors detected
+ *
+ * ```
+ *
+ * ### Syntax
+ *
+ * - `...
`
+ * - `...
`
+ * - `...
`
+ */
+@Directive({selector: '[ngIf]', inputs: ['ngIf']})
+export class NgIf {
+ private _prevCondition: boolean = null;
+
+ constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
+
+ set ngIf(newCondition /* boolean */) {
+ if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
+ this._prevCondition = true;
+ this._viewContainer.createEmbeddedView(this._templateRef);
+ } else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
+ this._prevCondition = false;
+ this._viewContainer.clear();
+ }
+ }
+}
diff --git a/modules/angular2/src/common/directives/ng_style.ts b/modules/angular2/src/common/directives/ng_style.ts
new file mode 100644
index 000000000000..90b5d7448338
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_style.ts
@@ -0,0 +1,97 @@
+import {
+ DoCheck,
+ KeyValueDiffer,
+ KeyValueDiffers,
+ ElementRef,
+ Directive,
+ Renderer
+} from 'angular2/core';
+import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
+
+/**
+ * The `NgStyle` directive changes styles based on a result of expression evaluation.
+ *
+ * An expression assigned to the `ngStyle` property must evaluate to an object and the
+ * corresponding element styles are updated based on changes to this object. Style names to update
+ * are taken from the object's keys, and values - from the corresponding object's values.
+ *
+ * ### Syntax
+ *
+ * - `
`
+ * - `
` - here the `styleExp` must evaluate to an object
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
+ *
+ * ```
+ * import {Component} from 'angular2/core';
+ * import {NgStyle} from 'angular2/common';
+ *
+ * @Component({
+ * selector: 'ngStyle-example',
+ * template: `
+ *
+ * Change style of this text!
+ *
+ *
+ *
+ *
+ * Italic:
+ * Bold:
+ * Size:
+ * `,
+ * directives: [NgStyle]
+ * })
+ * export class NgStyleExample {
+ * style = 'normal';
+ * weight = 'normal';
+ * size = '20px';
+ *
+ * changeStyle($event: any) {
+ * this.style = $event.target.checked ? 'italic' : 'normal';
+ * }
+ *
+ * changeWeight($event: any) {
+ * this.weight = $event.target.checked ? 'bold' : 'normal';
+ * }
+ * }
+ * ```
+ *
+ * In this example the `font-style`, `font-size` and `font-weight` styles will be updated
+ * based on the `style` property's value changes.
+ */
+@Directive({selector: '[ngStyle]', inputs: ['rawStyle: ngStyle']})
+export class NgStyle implements DoCheck {
+ /** @internal */
+ _rawStyle;
+ /** @internal */
+ _differ: KeyValueDiffer;
+
+ constructor(private _differs: KeyValueDiffers, private _ngEl: ElementRef,
+ private _renderer: Renderer) {}
+
+ set rawStyle(v) {
+ this._rawStyle = v;
+ if (isBlank(this._differ) && isPresent(v)) {
+ this._differ = this._differs.find(this._rawStyle).create(null);
+ }
+ }
+
+ ngDoCheck() {
+ if (isPresent(this._differ)) {
+ var changes = this._differ.diff(this._rawStyle);
+ if (isPresent(changes)) {
+ this._applyChanges(changes);
+ }
+ }
+ }
+
+ private _applyChanges(changes: any): void {
+ changes.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
+ changes.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
+ changes.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
+ }
+
+ private _setStyle(name: string, val: string): void {
+ this._renderer.setElementStyle(this._ngEl, name, val);
+ }
+}
diff --git a/modules/angular2/src/common/directives/ng_switch.ts b/modules/angular2/src/common/directives/ng_switch.ts
new file mode 100644
index 000000000000..a25cb70a08ce
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_switch.ts
@@ -0,0 +1,202 @@
+import {Directive, Host, ViewContainerRef, TemplateRef} from 'angular2/core';
+import {isPresent, isBlank, normalizeBlank, CONST_EXPR} from 'angular2/src/facade/lang';
+import {ListWrapper, Map} from 'angular2/src/facade/collection';
+
+const _WHEN_DEFAULT = CONST_EXPR(new Object());
+
+class SwitchView {
+ constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
+
+ create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
+
+ destroy(): void { this._viewContainerRef.clear(); }
+}
+
+/**
+ * Adds or removes DOM sub-trees when their match expressions match the switch expression.
+ *
+ * Elements within `NgSwitch` but without `NgSwitchWhen` or `NgSwitchDefault` directives will be
+ * preserved at the location as specified in the template.
+ *
+ * `NgSwitch` simply inserts nested elements based on which match expression matches the value
+ * obtained from the evaluated switch expression. In other words, you define a container element
+ * (where you place the directive with a switch expression on the
+ * **`[ngSwitch]="..."` attribute**), define any inner elements inside of the directive and
+ * place a `[ngSwitchWhen]` attribute per element.
+ *
+ * The `ngSwitchWhen` property is used to inform `NgSwitch` which element to display when the
+ * expression is evaluated. If a matching expression is not found via a `ngSwitchWhen` property
+ * then an element with the `ngSwitchDefault` attribute is displayed.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
+ *
+ * ```typescript
+ * @Component({selector: 'app'})
+ * @View({
+ * template: `
+ * Value = {{value}}
+ * Increment
+ *
+ *
+ *
increment to start
+ *
0, increment again
+ *
1, increment again
+ *
2, stop incrementing
+ *
> 2, STOP!
+ *
+ *
+ *
+ *
+ *
+ * increment to start
+ * 0, increment again
+ * 1, increment again
+ * 2, stop incrementing
+ * > 2, STOP!
+ *
+ * `,
+ * directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]
+ * })
+ * export class App {
+ * value = 'init';
+ *
+ * inc() {
+ * this.value = this.value === 'init' ? 0 : this.value + 1;
+ * }
+ * }
+ *
+ * bootstrap(App).catch(err => console.error(err));
+ * ```
+ */
+@Directive({selector: '[ngSwitch]', inputs: ['ngSwitch']})
+export class NgSwitch {
+ private _switchValue: any;
+ private _useDefault: boolean = false;
+ private _valueViews = new Map();
+ private _activeViews: SwitchView[] = [];
+
+ set ngSwitch(value) {
+ // Empty the currently active ViewContainers
+ this._emptyAllActiveViews();
+
+ // Add the ViewContainers matching the value (with a fallback to default)
+ this._useDefault = false;
+ var views = this._valueViews.get(value);
+ if (isBlank(views)) {
+ this._useDefault = true;
+ views = normalizeBlank(this._valueViews.get(_WHEN_DEFAULT));
+ }
+ this._activateViews(views);
+
+ this._switchValue = value;
+ }
+
+ /** @internal */
+ _onWhenValueChanged(oldWhen, newWhen, view: SwitchView): void {
+ this._deregisterView(oldWhen, view);
+ this._registerView(newWhen, view);
+
+ if (oldWhen === this._switchValue) {
+ view.destroy();
+ ListWrapper.remove(this._activeViews, view);
+ } else if (newWhen === this._switchValue) {
+ if (this._useDefault) {
+ this._useDefault = false;
+ this._emptyAllActiveViews();
+ }
+ view.create();
+ this._activeViews.push(view);
+ }
+
+ // Switch to default when there is no more active ViewContainers
+ if (this._activeViews.length === 0 && !this._useDefault) {
+ this._useDefault = true;
+ this._activateViews(this._valueViews.get(_WHEN_DEFAULT));
+ }
+ }
+
+ /** @internal */
+ _emptyAllActiveViews(): void {
+ var activeContainers = this._activeViews;
+ for (var i = 0; i < activeContainers.length; i++) {
+ activeContainers[i].destroy();
+ }
+ this._activeViews = [];
+ }
+
+ /** @internal */
+ _activateViews(views: SwitchView[]): void {
+ // TODO(vicb): assert(this._activeViews.length === 0);
+ if (isPresent(views)) {
+ for (var i = 0; i < views.length; i++) {
+ views[i].create();
+ }
+ this._activeViews = views;
+ }
+ }
+
+ /** @internal */
+ _registerView(value, view: SwitchView): void {
+ var views = this._valueViews.get(value);
+ if (isBlank(views)) {
+ views = [];
+ this._valueViews.set(value, views);
+ }
+ views.push(view);
+ }
+
+ /** @internal */
+ _deregisterView(value, view: SwitchView): void {
+ // `_WHEN_DEFAULT` is used a marker for non-registered whens
+ if (value === _WHEN_DEFAULT) return;
+ var views = this._valueViews.get(value);
+ if (views.length == 1) {
+ this._valueViews.delete(value);
+ } else {
+ ListWrapper.remove(views, view);
+ }
+ }
+}
+
+/**
+ * Insert the sub-tree when the `ngSwitchWhen` expression evaluates to the same value as the
+ * enclosing switch expression.
+ *
+ * If multiple match expression match the switch expression value, all of them are displayed.
+ *
+ * See {@link NgSwitch} for more details and example.
+ */
+@Directive({selector: '[ngSwitchWhen]', inputs: ['ngSwitchWhen']})
+export class NgSwitchWhen {
+ // `_WHEN_DEFAULT` is used as a marker for a not yet initialized value
+ /** @internal */
+ _value: any = _WHEN_DEFAULT;
+ /** @internal */
+ _view: SwitchView;
+ private _switch: NgSwitch;
+
+ constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
+ @Host() ngSwitch: NgSwitch) {
+ this._switch = ngSwitch;
+ this._view = new SwitchView(viewContainer, templateRef);
+ }
+
+ set ngSwitchWhen(value) {
+ this._switch._onWhenValueChanged(this._value, value, this._view);
+ this._value = value;
+ }
+}
+
+/**
+ * Default case statements are displayed when no match expression matches the switch expression
+ * value.
+ *
+ * See {@link NgSwitch} for more details and example.
+ */
+@Directive({selector: '[ngSwitchDefault]'})
+export class NgSwitchDefault {
+ constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
+ @Host() sswitch: NgSwitch) {
+ sswitch._registerView(_WHEN_DEFAULT, new SwitchView(viewContainer, templateRef));
+ }
+}
diff --git a/modules/angular2/src/common/directives/observable_list_diff.dart b/modules/angular2/src/common/directives/observable_list_diff.dart
new file mode 100644
index 000000000000..cca7057facaf
--- /dev/null
+++ b/modules/angular2/src/common/directives/observable_list_diff.dart
@@ -0,0 +1,63 @@
+library angular2.directives.observable_list_iterable_diff;
+
+import 'package:observe/observe.dart' show ObservableList;
+import 'package:angular2/core.dart';
+import 'package:angular2/src/core/change_detection/differs/default_iterable_differ.dart';
+import 'dart:async';
+
+class ObservableListDiff extends DefaultIterableDiffer {
+ ChangeDetectorRef _ref;
+ ObservableListDiff(this._ref);
+
+ bool _updated = true;
+ ObservableList _collection;
+ StreamSubscription _subscription;
+
+ onDestroy() {
+ if (this._subscription != null) {
+ this._subscription.cancel();
+ this._subscription = null;
+ this._collection = null;
+ }
+ }
+
+ dynamic diff(ObservableList collection) {
+ if (collection is! ObservableList) {
+ throw "Cannot change the type of a collection";
+ }
+
+ // A new collection instance is passed in.
+ // - We need to set up a listener.
+ // - We need to diff collection.
+ if (!identical(_collection, collection)) {
+ _collection = collection;
+
+ if (_subscription != null) _subscription.cancel();
+ _subscription = collection.changes.listen((_) {
+ _updated = true;
+ _ref.markForCheck();
+ });
+ _updated = false;
+ return super.diff(collection);
+
+ // An update has been registered since the last change detection check.
+ // - We reset the flag.
+ // - We diff the collection.
+ } else if (_updated) {
+ _updated = false;
+ return super.diff(collection);
+
+ // No updates has been registered.
+ } else {
+ return null;
+ }
+ }
+}
+
+class ObservableListDiffFactory implements IterableDifferFactory {
+ const ObservableListDiffFactory();
+ bool supports(obj) => obj is ObservableList;
+ IterableDiffer create(ChangeDetectorRef cdRef) {
+ return new ObservableListDiff(cdRef);
+ }
+}
diff --git a/modules/angular2/src/common/directives/observable_list_diff.ts b/modules/angular2/src/common/directives/observable_list_diff.ts
new file mode 100644
index 000000000000..ee99cf404765
--- /dev/null
+++ b/modules/angular2/src/common/directives/observable_list_diff.ts
@@ -0,0 +1,10 @@
+// TS does not have Observables
+
+// I need to be here to make TypeScript think this is a module.
+import {} from 'angular2/src/facade/lang';
+
+/**
+ * This module exists in Dart, but not in Typescript. This exported symbol
+ * is only here to help Typescript think this is a module.
+ */
+export var workaround_empty_observable_list_diff: any;
diff --git a/modules/angular2/src/common/forms.ts b/modules/angular2/src/common/forms.ts
new file mode 100644
index 000000000000..e18ef145e8b3
--- /dev/null
+++ b/modules/angular2/src/common/forms.ts
@@ -0,0 +1,42 @@
+/**
+ * @module
+ * @description
+ * This module is used for handling user input, by defining and building a {@link ControlGroup} that
+ * consists of
+ * {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used
+ * to read information
+ * from the form DOM elements.
+ *
+ * This module is not included in the `angular2` module; you must import the forms module
+ * explicitly.
+ *
+ */
+export {AbstractControl, Control, ControlGroup, ControlArray} from './forms/model';
+
+export {AbstractControlDirective} from './forms/directives/abstract_control_directive';
+export {Form} from './forms/directives/form_interface';
+export {ControlContainer} from './forms/directives/control_container';
+export {NgControlName} from './forms/directives/ng_control_name';
+export {NgFormControl} from './forms/directives/ng_form_control';
+export {NgModel} from './forms/directives/ng_model';
+export {NgControl} from './forms/directives/ng_control';
+export {NgControlGroup} from './forms/directives/ng_control_group';
+export {NgFormModel} from './forms/directives/ng_form_model';
+export {NgForm} from './forms/directives/ng_form';
+export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms/directives/control_value_accessor';
+export {DefaultValueAccessor} from './forms/directives/default_value_accessor';
+export {NgControlStatus} from './forms/directives/ng_control_status';
+export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_accessor';
+export {
+ NgSelectOption,
+ SelectControlValueAccessor
+} from './forms/directives/select_control_value_accessor';
+export {FORM_DIRECTIVES} from './forms/directives';
+export {NG_VALIDATORS, NG_ASYNC_VALIDATORS, Validators} from './forms/validators';
+export {
+ RequiredValidator,
+ MinLengthValidator,
+ MaxLengthValidator,
+ Validator
+} from './forms/directives/validators';
+export {FormBuilder, FORM_PROVIDERS, FORM_BINDINGS} from './forms/form_builder';
\ No newline at end of file
diff --git a/modules/angular2/src/common/forms/directives.ts b/modules/angular2/src/common/forms/directives.ts
new file mode 100644
index 000000000000..0a3bcbbd5ae1
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives.ts
@@ -0,0 +1,71 @@
+import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
+import {NgControlName} from './directives/ng_control_name';
+import {NgFormControl} from './directives/ng_form_control';
+import {NgModel} from './directives/ng_model';
+import {NgControlGroup} from './directives/ng_control_group';
+import {NgFormModel} from './directives/ng_form_model';
+import {NgForm} from './directives/ng_form';
+import {DefaultValueAccessor} from './directives/default_value_accessor';
+import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
+import {NumberValueAccessor} from './directives/number_value_accessor';
+import {NgControlStatus} from './directives/ng_control_status';
+import {
+ SelectControlValueAccessor,
+ NgSelectOption
+} from './directives/select_control_value_accessor';
+import {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
+
+export {NgControlName} from './directives/ng_control_name';
+export {NgFormControl} from './directives/ng_form_control';
+export {NgModel} from './directives/ng_model';
+export {NgControlGroup} from './directives/ng_control_group';
+export {NgFormModel} from './directives/ng_form_model';
+export {NgForm} from './directives/ng_form';
+export {DefaultValueAccessor} from './directives/default_value_accessor';
+export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
+export {NumberValueAccessor} from './directives/number_value_accessor';
+export {NgControlStatus} from './directives/ng_control_status';
+export {
+ SelectControlValueAccessor,
+ NgSelectOption
+} from './directives/select_control_value_accessor';
+export {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
+export {NgControl} from './directives/ng_control';
+export {ControlValueAccessor} from './directives/control_value_accessor';
+
+/**
+ *
+ * A list of all the form directives used as part of a `@View` annotation.
+ *
+ * This is a shorthand for importing them each individually.
+ *
+ * ### Example
+ *
+ * ```typescript
+ * @Component({
+ * selector: 'my-app',
+ * directives: [FORM_DIRECTIVES]
+ * })
+ * class MyApp {}
+ * ```
+ */
+export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
+ NgControlName,
+ NgControlGroup,
+
+ NgFormControl,
+ NgModel,
+ NgFormModel,
+ NgForm,
+
+ NgSelectOption,
+ DefaultValueAccessor,
+ NumberValueAccessor,
+ CheckboxControlValueAccessor,
+ SelectControlValueAccessor,
+ NgControlStatus,
+
+ RequiredValidator,
+ MinLengthValidator,
+ MaxLengthValidator
+]);
diff --git a/modules/angular2/src/common/forms/directives/abstract_control_directive.ts b/modules/angular2/src/common/forms/directives/abstract_control_directive.ts
new file mode 100644
index 000000000000..a31a05ed11a4
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/abstract_control_directive.ts
@@ -0,0 +1,30 @@
+import {AbstractControl} from '../model';
+import {isPresent} from 'angular2/src/facade/lang';
+import {unimplemented} from 'angular2/src/facade/exceptions';
+
+/**
+ * Base class for control directives.
+ *
+ * Only used internally in the forms module.
+ */
+export abstract class AbstractControlDirective {
+ get control(): AbstractControl { return unimplemented(); }
+
+ get value(): any { return isPresent(this.control) ? this.control.value : null; }
+
+ get valid(): boolean { return isPresent(this.control) ? this.control.valid : null; }
+
+ get errors(): {[key: string]: any} {
+ return isPresent(this.control) ? this.control.errors : null;
+ }
+
+ get pristine(): boolean { return isPresent(this.control) ? this.control.pristine : null; }
+
+ get dirty(): boolean { return isPresent(this.control) ? this.control.dirty : null; }
+
+ get touched(): boolean { return isPresent(this.control) ? this.control.touched : null; }
+
+ get untouched(): boolean { return isPresent(this.control) ? this.control.untouched : null; }
+
+ get path(): string[] { return null; }
+}
diff --git a/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts
new file mode 100644
index 000000000000..3e52b078b3a0
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts
@@ -0,0 +1,34 @@
+import {Directive, Renderer, ElementRef, Self, forwardRef, Provider} from 'angular2/core';
+
+import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
+import {CONST_EXPR} from 'angular2/src/facade/lang';
+
+const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Provider(
+ NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => CheckboxControlValueAccessor), multi: true}));
+
+/**
+ * The accessor for writing a value and listening to changes on a checkbox input element.
+ *
+ * ### Example
+ * ```
+ *
+ * ```
+ */
+@Directive({
+ selector:
+ 'input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]',
+ host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
+ bindings: [CHECKBOX_VALUE_ACCESSOR]
+})
+export class CheckboxControlValueAccessor implements ControlValueAccessor {
+ onChange = (_) => {};
+ onTouched = () => {};
+
+ constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
+
+ writeValue(value: any): void {
+ this._renderer.setElementProperty(this._elementRef, 'checked', value);
+ }
+ registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
+ registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
+}
diff --git a/modules/angular2/src/common/forms/directives/control_container.ts b/modules/angular2/src/common/forms/directives/control_container.ts
new file mode 100644
index 000000000000..a84ad46430ee
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/control_container.ts
@@ -0,0 +1,21 @@
+import {Form} from './form_interface';
+import {AbstractControlDirective} from './abstract_control_directive';
+
+/**
+ * A directive that contains multiple {@link NgControl}s.
+ *
+ * Only used by the forms module.
+ */
+export class ControlContainer extends AbstractControlDirective {
+ name: string;
+
+ /**
+ * Get the form to which this container belongs.
+ */
+ get formDirective(): Form { return null; }
+
+ /**
+ * Get the path to this container.
+ */
+ get path(): string[] { return null; }
+}
diff --git a/modules/angular2/src/common/forms/directives/control_value_accessor.ts b/modules/angular2/src/common/forms/directives/control_value_accessor.ts
new file mode 100644
index 000000000000..90820f7d7071
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/control_value_accessor.ts
@@ -0,0 +1,34 @@
+import {OpaqueToken} from 'angular2/core';
+import {CONST_EXPR} from 'angular2/src/facade/lang';
+
+/**
+ * A bridge between a control and a native element.
+ *
+ * A `ControlValueAccessor` abstracts the operations of writing a new value to a
+ * DOM element representing an input control.
+ *
+ * Please see {@link DefaultValueAccessor} for more information.
+ */
+export interface ControlValueAccessor {
+ /**
+ * Write a new value to the element.
+ */
+ writeValue(obj: any): void;
+
+ /**
+ * Set the function to be called when the control receives a change event.
+ */
+ registerOnChange(fn: any): void;
+
+ /**
+ * Set the function to be called when the control receives a touch event.
+ */
+ registerOnTouched(fn: any): void;
+}
+
+/**
+ * Used to provide a {@link ControlValueAccessor} for form controls.
+ *
+ * See {@link DefaultValueAccessor} for how to implement one.
+ */
+export const NG_VALUE_ACCESSOR: OpaqueToken = CONST_EXPR(new OpaqueToken("NgValueAccessor"));
\ No newline at end of file
diff --git a/modules/angular2/src/common/forms/directives/default_value_accessor.ts b/modules/angular2/src/common/forms/directives/default_value_accessor.ts
new file mode 100644
index 000000000000..67c88b3facba
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/default_value_accessor.ts
@@ -0,0 +1,39 @@
+import {Directive, ElementRef, Renderer, Self, forwardRef, Provider} from 'angular2/core';
+import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
+import {isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
+
+const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
+ NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DefaultValueAccessor), multi: true}));
+
+/**
+ * The default accessor for writing a value and listening to changes that is used by the
+ * {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
+ *
+ * ### Example
+ * ```
+ *
+ * ```
+ */
+@Directive({
+ selector:
+ 'input:not([type=checkbox])[ngControl],textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
+ // TODO: vsavkin replace the above selector with the one below it once
+ // https://github.com/angular/angular/issues/3011 is implemented
+ // selector: '[ngControl],[ngModel],[ngFormControl]',
+ host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
+ bindings: [DEFAULT_VALUE_ACCESSOR]
+})
+export class DefaultValueAccessor implements ControlValueAccessor {
+ onChange = (_) => {};
+ onTouched = () => {};
+
+ constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
+
+ writeValue(value: any): void {
+ var normalizedValue = isBlank(value) ? '' : value;
+ this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue);
+ }
+
+ registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
+ registerOnTouched(fn: () => void): void { this.onTouched = fn; }
+}
diff --git a/modules/angular2/src/common/forms/directives/form_interface.ts b/modules/angular2/src/common/forms/directives/form_interface.ts
new file mode 100644
index 000000000000..7e3f04651c81
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/form_interface.ts
@@ -0,0 +1,45 @@
+import {NgControl} from './ng_control';
+import {NgControlGroup} from './ng_control_group';
+import {Control, ControlGroup} from '../model';
+
+/**
+ * An interface that {@link NgFormModel} and {@link NgForm} implement.
+ *
+ * Only used by the forms module.
+ */
+export interface Form {
+ /**
+ * Add a control to this form.
+ */
+ addControl(dir: NgControl): void;
+
+ /**
+ * Remove a control from this form.
+ */
+ removeControl(dir: NgControl): void;
+
+ /**
+ * Look up the {@link Control} associated with a particular {@link NgControl}.
+ */
+ getControl(dir: NgControl): Control;
+
+ /**
+ * Add a group of controls to this form.
+ */
+ addControlGroup(dir: NgControlGroup): void;
+
+ /**
+ * Remove a group of controls from this form.
+ */
+ removeControlGroup(dir: NgControlGroup): void;
+
+ /**
+ * Look up the {@link ControlGroup} associated with a particular {@link NgControlGroup}.
+ */
+ getControlGroup(dir: NgControlGroup): ControlGroup;
+
+ /**
+ * Update the model for a particular control with a new value.
+ */
+ updateModel(dir: NgControl, value: any): void;
+}
\ No newline at end of file
diff --git a/modules/angular2/src/common/forms/directives/ng_control.ts b/modules/angular2/src/common/forms/directives/ng_control.ts
new file mode 100644
index 000000000000..b43bff5dc01d
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/ng_control.ts
@@ -0,0 +1,19 @@
+import {ControlValueAccessor} from './control_value_accessor';
+import {AbstractControlDirective} from './abstract_control_directive';
+import {unimplemented} from 'angular2/src/facade/exceptions';
+
+/**
+ * A base class that all control directive extend.
+ * It binds a {@link Control} object to a DOM element.
+ *
+ * Used internally by Angular forms.
+ */
+export abstract class NgControl extends AbstractControlDirective {
+ name: string = null;
+ valueAccessor: ControlValueAccessor = null;
+
+ get validator(): Function { return unimplemented(); }
+ get asyncValidator(): Function { return unimplemented(); }
+
+ abstract viewToModelUpdate(newValue: any): void;
+}
diff --git a/modules/angular2/src/common/forms/directives/ng_control_group.ts b/modules/angular2/src/common/forms/directives/ng_control_group.ts
new file mode 100644
index 000000000000..84d6fa04f4d4
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/ng_control_group.ts
@@ -0,0 +1,112 @@
+import {
+ OnInit,
+ OnDestroy,
+ Directive,
+ Optional,
+ Inject,
+ Host,
+ SkipSelf,
+ forwardRef,
+ Provider,
+ Self
+} from 'angular2/core';
+import {CONST_EXPR} from 'angular2/src/facade/lang';
+
+import {ControlContainer} from './control_container';
+import {controlPath, composeValidators, composeAsyncValidators} from './shared';
+import {ControlGroup} from '../model';
+import {Form} from './form_interface';
+import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
+
+const controlGroupProvider =
+ CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgControlGroup)}));
+
+/**
+ * Creates and binds a control group to a DOM element.
+ *
+ * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
+ *
+ * ### Example ([live demo](http://plnkr.co/edit/7EJ11uGeaggViYM6T5nq?p=preview))
+ *
+ * ```typescript
+ * @Component({
+ * selector: 'my-app',
+ * directives: [FORM_DIRECTIVES],
+ * })
+ * @View({
+ * template: `
+ *
+ *
Angular2 Control & ControlGroup Example
+ *
+ *
+ * `,
+ * directives: [FORM_DIRECTIVES]
+ * })
+ * export class App {
+ * valueOf(cg: NgControlGroup): string {
+ * if (cg.control == null) {
+ * return null;
+ * }
+ * return JSON.stringify(cg.control.value, null, 2);
+ * }
+ * }
+ * ```
+ *
+ * This example declares a control group for a user's name. The value and validation state of
+ * this group can be accessed separately from the overall form.
+ */
+@Directive({
+ selector: '[ngControlGroup]',
+ providers: [controlGroupProvider],
+ inputs: ['name: ngControlGroup'],
+ exportAs: 'ngForm'
+})
+export class NgControlGroup extends ControlContainer implements OnInit,
+ OnDestroy {
+ /** @internal */
+ _parent: ControlContainer;
+
+ constructor(@Host() @SkipSelf() parent: ControlContainer,
+ @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
+ @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
+ super();
+ this._parent = parent;
+ }
+
+ ngOnInit(): void { this.formDirective.addControlGroup(this); }
+
+ ngOnDestroy(): void { this.formDirective.removeControlGroup(this); }
+
+ /**
+ * Get the {@link ControlGroup} backing this binding.
+ */
+ get control(): ControlGroup { return this.formDirective.getControlGroup(this); }
+
+ /**
+ * Get the path to this control group.
+ */
+ get path(): string[] { return controlPath(this.name, this._parent); }
+
+ /**
+ * Get the {@link Form} to which this group belongs.
+ */
+ get formDirective(): Form { return this._parent.formDirective; }
+
+ get validator(): Function { return composeValidators(this._validators); }
+
+ get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
+}
diff --git a/modules/angular2/src/common/forms/directives/ng_control_name.ts b/modules/angular2/src/common/forms/directives/ng_control_name.ts
new file mode 100644
index 000000000000..e54c847a0a7e
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/ng_control_name.ts
@@ -0,0 +1,144 @@
+import {CONST_EXPR} from 'angular2/src/facade/lang';
+import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
+
+import {
+ OnChanges,
+ OnDestroy,
+ SimpleChange,
+ Query,
+ Directive,
+ forwardRef,
+ Host,
+ SkipSelf,
+ Provider,
+ Inject,
+ Optional,
+ Self
+} from 'angular2/core';
+
+import {ControlContainer} from './control_container';
+import {NgControl} from './ng_control';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
+import {
+ controlPath,
+ composeValidators,
+ composeAsyncValidators,
+ isPropertyUpdated,
+ selectValueAccessor
+} from './shared';
+import {Control} from '../model';
+import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
+
+
+const controlNameBinding =
+ CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgControlName)}));
+
+/**
+ * Creates and binds a control with a specified name to a DOM element.
+ *
+ * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
+
+ * ### Example
+ *
+ * In this example, we create the login and password controls.
+ * We can work with each control separately: check its validity, get its value, listen to its
+ * changes.
+ *
+ * ```
+ * @Component({
+ * selector: "login-comp",
+ * directives: [FORM_DIRECTIVES],
+ * template: `
+ *
+ * `})
+ * class LoginComp {
+ * onLogIn(value): void {
+ * // value === {login: 'some login', password: 'some password'}
+ * }
+ * }
+ * ```
+ *
+ * We can also use ngModel to bind a domain model to the form.
+ *
+ * ```
+ * @Component({
+ * selector: "login-comp",
+ * directives: [FORM_DIRECTIVES],
+ * template: `
+ *
+ * `})
+ * class LoginComp {
+ * credentials: {login:string, password:string};
+ *
+ * onLogIn(): void {
+ * // this.credentials.login === "some login"
+ * // this.credentials.password === "some password"
+ * }
+ * }
+ * ```
+ */
+@Directive({
+ selector: '[ngControl]',
+ bindings: [controlNameBinding],
+ inputs: ['name: ngControl', 'model: ngModel'],
+ outputs: ['update: ngModelChange'],
+ exportAs: 'ngForm'
+})
+export class NgControlName extends NgControl implements OnChanges,
+ OnDestroy {
+ /** @internal */
+ update = new EventEmitter();
+ model: any;
+ viewModel: any;
+ private _added = false;
+
+ constructor(@Host() @SkipSelf() private _parent: ControlContainer,
+ @Optional() @Self() @Inject(NG_VALIDATORS) private _validators:
+ /* Array */ any[],
+ @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators:
+ /* Array */ any[],
+ @Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
+ valueAccessors: ControlValueAccessor[]) {
+ super();
+ this.valueAccessor = selectValueAccessor(this, valueAccessors);
+ }
+
+ ngOnChanges(changes: {[key: string]: SimpleChange}) {
+ if (!this._added) {
+ this.formDirective.addControl(this);
+ this._added = true;
+ }
+ if (isPropertyUpdated(changes, this.viewModel)) {
+ this.viewModel = this.model;
+ this.formDirective.updateModel(this, this.model);
+ }
+ }
+
+ ngOnDestroy(): void { this.formDirective.removeControl(this); }
+
+ viewToModelUpdate(newValue: any): void {
+ this.viewModel = newValue;
+ ObservableWrapper.callEmit(this.update, newValue);
+ }
+
+ get path(): string[] { return controlPath(this.name, this._parent); }
+
+ get formDirective(): any { return this._parent.formDirective; }
+
+ get validator(): Function { return composeValidators(this._validators); }
+
+ get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
+
+ get control(): Control { return this.formDirective.getControl(this); }
+}
diff --git a/modules/angular2/src/common/forms/directives/ng_control_status.ts b/modules/angular2/src/common/forms/directives/ng_control_status.ts
new file mode 100644
index 000000000000..38b7ccdabb4c
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/ng_control_status.ts
@@ -0,0 +1,43 @@
+import {Directive, Self} from 'angular2/core';
+import {NgControl} from './ng_control';
+import {isBlank, isPresent} from 'angular2/src/facade/lang';
+
+/**
+ * Directive automatically applied to Angular forms that sets CSS classes
+ * based on control status (valid/invalid/dirty/etc).
+ */
+@Directive({
+ selector: '[ngControl],[ngModel],[ngFormControl]',
+ host: {
+ '[class.ng-untouched]': 'ngClassUntouched',
+ '[class.ng-touched]': 'ngClassTouched',
+ '[class.ng-pristine]': 'ngClassPristine',
+ '[class.ng-dirty]': 'ngClassDirty',
+ '[class.ng-valid]': 'ngClassValid',
+ '[class.ng-invalid]': 'ngClassInvalid'
+ }
+})
+export class NgControlStatus {
+ private _cd: NgControl;
+
+ constructor(@Self() cd: NgControl) { this._cd = cd; }
+
+ get ngClassUntouched(): boolean {
+ return isPresent(this._cd.control) ? this._cd.control.untouched : false;
+ }
+ get ngClassTouched(): boolean {
+ return isPresent(this._cd.control) ? this._cd.control.touched : false;
+ }
+ get ngClassPristine(): boolean {
+ return isPresent(this._cd.control) ? this._cd.control.pristine : false;
+ }
+ get ngClassDirty(): boolean {
+ return isPresent(this._cd.control) ? this._cd.control.dirty : false;
+ }
+ get ngClassValid(): boolean {
+ return isPresent(this._cd.control) ? this._cd.control.valid : false;
+ }
+ get ngClassInvalid(): boolean {
+ return isPresent(this._cd.control) ? !this._cd.control.valid : false;
+ }
+}
diff --git a/modules/angular2/src/common/forms/directives/ng_form.ts b/modules/angular2/src/common/forms/directives/ng_form.ts
new file mode 100644
index 000000000000..639bbf4a58c8
--- /dev/null
+++ b/modules/angular2/src/common/forms/directives/ng_form.ts
@@ -0,0 +1,171 @@
+import {
+ PromiseWrapper,
+ ObservableWrapper,
+ EventEmitter,
+ PromiseCompleter
+} from 'angular2/src/facade/async';
+import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
+import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
+import {Directive, forwardRef, Provider, Optional, Inject, Self} from 'angular2/core';
+import {NgControl} from './ng_control';
+import {Form} from './form_interface';
+import {NgControlGroup} from './ng_control_group';
+import {ControlContainer} from './control_container';
+import {AbstractControl, ControlGroup, Control} from '../model';
+import {setUpControl, setUpControlGroup, composeValidators, composeAsyncValidators} from './shared';
+import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
+
+const formDirectiveProvider =
+ CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgForm)}));
+
+/**
+ * If `NgForm` is bound in a component, `