diff --git a/build.sh b/build.sh
index a9a9bad088..7b373e0aba 100755
--- a/build.sh
+++ b/build.sh
@@ -44,6 +44,7 @@ for PACKAGE in \
core \
compiler \
common \
+ forms \
platform-browser \
platform-browser-dynamic \
platform-server \
diff --git a/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts b/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts
index 71f2773f7f..9072bacdc0 100644
--- a/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts
+++ b/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts
@@ -95,6 +95,11 @@ export class NgForm extends ControlContainer implements Form {
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
super();
+ console.warn(`
+ *It looks like you're using the old forms module. This will be opt-in in the next RC, and
+ will eventually be removed in favor of the new forms module. For more information, see:
+ https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub
+ `);
this.form = new ControlGroup(
{}, null, composeValidators(validators), composeAsyncValidators(asyncValidators));
}
diff --git a/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts b/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts
index 30b0669280..8c358b141c 100644
--- a/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts
+++ b/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts
@@ -113,6 +113,11 @@ export class NgFormModel extends ControlContainer implements Form,
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
super();
+ console.warn(`
+ *It looks like you're using the old forms module. This will be opt-in in the next RC, and
+ will eventually be removed in favor of the new forms module. For more information, see:
+ https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub
+ `);
}
ngOnChanges(changes: SimpleChanges): void {
diff --git a/modules/@angular/common/src/forms.ts b/modules/@angular/common/src/forms.ts
deleted file mode 100644
index 8333300ac7..0000000000
--- a/modules/@angular/common/src/forms.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @module
- * @description
- * This module is used for handling user input, by defining and building a {@link FormGroup} that
- * consists of
- * {@link FormControl} objects, and mapping them onto the DOM. {@link FormControl} objects can then
- * be used
- * to read information
- * from the form DOM elements.
- *
- * Forms providers are not included in default providers; you must import these providers
- * explicitly.
- */
-import {Type} from '@angular/core';
-
-import {RadioControlRegistry} from './forms/directives/radio_control_value_accessor';
-import {FormBuilder} from './forms/form_builder';
-
-export {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, RadioButtonState} from './forms/directives';
-export {AbstractControlDirective} from './forms/directives/abstract_control_directive';
-export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_accessor';
-export {ControlContainer} from './forms/directives/control_container';
-export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms/directives/control_value_accessor';
-export {DefaultValueAccessor} from './forms/directives/default_value_accessor';
-export {Form} from './forms/directives/form_interface';
-export {NgControl} from './forms/directives/ng_control';
-export {NgControlStatus} from './forms/directives/ng_control_status';
-export {NgForm} from './forms/directives/ng_form';
-export {NgModel} from './forms/directives/ng_model';
-export {NgModelGroup} from './forms/directives/ng_model_group';
-export {FormControlDirective} from './forms/directives/reactive_directives/form_control_directive';
-export {FormControlName} from './forms/directives/reactive_directives/form_control_name';
-export {FormGroupDirective} from './forms/directives/reactive_directives/form_group_directive';
-export {FormGroupName} from './forms/directives/reactive_directives/form_group_name';
-export {NgSelectOption, SelectControlValueAccessor} from './forms/directives/select_control_value_accessor';
-export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms/directives/validators';
-export {FormBuilder} from './forms/form_builder';
-export {AbstractControl, FormArray, FormControl, FormGroup} from './forms/model';
-export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms/validators';
-
-
-
-/**
- * Shorthand set of providers used for building Angular forms.
- *
- * ### Example
- *
- * ```typescript
- * bootstrap(MyApp, [FORM_PROVIDERS]);
- * ```
- *
- * @experimental
- */
-export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry];
diff --git a/modules/@angular/core/core.dart b/modules/@angular/core/core.dart
index 28f59a9307..fe0cc13dd1 100644
--- a/modules/@angular/core/core.dart
+++ b/modules/@angular/core/core.dart
@@ -21,6 +21,7 @@ export './src/core/debug/debug_node.dart' show DebugElement,
asNativeElements;
export './src/core/testability/testability.dart';
export './src/core/change_detection.dart';
+export './src/core/platform_directives_and_pipes.dart';
export './src/core/platform_common_providers.dart';
export './src/core/application_common_providers.dart';
export './src/core/reflection/reflection.dart';
diff --git a/modules/@angular/core/index.ts b/modules/@angular/core/index.ts
index 5f4b02349f..f4d4a25f22 100644
--- a/modules/@angular/core/index.ts
+++ b/modules/@angular/core/index.ts
@@ -14,6 +14,7 @@ export * from './src/linker';
export {DebugElement, DebugNode, asNativeElements, getDebugNode} from './src/debug/debug_node';
export * from './src/testability/testability';
export * from './src/change_detection';
+export * from './src/platform_directives_and_pipes';
export * from './src/platform_common_providers';
export * from './src/application_common_providers';
export {wtfCreateScope, wtfLeave, wtfStartTimeRange, wtfEndTimeRange, WtfScopeFn} from './src/profile/profile';
diff --git a/modules/@angular/core/src/platform_directives_and_pipes.ts b/modules/@angular/core/src/platform_directives_and_pipes.ts
new file mode 100644
index 0000000000..03974185ec
--- /dev/null
+++ b/modules/@angular/core/src/platform_directives_and_pipes.ts
@@ -0,0 +1,58 @@
+import {OpaqueToken} from './di';
+
+/**
+ A token that can be provided when bootstrapping an application to make an array of directives
+ * available in every component of the application.
+ *
+ * ### Example
+ *
+ * ```typescript
+ * import {PLATFORM_DIRECTIVES} from '@angular/core';
+ * import {OtherDirective} from './myDirectives';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * template: `
+ *
+ *
+ * `
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ *
+ * bootstrap(MyComponent, [{provide: PLATFORM_DIRECTIVES, useValue: [OtherDirective],
+ multi:true}]);
+ * ```
+ * @stable
+ */
+
+export const PLATFORM_DIRECTIVES: OpaqueToken =
+ /*@ts2dart_const*/ new OpaqueToken('Platform Directives');
+
+/**
+ * A token that can be provided when bootstraping an application to make an array of pipes
+ * available in every component of the application.
+ *
+ * ### Example
+ *
+ * ```typescript
+ * import {PLATFORM_PIPES} from '@angular/core';
+ * import {OtherPipe} from './myPipe';
+ *
+ * @Component({
+ * selector: 'my-component',
+ * template: `
+ * {{123 | other-pipe}}
+ * `
+ * })
+ * export class MyComponent {
+ * ...
+ * }
+ *
+ * bootstrap(MyComponent, [{provide: PLATFORM_PIPES, useValue: [OtherPipe], multi:true}]);
+ * ```
+ * @stable
+ */
+
+export const PLATFORM_PIPES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Platform Pipes');
\ No newline at end of file
diff --git a/modules/@angular/forms/common.dart b/modules/@angular/forms/common.dart
new file mode 100644
index 0000000000..1592d948a1
--- /dev/null
+++ b/modules/@angular/forms/common.dart
@@ -0,0 +1 @@
+export 'index.dart';
diff --git a/modules/@angular/forms/index.ts b/modules/@angular/forms/index.ts
new file mode 100644
index 0000000000..17858b1182
--- /dev/null
+++ b/modules/@angular/forms/index.ts
@@ -0,0 +1 @@
+export * from './src/forms';
diff --git a/modules/@angular/forms/package.json b/modules/@angular/forms/package.json
new file mode 100644
index 0000000000..2448d234fd
--- /dev/null
+++ b/modules/@angular/forms/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@angular/forms",
+ "version": "0.0.0-PLACEHOLDER",
+ "description": "",
+ "main": "index.js",
+ "jsnext:main": "esm/index.js",
+ "typings": "index.d.ts",
+ "author": "angular",
+ "license": "MIT",
+ "peerDependencies": {
+ "@angular/core": "0.0.0-PLACEHOLDER",
+ "@angular/common": "0.0.0-PLACEHOLDER",
+ "@angular/compiler": "0.0.0-PLACEHOLDER"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular/angular.git"
+ }
+}
diff --git a/modules/@angular/forms/rollup.config.js b/modules/@angular/forms/rollup.config.js
new file mode 100644
index 0000000000..2e44f38fa5
--- /dev/null
+++ b/modules/@angular/forms/rollup.config.js
@@ -0,0 +1,19 @@
+
+export default {
+ entry: '../../../dist/packages-dist/forms/esm/index.js',
+ dest: '../../../dist/packages-dist/forms/esm/forms.umd.js',
+ format: 'umd',
+ moduleName: 'ng.forms',
+ globals: {
+ '@angular/core': 'ng.core',
+ '@angular/common': 'ng.common',
+ '@angular/compiler': 'ng.compiler',
+ 'rxjs/Subject': 'Rx',
+ 'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
+ 'rxjs/operator/toPromise': 'Rx.Observable.prototype',
+ 'rxjs/Observable': 'Rx'
+ },
+ plugins: [
+// nodeResolve({ jsnext: true, main: true }),
+ ]
+}
diff --git a/modules/@angular/common/src/forms/directives.ts b/modules/@angular/forms/src/directives.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives.ts
rename to modules/@angular/forms/src/directives.ts
diff --git a/modules/@angular/common/src/forms/directives/abstract_control_directive.ts b/modules/@angular/forms/src/directives/abstract_control_directive.ts
similarity index 90%
rename from modules/@angular/common/src/forms/directives/abstract_control_directive.ts
rename to modules/@angular/forms/src/directives/abstract_control_directive.ts
index 19155a3551..abd0444e1a 100644
--- a/modules/@angular/common/src/forms/directives/abstract_control_directive.ts
+++ b/modules/@angular/forms/src/directives/abstract_control_directive.ts
@@ -1,5 +1,5 @@
-import {unimplemented} from '../../facade/exceptions';
-import {isPresent} from '../../facade/lang';
+import {unimplemented} from '../facade/exceptions';
+import {isPresent} from '../facade/lang';
import {AbstractControl} from '../model';
diff --git a/modules/@angular/common/src/forms/directives/abstract_form_group_directive.ts b/modules/@angular/forms/src/directives/abstract_form_group_directive.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/abstract_form_group_directive.ts
rename to modules/@angular/forms/src/directives/abstract_form_group_directive.ts
diff --git a/modules/@angular/common/src/forms/directives/checkbox_value_accessor.ts b/modules/@angular/forms/src/directives/checkbox_value_accessor.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/checkbox_value_accessor.ts
rename to modules/@angular/forms/src/directives/checkbox_value_accessor.ts
diff --git a/modules/@angular/common/src/forms/directives/control_container.ts b/modules/@angular/forms/src/directives/control_container.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/control_container.ts
rename to modules/@angular/forms/src/directives/control_container.ts
diff --git a/modules/@angular/common/src/forms/directives/control_value_accessor.ts b/modules/@angular/forms/src/directives/control_value_accessor.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/control_value_accessor.ts
rename to modules/@angular/forms/src/directives/control_value_accessor.ts
diff --git a/modules/@angular/common/src/forms/directives/default_value_accessor.ts b/modules/@angular/forms/src/directives/default_value_accessor.ts
similarity index 97%
rename from modules/@angular/common/src/forms/directives/default_value_accessor.ts
rename to modules/@angular/forms/src/directives/default_value_accessor.ts
index 0bd476bcbd..fe7fbeb1ca 100644
--- a/modules/@angular/common/src/forms/directives/default_value_accessor.ts
+++ b/modules/@angular/forms/src/directives/default_value_accessor.ts
@@ -1,6 +1,6 @@
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
-import {isBlank} from '../../facade/lang';
+import {isBlank} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
diff --git a/modules/@angular/common/src/forms/directives/form_interface.ts b/modules/@angular/forms/src/directives/form_interface.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/form_interface.ts
rename to modules/@angular/forms/src/directives/form_interface.ts
diff --git a/modules/@angular/common/src/forms/directives/ng_control.ts b/modules/@angular/forms/src/directives/ng_control.ts
similarity index 92%
rename from modules/@angular/common/src/forms/directives/ng_control.ts
rename to modules/@angular/forms/src/directives/ng_control.ts
index e9d237b307..b9a8522773 100644
--- a/modules/@angular/common/src/forms/directives/ng_control.ts
+++ b/modules/@angular/forms/src/directives/ng_control.ts
@@ -1,4 +1,4 @@
-import {unimplemented} from '../../facade/exceptions';
+import {unimplemented} from '../facade/exceptions';
import {AbstractControlDirective} from './abstract_control_directive';
import {ControlValueAccessor} from './control_value_accessor';
diff --git a/modules/@angular/common/src/forms/directives/ng_control_status.ts b/modules/@angular/forms/src/directives/ng_control_status.ts
similarity index 96%
rename from modules/@angular/common/src/forms/directives/ng_control_status.ts
rename to modules/@angular/forms/src/directives/ng_control_status.ts
index 38627e246c..f5ba580bc0 100644
--- a/modules/@angular/common/src/forms/directives/ng_control_status.ts
+++ b/modules/@angular/forms/src/directives/ng_control_status.ts
@@ -1,6 +1,6 @@
import {Directive, Self} from '@angular/core';
-import {isPresent} from '../../facade/lang';
+import {isPresent} from '../facade/lang';
import {NgControl} from './ng_control';
diff --git a/modules/@angular/common/src/forms/directives/ng_form.ts b/modules/@angular/forms/src/directives/ng_form.ts
similarity index 97%
rename from modules/@angular/common/src/forms/directives/ng_form.ts
rename to modules/@angular/forms/src/directives/ng_form.ts
index 3ada4e4a4e..37c40d3b22 100644
--- a/modules/@angular/common/src/forms/directives/ng_form.ts
+++ b/modules/@angular/forms/src/directives/ng_form.ts
@@ -1,8 +1,8 @@
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
-import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../../facade/async';
-import {ListWrapper} from '../../facade/collection';
-import {isPresent} from '../../facade/lang';
+import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../facade/async';
+import {ListWrapper} from '../facade/collection';
+import {isPresent} from '../facade/lang';
import {AbstractControl, FormControl, FormGroup} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
diff --git a/modules/@angular/common/src/forms/directives/ng_model.ts b/modules/@angular/forms/src/directives/ng_model.ts
similarity index 97%
rename from modules/@angular/common/src/forms/directives/ng_model.ts
rename to modules/@angular/forms/src/directives/ng_model.ts
index a4a5fb19a6..2e125dd714 100644
--- a/modules/@angular/common/src/forms/directives/ng_model.ts
+++ b/modules/@angular/forms/src/directives/ng_model.ts
@@ -1,7 +1,7 @@
import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
-import {EventEmitter, ObservableWrapper} from '../../facade/async';
-import {BaseException} from '../../facade/exceptions';
+import {EventEmitter, ObservableWrapper} from '../facade/async';
+import {BaseException} from '../facade/exceptions';
import {FormControl} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
diff --git a/modules/@angular/common/src/forms/directives/ng_model_group.ts b/modules/@angular/forms/src/directives/ng_model_group.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/ng_model_group.ts
rename to modules/@angular/forms/src/directives/ng_model_group.ts
diff --git a/modules/@angular/common/src/forms/directives/normalize_validator.dart b/modules/@angular/forms/src/directives/normalize_validator.dart
similarity index 100%
rename from modules/@angular/common/src/forms/directives/normalize_validator.dart
rename to modules/@angular/forms/src/directives/normalize_validator.dart
diff --git a/modules/@angular/common/src/forms/directives/normalize_validator.ts b/modules/@angular/forms/src/directives/normalize_validator.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/normalize_validator.ts
rename to modules/@angular/forms/src/directives/normalize_validator.ts
diff --git a/modules/@angular/common/src/forms/directives/number_value_accessor.ts b/modules/@angular/forms/src/directives/number_value_accessor.ts
similarity index 96%
rename from modules/@angular/common/src/forms/directives/number_value_accessor.ts
rename to modules/@angular/forms/src/directives/number_value_accessor.ts
index 58b37d9161..f0910df337 100644
--- a/modules/@angular/common/src/forms/directives/number_value_accessor.ts
+++ b/modules/@angular/forms/src/directives/number_value_accessor.ts
@@ -1,6 +1,6 @@
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
-import {NumberWrapper} from '../../facade/lang';
+import {NumberWrapper} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
diff --git a/modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts b/modules/@angular/forms/src/directives/radio_control_value_accessor.ts
similarity index 97%
rename from modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts
rename to modules/@angular/forms/src/directives/radio_control_value_accessor.ts
index eb822c6b86..a62396789d 100644
--- a/modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts
+++ b/modules/@angular/forms/src/directives/radio_control_value_accessor.ts
@@ -1,7 +1,7 @@
import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer, forwardRef} from '@angular/core';
-import {ListWrapper} from '../../facade/collection';
-import {isPresent} from '../../facade/lang';
+import {ListWrapper} from '../facade/collection';
+import {isPresent} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts
similarity index 96%
rename from modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts
rename to modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts
index 0802c036dd..c69fcd09e4 100644
--- a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts
+++ b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts
@@ -1,7 +1,7 @@
import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
-import {EventEmitter, ObservableWrapper} from '../../../facade/async';
-import {StringMapWrapper} from '../../../facade/collection';
+import {EventEmitter, ObservableWrapper} from '../../facade/async';
+import {StringMapWrapper} from '../../facade/collection';
import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts b/modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts
similarity index 98%
rename from modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts
rename to modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts
index 2c17763020..45c8e89b20 100644
--- a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts
+++ b/modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts
@@ -1,6 +1,6 @@
import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
-import {EventEmitter, ObservableWrapper} from '../../../facade/async';
+import {EventEmitter, ObservableWrapper} from '../../facade/async';
import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts
similarity index 95%
rename from modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts
rename to modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts
index 1f3278efe7..f22e129e75 100644
--- a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts
+++ b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts
@@ -1,9 +1,9 @@
import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
-import {EventEmitter, ObservableWrapper} from '../../../facade/async';
-import {ListWrapper, StringMapWrapper} from '../../../facade/collection';
-import {BaseException} from '../../../facade/exceptions';
-import {isBlank} from '../../../facade/lang';
+import {EventEmitter, ObservableWrapper} from '../../facade/async';
+import {ListWrapper, StringMapWrapper} from '../../facade/collection';
+import {BaseException} from '../../facade/exceptions';
+import {isBlank} from '../../facade/lang';
import {FormControl, FormGroup} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
import {ControlContainer} from '../control_container';
diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_name.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_name.ts
similarity index 100%
rename from modules/@angular/common/src/forms/directives/reactive_directives/form_group_name.ts
rename to modules/@angular/forms/src/directives/reactive_directives/form_group_name.ts
diff --git a/modules/@angular/common/src/forms/directives/select_control_value_accessor.ts b/modules/@angular/forms/src/directives/select_control_value_accessor.ts
similarity index 97%
rename from modules/@angular/common/src/forms/directives/select_control_value_accessor.ts
rename to modules/@angular/forms/src/directives/select_control_value_accessor.ts
index 0ff036f5c9..ff03603979 100644
--- a/modules/@angular/common/src/forms/directives/select_control_value_accessor.ts
+++ b/modules/@angular/forms/src/directives/select_control_value_accessor.ts
@@ -1,7 +1,7 @@
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
-import {MapWrapper} from '../../facade/collection';
-import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../../facade/lang';
+import {MapWrapper} from '../facade/collection';
+import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
diff --git a/modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts b/modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts
similarity index 98%
rename from modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts
rename to modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts
index 860f7e7e13..886f73e445 100644
--- a/modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts
+++ b/modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts
@@ -1,7 +1,7 @@
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
-import {MapWrapper} from '../../facade/collection';
-import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../../facade/lang';
+import {MapWrapper} from '../facade/collection';
+import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
diff --git a/modules/@angular/common/src/forms/directives/shared.ts b/modules/@angular/forms/src/directives/shared.ts
similarity index 96%
rename from modules/@angular/common/src/forms/directives/shared.ts
rename to modules/@angular/forms/src/directives/shared.ts
index 827595fc5d..55dd9c54f0 100644
--- a/modules/@angular/common/src/forms/directives/shared.ts
+++ b/modules/@angular/forms/src/directives/shared.ts
@@ -1,6 +1,6 @@
-import {ListWrapper, StringMapWrapper} from '../../facade/collection';
-import {BaseException} from '../../facade/exceptions';
-import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang';
+import {ListWrapper, StringMapWrapper} from '../facade/collection';
+import {BaseException} from '../facade/exceptions';
+import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang';
import {FormControl, FormGroup} from '../model';
import {Validators} from '../validators';
diff --git a/modules/@angular/common/src/forms/directives/validators.ts b/modules/@angular/forms/src/directives/validators.ts
similarity index 98%
rename from modules/@angular/common/src/forms/directives/validators.ts
rename to modules/@angular/forms/src/directives/validators.ts
index fff44651bf..3c45ece10c 100644
--- a/modules/@angular/common/src/forms/directives/validators.ts
+++ b/modules/@angular/forms/src/directives/validators.ts
@@ -1,6 +1,6 @@
import {Attribute, Directive, forwardRef} from '@angular/core';
-import {NumberWrapper} from '../../facade/lang';
+import {NumberWrapper} from '../facade/lang';
import {AbstractControl} from '../model';
import {NG_VALIDATORS, Validators} from '../validators';
diff --git a/modules/@angular/forms/src/facade/async.dart b/modules/@angular/forms/src/facade/async.dart
new file mode 100644
index 0000000000..899a411b99
--- /dev/null
+++ b/modules/@angular/forms/src/facade/async.dart
@@ -0,0 +1,132 @@
+library angular2.core.facade.async;
+
+import 'dart:async';
+export 'dart:async' show Stream, StreamController, StreamSubscription;
+
+export 'promise.dart';
+
+class TimerWrapper {
+ static Timer setTimeout(fn(), int millis) =>
+ new Timer(new Duration(milliseconds: millis), fn);
+ static void clearTimeout(Timer timer) {
+ timer.cancel();
+ }
+
+ static Timer setInterval(fn(), int millis) {
+ var interval = new Duration(milliseconds: millis);
+ return new Timer.periodic(interval, (Timer timer) {
+ fn();
+ });
+ }
+
+ static void clearInterval(Timer timer) {
+ timer.cancel();
+ }
+}
+
+class ObservableWrapper {
+ static StreamSubscription subscribe/**/(Stream s, onNext(/*=T*/ value),
+ [onError, onComplete]) {
+ return s.listen(onNext,
+ onError: onError, onDone: onComplete, cancelOnError: true);
+ }
+
+ static bool isObservable(obs) {
+ return obs is Stream;
+ }
+
+ /**
+ * Returns whether `emitter` has any subscribers listening to events.
+ */
+ static bool hasSubscribers(EventEmitter emitter) {
+ return emitter._controller.hasListener;
+ }
+
+ static void dispose(StreamSubscription s) {
+ s.cancel();
+ }
+
+ @Deprecated('Use callEmit() instead')
+ static void callNext(EventEmitter emitter, value) {
+ emitter.add(value);
+ }
+
+ static void callEmit(EventEmitter emitter, value) {
+ emitter.add(value);
+ }
+
+ static void callError(EventEmitter emitter, error) {
+ emitter.addError(error);
+ }
+
+ static void callComplete(EventEmitter emitter) {
+ emitter.close();
+ }
+
+ static Stream fromPromise(Future f) {
+ return new Stream.fromFuture(f);
+ }
+
+ static Future toPromise(Stream s) {
+ return s.single;
+ }
+}
+
+class EventEmitter extends Stream {
+ StreamController _controller;
+
+ /// Creates an instance of [EventEmitter], which depending on [isAsync],
+ /// delivers events synchronously or asynchronously.
+ EventEmitter([bool isAsync = true]) {
+ _controller = new StreamController.broadcast(sync: !isAsync);
+ }
+
+ StreamSubscription listen(void onData(T event),
+ {Function onError, void onDone(), bool cancelOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+ }
+
+ void add(value) {
+ _controller.add(value);
+ }
+
+ void emit(value) {
+ _controller.add(value);
+ }
+
+ void addError(error) {
+ _controller.addError(error);
+ }
+
+ void close() {
+ _controller.close();
+ }
+}
+
+//todo(robwormald): maybe fix in ts2dart?
+class Subject extends Stream {
+ StreamController _controller;
+
+ Subject([bool isAsync = true]) {
+ _controller = new StreamController.broadcast(sync: !isAsync);
+ }
+
+ StreamSubscription listen(void onData(T data),
+ {Function onError, void onDone(), bool cancelOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+ }
+
+ void add(value) {
+ _controller.add(value);
+ }
+
+ void addError(error) {
+ _controller.addError(error);
+ }
+
+ void close() {
+ _controller.close();
+ }
+}
diff --git a/modules/@angular/forms/src/facade/async.ts b/modules/@angular/forms/src/facade/async.ts
new file mode 100644
index 0000000000..d9433792e3
--- /dev/null
+++ b/modules/@angular/forms/src/facade/async.ts
@@ -0,0 +1,169 @@
+import {Observable} from 'rxjs/Observable';
+import {Subject} from 'rxjs/Subject';
+import {PromiseObservable} from 'rxjs/observable/PromiseObservable';
+import {toPromise} from 'rxjs/operator/toPromise';
+
+import {global, noop} from './lang';
+
+export {Observable} from 'rxjs/Observable';
+export {Subject} from 'rxjs/Subject';
+export {PromiseCompleter, PromiseWrapper} from './promise';
+
+export class TimerWrapper {
+ static setTimeout(fn: (...args: any[]) => void, millis: number): number {
+ return global.setTimeout(fn, millis);
+ }
+ static clearTimeout(id: number): void { global.clearTimeout(id); }
+
+ static setInterval(fn: (...args: any[]) => void, millis: number): number {
+ return global.setInterval(fn, millis);
+ }
+ static clearInterval(id: number): void { global.clearInterval(id); }
+}
+
+export class ObservableWrapper {
+ // TODO(vsavkin): when we use rxnext, try inferring the generic type from the first arg
+ static subscribe(
+ emitter: any, onNext: (value: T) => void, onError?: (exception: any) => void,
+ onComplete: () => void = () => {}): Object {
+ onError = (typeof onError === 'function') && onError || noop;
+ onComplete = (typeof onComplete === 'function') && onComplete || noop;
+ return emitter.subscribe({next: onNext, error: onError, complete: onComplete});
+ }
+
+ static isObservable(obs: any): boolean { return !!obs.subscribe; }
+
+ /**
+ * Returns whether `obs` has any subscribers listening to events.
+ */
+ static hasSubscribers(obs: EventEmitter): boolean { return obs.observers.length > 0; }
+
+ static dispose(subscription: any) { subscription.unsubscribe(); }
+
+ /**
+ * @deprecated - use callEmit() instead
+ */
+ static callNext(emitter: EventEmitter, value: any) { emitter.next(value); }
+
+ static callEmit(emitter: EventEmitter, value: any) { emitter.emit(value); }
+
+ static callError(emitter: EventEmitter, error: any) { emitter.error(error); }
+
+ static callComplete(emitter: EventEmitter) { emitter.complete(); }
+
+ static fromPromise(promise: Promise): Observable {
+ return PromiseObservable.create(promise);
+ }
+
+ static toPromise(obj: Observable): Promise { return toPromise.call(obj); }
+}
+
+/**
+ * Use by directives and components to emit custom Events.
+ *
+ * ### Examples
+ *
+ * In the following example, `Zippy` alternatively emits `open` and `close` events when its
+ * title gets clicked:
+ *
+ * ```
+ * @Component({
+ * selector: 'zippy',
+ * template: `
+ *
+ *
Toggle
+ *
+ *
+ *
+ *
`})
+ * export class Zippy {
+ * visible: boolean = true;
+ * @Output() open: EventEmitter = new EventEmitter();
+ * @Output() close: EventEmitter = new EventEmitter();
+ *
+ * toggle() {
+ * this.visible = !this.visible;
+ * if (this.visible) {
+ * this.open.emit(null);
+ * } else {
+ * this.close.emit(null);
+ * }
+ * }
+ * }
+ * ```
+ *
+ * The events payload can be accessed by the parameter `$event` on the components output event
+ * handler:
+ *
+ * ```
+ *
+ * ```
+ *
+ * Uses Rx.Observable but provides an adapter to make it work as specified here:
+ * https://github.com/jhusain/observable-spec
+ *
+ * Once a reference implementation of the spec is available, switch to it.
+ * @stable
+ */
+export class EventEmitter extends Subject {
+ // TODO: mark this as internal once all the facades are gone
+ // we can't mark it as internal now because EventEmitter exported via @angular/core would not
+ // contain this property making it incompatible with all the code that uses EventEmitter via
+ // facades, which are local to the code and do not have this property stripped.
+ // tslint:disable-next-line
+ __isAsync: boolean;
+
+ /**
+ * Creates an instance of [EventEmitter], which depending on [isAsync],
+ * delivers events synchronously or asynchronously.
+ */
+ constructor(isAsync: boolean = false) {
+ super();
+ this.__isAsync = isAsync;
+ }
+
+ emit(value: T) { super.next(value); }
+
+ /**
+ * @deprecated - use .emit(value) instead
+ */
+ next(value: any) { super.next(value); }
+
+ subscribe(generatorOrNext?: any, error?: any, complete?: any): any {
+ let schedulerFn: any /** TODO #9100 */;
+ let errorFn = (err: any): any /** TODO #9100 */ => null;
+ let completeFn = (): any /** TODO #9100 */ => null;
+
+ if (generatorOrNext && typeof generatorOrNext === 'object') {
+ schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
+ setTimeout(() => generatorOrNext.next(value));
+ } : (value: any /** TODO #9100 */) => { generatorOrNext.next(value); };
+
+ if (generatorOrNext.error) {
+ errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } :
+ (err) => { generatorOrNext.error(err); };
+ }
+
+ if (generatorOrNext.complete) {
+ completeFn = this.__isAsync ? () => { setTimeout(() => generatorOrNext.complete()); } :
+ () => { generatorOrNext.complete(); };
+ }
+ } else {
+ schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
+ setTimeout(() => generatorOrNext(value));
+ } : (value: any /** TODO #9100 */) => { generatorOrNext(value); };
+
+ if (error) {
+ errorFn =
+ this.__isAsync ? (err) => { setTimeout(() => error(err)); } : (err) => { error(err); };
+ }
+
+ if (complete) {
+ completeFn =
+ this.__isAsync ? () => { setTimeout(() => complete()); } : () => { complete(); };
+ }
+ }
+
+ return super.subscribe(schedulerFn, errorFn, completeFn);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/base_wrapped_exception.dart b/modules/@angular/forms/src/facade/base_wrapped_exception.dart
new file mode 100644
index 0000000000..ab3b913f55
--- /dev/null
+++ b/modules/@angular/forms/src/facade/base_wrapped_exception.dart
@@ -0,0 +1,17 @@
+library angular.core.facade.base_wrapped_exception;
+
+/**
+ * A base class for the WrappedException that can be used to identify
+ * a WrappedException from ExceptionHandler without adding circular
+ * dependency.
+ */
+class BaseWrappedException extends Error {
+ BaseWrappedException();
+
+ get originalException => null;
+ get originalStack => null;
+
+ String get message => '';
+ String get wrapperMessage => '';
+ dynamic get context => null;
+}
diff --git a/modules/@angular/forms/src/facade/base_wrapped_exception.ts b/modules/@angular/forms/src/facade/base_wrapped_exception.ts
new file mode 100644
index 0000000000..22b486778e
--- /dev/null
+++ b/modules/@angular/forms/src/facade/base_wrapped_exception.ts
@@ -0,0 +1,15 @@
+/**
+ * A base class for the WrappedException that can be used to identify
+ * a WrappedException from ExceptionHandler without adding circular
+ * dependency.
+ */
+export class BaseWrappedException extends Error {
+ constructor(message: string) { super(message); }
+
+ get wrapperMessage(): string { return ''; }
+ get wrapperStack(): any { return null; }
+ get originalException(): any { return null; }
+ get originalStack(): any { return null; }
+ get context(): any { return null; }
+ get message(): string { return ''; }
+}
diff --git a/modules/@angular/forms/src/facade/browser.dart b/modules/@angular/forms/src/facade/browser.dart
new file mode 100644
index 0000000000..8c364d7f7d
--- /dev/null
+++ b/modules/@angular/forms/src/facade/browser.dart
@@ -0,0 +1,32 @@
+/**
+ * Dart version of browser APIs. This library depends on 'dart:html' and
+ * therefore can only run in the browser.
+ */
+library angular2.src.facade.browser;
+
+import 'dart:js' show context;
+import 'dart:html' show Location, window;
+
+export 'dart:html'
+ show
+ document,
+ window,
+ Element,
+ Node,
+ MouseEvent,
+ KeyboardEvent,
+ Event,
+ EventTarget,
+ History,
+ Location,
+ EventListener;
+
+Location get location => window.location;
+
+final _gc = context['gc'];
+
+void gc() {
+ if (_gc != null) {
+ _gc.apply(const []);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/browser.ts b/modules/@angular/forms/src/facade/browser.ts
new file mode 100644
index 0000000000..ca8277f540
--- /dev/null
+++ b/modules/@angular/forms/src/facade/browser.ts
@@ -0,0 +1,17 @@
+/**
+ * JS version of browser APIs. This library can only run in the browser.
+ */
+var win = typeof window !== 'undefined' && window || {};
+
+export {win as window};
+export var document = win.document;
+export var location = win.location;
+export var gc = win['gc'] ? () => win['gc']() : (): any /** TODO #9100 */ => null;
+export var performance = win['performance'] ? win['performance'] : null;
+export const Event = win['Event'];
+export const MouseEvent = win['MouseEvent'];
+export const KeyboardEvent = win['KeyboardEvent'];
+export const EventTarget = win['EventTarget'];
+export const History = win['History'];
+export const Location = win['Location'];
+export const EventListener = win['EventListener'];
diff --git a/modules/@angular/forms/src/facade/collection.dart b/modules/@angular/forms/src/facade/collection.dart
new file mode 100644
index 0000000000..cabedff0a4
--- /dev/null
+++ b/modules/@angular/forms/src/facade/collection.dart
@@ -0,0 +1,287 @@
+library facade.collection;
+
+import 'dart:collection' show IterableBase;
+import 'dart:convert' show JsonEncoder;
+export 'dart:core' show Iterator, Map, List, Set;
+import 'dart:math' show max, min;
+
+var jsonEncoder = new JsonEncoder();
+
+class MapIterator extends Iterator {
+ final Iterator _iterator;
+ final Map _map;
+
+ MapIterator(Map map)
+ : _map = map,
+ _iterator = map.keys.iterator;
+
+ bool moveNext() => _iterator.moveNext();
+
+ List get current {
+ return _iterator.current != null
+ ? [_iterator.current, _map[_iterator.current]]
+ : null;
+ }
+}
+
+class IterableMap extends IterableBase {
+ final Map _map;
+
+ IterableMap(Map map) : _map = map;
+
+ Iterator get iterator => new MapIterator(_map);
+}
+
+class MapWrapper {
+ static Map/**/ clone/**/(Map/**/ m) => new Map.from(m);
+
+ // in opposite to JS, Dart does not create a new map
+ static Map/**/ createFromStringMap/**/(Map/**/ m) => m;
+
+ // in opposite to JS, Dart does not create a new map
+ static Map/**/ toStringMap/**/(Map/**/ m) => m;
+
+ static Map/**/ createFromPairs/**/(List pairs) => pairs.fold(/**/{}, (m, p) {
+ m[p[0]] = p[1];
+ return m;
+ });
+
+ static void clearValues(Map m) {
+ for (var k in m.keys) {
+ m[k] = null;
+ }
+ }
+
+ static Iterable/*>*/ iterable/**/(Map/**/ m) => new IterableMap(m);
+ static List/**/ keys/**/(Map/**/ m) => m.keys.toList();
+ static List/**/ values/**/(Map/**/ m) => m.values.toList();
+}
+
+class StringMapWrapper {
+ static Map/**/ create/**/() => {};
+ static bool contains/**/(Map/**/ map, String key) => map.containsKey(key);
+ static get/**/(Map/**/ map, String key) => map[key];
+ static void set/**/(Map/**/ map, String key, /*=V*/value) {
+ map[key] = value;
+ }
+
+ static void delete/**/(Map/**/ m, String k) {
+ m.remove(k);
+ }
+
+ static void forEach/**/(Map/**/ m, fn(/*=V*/ v, String k)) {
+ m.forEach((k, v) => fn(v, k));
+ }
+
+ static Map/**/ merge/**/(Map/**/ a, Map/**/ b) {
+ var m = new Map/**/.from(a);
+ if (b != null) {
+ b.forEach((k, v) => m[k] = v);
+ }
+ return m;
+ }
+
+ static List keys(Map a) {
+ return a.keys.toList();
+ }
+
+ static List values(Map a) {
+ return a.values.toList();
+ }
+
+ static bool isEmpty(Map m) => m.isEmpty;
+ static bool equals/**/(Map/**/ m1, Map/**/ m2) {
+ if (m1.length != m2.length) {
+ return false;
+ }
+ for (var key in m1.keys) {
+ if (m1[key] != m2[key]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+typedef bool Predicate(T item);
+
+class ListWrapper {
+ static List/**/ clone/**/(Iterable/**/ l) => new List.from(l);
+ static List/**/ createFixedSize/**/(int size) => new List(size);
+ static List/**/ createGrowableSize/**/(int size) =>
+ new List.generate(size, (_) => null, growable: true);
+
+ static bool contains(List m, k) => m.contains(k);
+ static int indexOf(List list, value, [int startIndex = 0]) =>
+ list.indexOf(value, startIndex);
+ static int lastIndexOf(List list, value, [int startIndex = null]) =>
+ list.lastIndexOf(value, startIndex == null ? list.length : startIndex);
+
+ static void forEachWithIndex/**/(List/**/ list, fn(/*=T*/ item, int index)) {
+ for (var i = 0; i < list.length; ++i) {
+ fn(list[i], i);
+ }
+ }
+ static /*=T*/ first/**/(List/**/ list) => list.isEmpty ? null : list.first;
+ static /*=T*/ last/**/(List/**/ list) => list.isEmpty ? null : list.last;
+ static List/**/ reversed/**/(List/**/ list) => list.reversed.toList();
+ static List/**/ concat/**/(List/**/ a, List/**/ b) {
+ return new List()
+ ..length = a.length + b.length
+ ..setRange(0, a.length, a)
+ ..setRange(a.length, a.length + b.length, b);
+ }
+
+ static void insert/**/(List/**/ l, int index, /*=T*/ value) {
+ l.insert(index, value);
+ }
+
+ static removeAt(List l, int index) => l.removeAt(index);
+ static void removeAll/**/(List/**/ list, List/**/ items) {
+ for (var i = 0; i < items.length; ++i) {
+ list.remove(items[i]);
+ }
+ }
+
+ static bool remove/**/(List/**/ list, /*=T*/ item) => list.remove(item);
+ static void clear(List l) {
+ l.clear();
+ }
+
+ static bool isEmpty(Iterable list) => list.isEmpty;
+ static void fill/**/(List/**/ l, /*=T*/ value, [int start = 0, int end]) {
+ l.fillRange(_startOffset(l, start), _endOffset(l, end), value);
+ }
+
+ static bool equals/**/(List/**/ a, List/**/ b) {
+ if (a.length != b.length) return false;
+ for (var i = 0; i < a.length; ++i) {
+ if (a[i] != b[i]) return false;
+ }
+ return true;
+ }
+
+ static List/**/ slice/**/(List/**/ l, [int from = 0, int to]) {
+ from = _startOffset(l, from);
+ to = _endOffset(l, to);
+ //in JS if from > to an empty array is returned
+ if (to != null && from > to) {
+ return [];
+ }
+ return l.sublist(from, to);
+ }
+
+ static List/**/ splice/**/(List/**/ l, int from, int length) {
+ from = _startOffset(l, from);
+ var to = from + length;
+ var sub = l.sublist(from, to);
+ l.removeRange(from, to);
+ return sub;
+ }
+
+ static void sort/**/(List/**/ l, [int compareFn(/*=T*/a, /*=T*/b) = null]) {
+ if (compareFn == null) {
+ l.sort();
+ } else {
+ l.sort(compareFn);
+ }
+ }
+
+ static String toJSON(List l) {
+ return jsonEncoder.convert(l);
+ }
+
+ // JS splice, slice, fill functions can take start < 0 which indicates a position relative to
+ // the end of the list
+ static int _startOffset(List l, int start) {
+ int len = l.length;
+ return start < 0 ? max(len + start, 0) : min(start, len);
+ }
+
+ // JS splice, slice, fill functions can take end < 0 which indicates a position relative to
+ // the end of the list
+ static int _endOffset(List l, int end) {
+ int len = l.length;
+ if (end == null) return len;
+ return end < 0 ? max(len + end, 0) : min(end, len);
+ }
+
+ static maximum(List l, fn(item)) {
+ if (l.length == 0) {
+ return null;
+ }
+ var solution = null;
+ var maxValue = double.NEGATIVE_INFINITY;
+ for (var index = 0; index < l.length; index++) {
+ var candidate = l[index];
+ if (candidate == null) {
+ continue;
+ }
+ var candidateValue = fn(candidate);
+ if (candidateValue > maxValue) {
+ solution = candidate;
+ maxValue = candidateValue;
+ }
+ }
+ return solution;
+ }
+
+ static List flatten(List l) {
+ var target = [];
+ _flattenArray(l, target);
+ return target;
+ }
+
+ static addAll(List l, List source) {
+ l.addAll(source);
+ }
+}
+
+List _flattenArray(List source, List target) {
+ if (source != null) {
+ for (var i = 0; i < source.length; i++) {
+ var item = source[i];
+ if (item is List) {
+ _flattenArray(item, target);
+ } else {
+ target.add(item);
+ }
+ }
+ }
+ return target;
+}
+
+
+bool isListLikeIterable(obj) => obj is Iterable;
+
+bool areIterablesEqual/**/(
+ Iterable/**/ a,
+ Iterable/**/ b,
+ bool comparator(/*=T*/a, /*=T*/b))
+{
+ var iterator1 = a.iterator;
+ var iterator2 = b.iterator;
+
+ while (true) {
+ var done1 = !iterator1.moveNext();
+ var done2 = !iterator2.moveNext();
+ if (done1 && done2) return true;
+ if (done1 || done2) return false;
+ if (!comparator(iterator1.current, iterator2.current)) return false;
+ }
+}
+
+void iterateListLike/**/(Iterable/**/ iter, fn(/*=T*/item)) {
+ assert(iter is Iterable);
+ for (var item in iter) {
+ fn(item);
+ }
+}
+
+class SetWrapper {
+ static Set/**/ createFromList/**/(List/**/ l) => new Set.from(l);
+ static bool has/**/(Set/**/ s, /*=T*/key) => s.contains(key);
+ static void delete/**/(Set/**/ m, /*=T*/k) {
+ m.remove(k);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/collection.ts b/modules/@angular/forms/src/facade/collection.ts
new file mode 100644
index 0000000000..cf67e2510d
--- /dev/null
+++ b/modules/@angular/forms/src/facade/collection.ts
@@ -0,0 +1,352 @@
+import {getSymbolIterator, global, isArray, isBlank, isJsObject, isPresent} from './lang';
+
+export var Map = global.Map;
+export var Set = global.Set;
+
+// Safari and Internet Explorer do not support the iterable parameter to the
+// Map constructor. We work around that by manually adding the items.
+var createMapFromPairs: {(pairs: any[]): Map} = (function() {
+ try {
+ if (new Map([[1, 2]]).size === 1) {
+ return function createMapFromPairs(pairs: any[]): Map { return new Map(pairs); };
+ }
+ } catch (e) {
+ }
+ return function createMapAndPopulateFromPairs(pairs: any[]): Map {
+ var map = new Map();
+ for (var i = 0; i < pairs.length; i++) {
+ var pair = pairs[i];
+ map.set(pair[0], pair[1]);
+ }
+ return map;
+ };
+})();
+var createMapFromMap: {(m: Map): Map} = (function() {
+ try {
+ if (new Map(new Map())) {
+ return function createMapFromMap(m: Map): Map { return new Map(m); };
+ }
+ } catch (e) {
+ }
+ return function createMapAndPopulateFromMap(m: Map): Map {
+ var map = new Map();
+ m.forEach((v, k) => { map.set(k, v); });
+ return map;
+ };
+})();
+var _clearValues: {(m: Map): void} = (function() {
+ if (((new Map()).keys()).next) {
+ return function _clearValues(m: Map) {
+ var keyIterator = m.keys();
+ var k: any /** TODO #???? */;
+ while (!((k = (keyIterator).next()).done)) {
+ m.set(k.value, null);
+ }
+ };
+ } else {
+ return function _clearValuesWithForeEach(m: Map) {
+ m.forEach((v, k) => { m.set(k, null); });
+ };
+ }
+})();
+// Safari doesn't implement MapIterator.next(), which is used is Traceur's polyfill of Array.from
+// TODO(mlaval): remove the work around once we have a working polyfill of Array.from
+var _arrayFromMap: {(m: Map, getValues: boolean): any[]} = (function() {
+ try {
+ if (((new Map()).values()).next) {
+ return function createArrayFromMap(m: Map, getValues: boolean): any[] {
+ return getValues ? (Array).from(m.values()) : (Array).from(m.keys());
+ };
+ }
+ } catch (e) {
+ }
+ return function createArrayFromMapWithForeach(m: Map, getValues: boolean): any[] {
+ var res = ListWrapper.createFixedSize(m.size), i = 0;
+ m.forEach((v, k) => {
+ res[i] = getValues ? v : k;
+ i++;
+ });
+ return res;
+ };
+})();
+
+export class MapWrapper {
+ static clone(m: Map): Map { return createMapFromMap(m); }
+ static createFromStringMap(stringMap: {[key: string]: T}): Map {
+ var result = new Map();
+ for (var prop in stringMap) {
+ result.set(prop, stringMap[prop]);
+ }
+ return result;
+ }
+ static toStringMap(m: Map): {[key: string]: T} {
+ var r: {[key: string]: T} = {};
+ m.forEach((v, k) => r[k] = v);
+ return r;
+ }
+ static createFromPairs(pairs: any[]): Map { return createMapFromPairs(pairs); }
+ static clearValues(m: Map) { _clearValues(m); }
+ static iterable(m: T): T { return m; }
+ static keys(m: Map): K[] { return _arrayFromMap(m, false); }
+ static values(m: Map): V[] { return _arrayFromMap(m, true); }
+}
+
+/**
+ * Wraps Javascript Objects
+ */
+export class StringMapWrapper {
+ static create(): {[k: /*any*/ string]: any} {
+ // Note: We are not using Object.create(null) here due to
+ // performance!
+ // http://jsperf.com/ng2-object-create-null
+ return {};
+ }
+ static contains(map: {[key: string]: any}, key: string): boolean {
+ return map.hasOwnProperty(key);
+ }
+ static get(map: {[key: string]: V}, key: string): V {
+ return map.hasOwnProperty(key) ? map[key] : undefined;
+ }
+ static set(map: {[key: string]: V}, key: string, value: V) { map[key] = value; }
+ static keys(map: {[key: string]: any}): string[] { return Object.keys(map); }
+ static values(map: {[key: string]: T}): T[] {
+ return Object.keys(map).reduce((r, a) => {
+ r.push(map[a]);
+ return r;
+ }, []);
+ }
+ static isEmpty(map: {[key: string]: any}): boolean {
+ for (var prop in map) {
+ return false;
+ }
+ return true;
+ }
+ static delete (map: {[key: string]: any}, key: string) { delete map[key]; }
+ static forEach(map: {[key: string]: V}, callback: /*(V, K) => void*/ Function) {
+ for (var prop in map) {
+ if (map.hasOwnProperty(prop)) {
+ callback(map[prop], prop);
+ }
+ }
+ }
+
+ static merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
+ var m: {[key: string]: V} = {};
+
+ for (var attr in m1) {
+ if (m1.hasOwnProperty(attr)) {
+ m[attr] = m1[attr];
+ }
+ }
+
+ for (var attr in m2) {
+ if (m2.hasOwnProperty(attr)) {
+ m[attr] = m2[attr];
+ }
+ }
+
+ return m;
+ }
+
+ static equals(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean {
+ var k1 = Object.keys(m1);
+ var k2 = Object.keys(m2);
+ if (k1.length != k2.length) {
+ return false;
+ }
+ var key: any /** TODO #???? */;
+ for (var i = 0; i < k1.length; i++) {
+ key = k1[i];
+ if (m1[key] !== m2[key]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+/**
+ * A boolean-valued function over a value, possibly including context information
+ * regarding that value's position in an array.
+ */
+export interface Predicate { (value: T, index?: number, array?: T[]): boolean; }
+
+export class ListWrapper {
+ // JS has no way to express a statically fixed size list, but dart does so we
+ // keep both methods.
+ static createFixedSize(size: number): any[] { return new Array(size); }
+ static createGrowableSize(size: number): any[] { return new Array(size); }
+ static clone(array: T[]): T[] { return array.slice(0); }
+ static forEachWithIndex(array: T[], fn: (t: T, n: number) => void) {
+ for (var i = 0; i < array.length; i++) {
+ fn(array[i], i);
+ }
+ }
+ static first(array: T[]): T {
+ if (!array) return null;
+ return array[0];
+ }
+ static last(array: T[]): T {
+ if (!array || array.length == 0) return null;
+ return array[array.length - 1];
+ }
+ static indexOf(array: T[], value: T, startIndex: number = 0): number {
+ return array.indexOf(value, startIndex);
+ }
+ static contains(list: T[], el: T): boolean { return list.indexOf(el) !== -1; }
+ static reversed(array: T[]): T[] {
+ var a = ListWrapper.clone(array);
+ return a.reverse();
+ }
+ static concat(a: any[], b: any[]): any[] { return a.concat(b); }
+ static insert(list: T[], index: number, value: T) { list.splice(index, 0, value); }
+ static removeAt(list: T[], index: number): T {
+ var res = list[index];
+ list.splice(index, 1);
+ return res;
+ }
+ static removeAll(list: T[], items: T[]) {
+ for (var i = 0; i < items.length; ++i) {
+ var index = list.indexOf(items[i]);
+ list.splice(index, 1);
+ }
+ }
+ static remove(list: T[], el: T): boolean {
+ var index = list.indexOf(el);
+ if (index > -1) {
+ list.splice(index, 1);
+ return true;
+ }
+ return false;
+ }
+ static clear(list: any[]) { list.length = 0; }
+ static isEmpty(list: any[]): boolean { return list.length == 0; }
+ static fill(list: any[], value: any, start: number = 0, end: number = null) {
+ list.fill(value, start, end === null ? list.length : end);
+ }
+ static equals(a: any[], b: any[]): boolean {
+ if (a.length != b.length) return false;
+ for (var i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+ }
+ static slice(l: T[], from: number = 0, to: number = null): T[] {
+ return l.slice(from, to === null ? undefined : to);
+ }
+ static splice(l: T[], from: number, length: number): T[] { return l.splice(from, length); }
+ static sort(l: T[], compareFn?: (a: T, b: T) => number) {
+ if (isPresent(compareFn)) {
+ l.sort(compareFn);
+ } else {
+ l.sort();
+ }
+ }
+ static toString(l: T[]): string { return l.toString(); }
+ static toJSON(l: T[]): string { return JSON.stringify(l); }
+
+ static maximum(list: T[], predicate: (t: T) => number): T {
+ if (list.length == 0) {
+ return null;
+ }
+ var solution: any /** TODO #???? */ = null;
+ var maxValue = -Infinity;
+ for (var index = 0; index < list.length; index++) {
+ var candidate = list[index];
+ if (isBlank(candidate)) {
+ continue;
+ }
+ var candidateValue = predicate(candidate);
+ if (candidateValue > maxValue) {
+ solution = candidate;
+ maxValue = candidateValue;
+ }
+ }
+ return solution;
+ }
+
+ static flatten(list: Array): T[] {
+ var target: any[] /** TODO #???? */ = [];
+ _flattenArray(list, target);
+ return target;
+ }
+
+ static addAll(list: Array, source: Array): void {
+ for (var i = 0; i < source.length; i++) {
+ list.push(source[i]);
+ }
+ }
+}
+
+function _flattenArray(source: any[], target: any[]): any[] {
+ if (isPresent(source)) {
+ for (var i = 0; i < source.length; i++) {
+ var item = source[i];
+ if (isArray(item)) {
+ _flattenArray(item, target);
+ } else {
+ target.push(item);
+ }
+ }
+ }
+ return target;
+}
+
+
+export function isListLikeIterable(obj: any): boolean {
+ if (!isJsObject(obj)) return false;
+ return isArray(obj) ||
+ (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
+ getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
+}
+
+export function areIterablesEqual(a: any, b: any, comparator: Function): boolean {
+ var iterator1 = a[getSymbolIterator()]();
+ var iterator2 = b[getSymbolIterator()]();
+
+ while (true) {
+ let item1 = iterator1.next();
+ let item2 = iterator2.next();
+ if (item1.done && item2.done) return true;
+ if (item1.done || item2.done) return false;
+ if (!comparator(item1.value, item2.value)) return false;
+ }
+}
+
+export function iterateListLike(obj: any, fn: Function) {
+ if (isArray(obj)) {
+ for (var i = 0; i < obj.length; i++) {
+ fn(obj[i]);
+ }
+ } else {
+ var iterator = obj[getSymbolIterator()]();
+ var item: any /** TODO #???? */;
+ while (!((item = iterator.next()).done)) {
+ fn(item.value);
+ }
+ }
+}
+
+// Safari and Internet Explorer do not support the iterable parameter to the
+// Set constructor. We work around that by manually adding the items.
+var createSetFromList: {(lst: any[]): Set} = (function() {
+ var test = new Set([1, 2, 3]);
+ if (test.size === 3) {
+ return function createSetFromList(lst: any[]): Set { return new Set(lst); };
+ } else {
+ return function createSetAndPopulateFromList(lst: any[]): Set {
+ var res = new Set(lst);
+ if (res.size !== lst.length) {
+ for (var i = 0; i < lst.length; i++) {
+ res.add(lst[i]);
+ }
+ }
+ return res;
+ };
+ }
+})();
+export class SetWrapper {
+ static createFromList(lst: T[]): Set { return createSetFromList(lst); }
+ static has(s: Set, key: T): boolean { return s.has(key); }
+ static delete(m: Set, k: K) { m.delete(k); }
+}
diff --git a/modules/@angular/forms/src/facade/exception_handler.ts b/modules/@angular/forms/src/facade/exception_handler.ts
new file mode 100644
index 0000000000..86d2bf4e53
--- /dev/null
+++ b/modules/@angular/forms/src/facade/exception_handler.ts
@@ -0,0 +1,133 @@
+import {BaseWrappedException} from './base_wrapped_exception';
+import {isListLikeIterable} from './collection';
+import {isBlank, isPresent} from './lang';
+
+class _ArrayLogger {
+ res: any[] = [];
+ log(s: any): void { this.res.push(s); }
+ logError(s: any): void { this.res.push(s); }
+ logGroup(s: any): void { this.res.push(s); }
+ logGroupEnd(){};
+}
+
+/**
+ * Provides a hook for centralized exception handling.
+ *
+ * The default implementation of `ExceptionHandler` prints error messages to the `Console`. To
+ * intercept error handling,
+ * write a custom exception handler that replaces this default as appropriate for your app.
+ *
+ * ### Example
+ *
+ * ```javascript
+ *
+ * class MyExceptionHandler implements ExceptionHandler {
+ * call(error, stackTrace = null, reason = null) {
+ * // do something with the exception
+ * }
+ * }
+ *
+ * bootstrap(MyApp, {provide: ExceptionHandler, useClass: MyExceptionHandler}])
+ *
+ * ```
+ * @stable
+ */
+export class ExceptionHandler {
+ constructor(private _logger: any, private _rethrowException: boolean = true) {}
+
+ static exceptionToString(exception: any, stackTrace: any = null, reason: string = null): string {
+ var l = new _ArrayLogger();
+ var e = new ExceptionHandler(l, false);
+ e.call(exception, stackTrace, reason);
+ return l.res.join('\n');
+ }
+
+ call(exception: any, stackTrace: any = null, reason: string = null): void {
+ var originalException = this._findOriginalException(exception);
+ var originalStack = this._findOriginalStack(exception);
+ var context = this._findContext(exception);
+
+ this._logger.logGroup(`EXCEPTION: ${this._extractMessage(exception)}`);
+
+ if (isPresent(stackTrace) && isBlank(originalStack)) {
+ this._logger.logError('STACKTRACE:');
+ this._logger.logError(this._longStackTrace(stackTrace));
+ }
+
+ if (isPresent(reason)) {
+ this._logger.logError(`REASON: ${reason}`);
+ }
+
+ if (isPresent(originalException)) {
+ this._logger.logError(`ORIGINAL EXCEPTION: ${this._extractMessage(originalException)}`);
+ }
+
+ if (isPresent(originalStack)) {
+ this._logger.logError('ORIGINAL STACKTRACE:');
+ this._logger.logError(this._longStackTrace(originalStack));
+ }
+
+ if (isPresent(context)) {
+ this._logger.logError('ERROR CONTEXT:');
+ this._logger.logError(context);
+ }
+
+ this._logger.logGroupEnd();
+
+ // We rethrow exceptions, so operations like 'bootstrap' will result in an error
+ // when an exception happens. If we do not rethrow, bootstrap will always succeed.
+ if (this._rethrowException) throw exception;
+ }
+
+ /** @internal */
+ _extractMessage(exception: any): string {
+ return exception instanceof BaseWrappedException ? exception.wrapperMessage :
+ exception.toString();
+ }
+
+ /** @internal */
+ _longStackTrace(stackTrace: any): any {
+ return isListLikeIterable(stackTrace) ? (stackTrace).join('\n\n-----async gap-----\n') :
+ stackTrace.toString();
+ }
+
+ /** @internal */
+ _findContext(exception: any): any {
+ try {
+ if (!(exception instanceof BaseWrappedException)) return null;
+ return isPresent(exception.context) ? exception.context :
+ this._findContext(exception.originalException);
+ } catch (e) {
+ // exception.context can throw an exception. if it happens, we ignore the context.
+ return null;
+ }
+ }
+
+ /** @internal */
+ _findOriginalException(exception: any): any {
+ if (!(exception instanceof BaseWrappedException)) return null;
+
+ var e = exception.originalException;
+ while (e instanceof BaseWrappedException && isPresent(e.originalException)) {
+ e = e.originalException;
+ }
+
+ return e;
+ }
+
+ /** @internal */
+ _findOriginalStack(exception: any): any {
+ if (!(exception instanceof BaseWrappedException)) return null;
+
+ var e = exception;
+ var stack = exception.originalStack;
+ while (e instanceof BaseWrappedException && isPresent(e.originalException)) {
+ e = e.originalException;
+ if (e instanceof BaseWrappedException && isPresent(e.originalException)) {
+ stack = e.originalStack;
+ }
+ }
+
+ return stack;
+ }
+}
diff --git a/modules/@angular/forms/src/facade/exceptions.dart b/modules/@angular/forms/src/facade/exceptions.dart
new file mode 100644
index 0000000000..bc16f3ca22
--- /dev/null
+++ b/modules/@angular/forms/src/facade/exceptions.dart
@@ -0,0 +1,50 @@
+library angular.core.facade.exceptions;
+
+import 'base_wrapped_exception.dart';
+import 'exception_handler.dart';
+export 'exception_handler.dart';
+
+class BaseException extends Error {
+ final String _message;
+
+ BaseException([this._message]);
+
+ String get message => _message;
+
+ String toString() {
+ return this.message;
+ }
+}
+
+class WrappedException extends BaseWrappedException {
+ final dynamic _context;
+ final String _wrapperMessage;
+ final originalException;
+ final originalStack;
+
+ WrappedException(
+ [this._wrapperMessage,
+ this.originalException,
+ this.originalStack,
+ this._context]);
+
+ String get message {
+ return ExceptionHandler.exceptionToString(this);
+ }
+
+ String toString() {
+ return this.message;
+ }
+
+ dynamic get context => _context;
+
+ String get wrapperMessage => _wrapperMessage;
+}
+
+Error makeTypeError([String message = ""]) {
+ return new BaseException(message);
+}
+
+dynamic unimplemented() {
+ throw new BaseException('unimplemented');
+}
diff --git a/modules/@angular/forms/src/facade/exceptions.ts b/modules/@angular/forms/src/facade/exceptions.ts
new file mode 100644
index 0000000000..abd6c9c537
--- /dev/null
+++ b/modules/@angular/forms/src/facade/exceptions.ts
@@ -0,0 +1,56 @@
+import {BaseWrappedException} from './base_wrapped_exception';
+import {ExceptionHandler} from './exception_handler';
+
+export {ExceptionHandler} from './exception_handler';
+
+/**
+ * @stable
+ */
+export class BaseException extends Error {
+ public stack: any;
+ constructor(public message: string = '--') {
+ super(message);
+ this.stack = (new Error(message)).stack;
+ }
+
+ toString(): string { return this.message; }
+}
+
+/**
+ * Wraps an exception and provides additional context or information.
+ * @stable
+ */
+export class WrappedException extends BaseWrappedException {
+ private _wrapperStack: any;
+
+ constructor(
+ private _wrapperMessage: string, private _originalException: any /** TODO #9100 */,
+ private _originalStack?: any /** TODO #9100 */, private _context?: any /** TODO #9100 */) {
+ super(_wrapperMessage);
+ this._wrapperStack = (new Error(_wrapperMessage)).stack;
+ }
+
+ get wrapperMessage(): string { return this._wrapperMessage; }
+
+ get wrapperStack(): any { return this._wrapperStack; }
+
+
+ get originalException(): any { return this._originalException; }
+
+ get originalStack(): any { return this._originalStack; }
+
+
+ get context(): any { return this._context; }
+
+ get message(): string { return ExceptionHandler.exceptionToString(this); }
+
+ toString(): string { return this.message; }
+}
+
+export function makeTypeError(message?: string): Error {
+ return new TypeError(message);
+}
+
+export function unimplemented(): any {
+ throw new BaseException('unimplemented');
+}
diff --git a/modules/@angular/forms/src/facade/intl.dart b/modules/@angular/forms/src/facade/intl.dart
new file mode 100644
index 0000000000..c94b66be5b
--- /dev/null
+++ b/modules/@angular/forms/src/facade/intl.dart
@@ -0,0 +1,57 @@
+library facade.intl;
+
+import 'package:intl/intl.dart';
+
+String _normalizeLocale(String locale) => locale.replaceAll('-', '_');
+
+enum NumberFormatStyle { Decimal, Percent, Currency }
+
+class NumberFormatter {
+ static String format(num number, String locale, NumberFormatStyle style,
+ {int minimumIntegerDigits: 1,
+ int minimumFractionDigits: 0,
+ int maximumFractionDigits: 3,
+ String currency,
+ bool currencyAsSymbol: false}) {
+ locale = _normalizeLocale(locale);
+ NumberFormat formatter;
+ switch (style) {
+ case NumberFormatStyle.Decimal:
+ formatter = new NumberFormat.decimalPattern(locale);
+ break;
+ case NumberFormatStyle.Percent:
+ formatter = new NumberFormat.percentPattern(locale);
+ break;
+ case NumberFormatStyle.Currency:
+ if (currencyAsSymbol) {
+ // See https://github.com/dart-lang/intl/issues/59.
+ throw new Exception(
+ 'Displaying currency as symbol is not supported.');
+ }
+ formatter = new NumberFormat.currencyPattern(locale, currency);
+ break;
+ }
+ formatter.minimumIntegerDigits = minimumIntegerDigits;
+ formatter.minimumFractionDigits = minimumFractionDigits;
+ formatter.maximumFractionDigits = maximumFractionDigits;
+ return formatter.format(number);
+ }
+}
+
+class DateFormatter {
+ static RegExp _multiPartRegExp = new RegExp(r'^([yMdE]+)([Hjms]+)$');
+
+ static String format(DateTime date, String locale, String pattern) {
+ locale = _normalizeLocale(locale);
+ var formatter = new DateFormat(null, locale);
+ var matches = _multiPartRegExp.firstMatch(pattern);
+ if (matches != null) {
+ // Support for patterns which have known date and time components.
+ formatter.addPattern(matches[1]);
+ formatter.addPattern(matches[2], ', ');
+ } else {
+ formatter.addPattern(pattern);
+ }
+ return formatter.format(date);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/intl.ts b/modules/@angular/forms/src/facade/intl.ts
new file mode 100644
index 0000000000..1a92f134e7
--- /dev/null
+++ b/modules/@angular/forms/src/facade/intl.ts
@@ -0,0 +1,207 @@
+export enum NumberFormatStyle {
+ Decimal,
+ Percent,
+ Currency
+}
+
+export class NumberFormatter {
+ static format(
+ num: number, locale: string, style: NumberFormatStyle,
+ {minimumIntegerDigits = 1, minimumFractionDigits = 0, maximumFractionDigits = 3, currency,
+ currencyAsSymbol = false}: {
+ minimumIntegerDigits?: number,
+ minimumFractionDigits?: number,
+ maximumFractionDigits?: number,
+ currency?: string,
+ currencyAsSymbol?: boolean
+ } = {}): string {
+ var intlOptions: Intl.NumberFormatOptions = {
+ minimumIntegerDigits: minimumIntegerDigits,
+ minimumFractionDigits: minimumFractionDigits,
+ maximumFractionDigits: maximumFractionDigits
+ };
+ intlOptions.style = NumberFormatStyle[style].toLowerCase();
+ if (style == NumberFormatStyle.Currency) {
+ intlOptions.currency = currency;
+ intlOptions.currencyDisplay = currencyAsSymbol ? 'symbol' : 'code';
+ }
+ return new Intl.NumberFormat(locale, intlOptions).format(num);
+ }
+}
+var DATE_FORMATS_SPLIT =
+ /((?:[^yMLdHhmsaZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|Z|G+|w+))(.*)/;
+
+var PATTERN_ALIASES = {
+ yMMMdjms: datePartGetterFactory(combine([
+ digitCondition('year', 1),
+ nameCondition('month', 3),
+ digitCondition('day', 1),
+ digitCondition('hour', 1),
+ digitCondition('minute', 1),
+ digitCondition('second', 1),
+ ])),
+ yMdjm: datePartGetterFactory(combine([
+ digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1),
+ digitCondition('hour', 1), digitCondition('minute', 1)
+ ])),
+ yMMMMEEEEd: datePartGetterFactory(combine([
+ digitCondition('year', 1), nameCondition('month', 4), nameCondition('weekday', 4),
+ digitCondition('day', 1)
+ ])),
+ yMMMMd: datePartGetterFactory(
+ combine([digitCondition('year', 1), nameCondition('month', 4), digitCondition('day', 1)])),
+ yMMMd: datePartGetterFactory(
+ combine([digitCondition('year', 1), nameCondition('month', 3), digitCondition('day', 1)])),
+ yMd: datePartGetterFactory(
+ combine([digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1)])),
+ jms: datePartGetterFactory(combine(
+ [digitCondition('hour', 1), digitCondition('second', 1), digitCondition('minute', 1)])),
+ jm: datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)]))
+};
+
+var DATE_FORMATS = {
+ yyyy: datePartGetterFactory(digitCondition('year', 4)),
+ yy: datePartGetterFactory(digitCondition('year', 2)),
+ y: datePartGetterFactory(digitCondition('year', 1)),
+ MMMM: datePartGetterFactory(nameCondition('month', 4)),
+ MMM: datePartGetterFactory(nameCondition('month', 3)),
+ MM: datePartGetterFactory(digitCondition('month', 2)),
+ M: datePartGetterFactory(digitCondition('month', 1)),
+ LLLL: datePartGetterFactory(nameCondition('month', 4)),
+ dd: datePartGetterFactory(digitCondition('day', 2)),
+ d: datePartGetterFactory(digitCondition('day', 1)),
+ HH: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false))),
+ H: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
+ hh: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true))),
+ h: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
+ jj: datePartGetterFactory(digitCondition('hour', 2)),
+ j: datePartGetterFactory(digitCondition('hour', 1)),
+ mm: datePartGetterFactory(digitCondition('minute', 2)),
+ m: datePartGetterFactory(digitCondition('minute', 1)),
+ ss: datePartGetterFactory(digitCondition('second', 2)),
+ s: datePartGetterFactory(digitCondition('second', 1)),
+ // while ISO 8601 requires fractions to be prefixed with `.` or `,`
+ // we can be just safely rely on using `sss` since we currently don't support single or two digit
+ // fractions
+ sss: datePartGetterFactory(digitCondition('second', 3)),
+ EEEE: datePartGetterFactory(nameCondition('weekday', 4)),
+ EEE: datePartGetterFactory(nameCondition('weekday', 3)),
+ EE: datePartGetterFactory(nameCondition('weekday', 2)),
+ E: datePartGetterFactory(nameCondition('weekday', 1)),
+ a: hourClockExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
+ Z: datePartGetterFactory({timeZoneName: 'long'}),
+ z: datePartGetterFactory({timeZoneName: 'short'}),
+ ww: datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
+ // first Thursday of the year. not support ?
+ w: datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
+ // of the year not support ?
+ G: datePartGetterFactory(nameCondition('era', 1)),
+ GG: datePartGetterFactory(nameCondition('era', 2)),
+ GGG: datePartGetterFactory(nameCondition('era', 3)),
+ GGGG: datePartGetterFactory(nameCondition('era', 4))
+};
+
+
+function hourClockExtracter(inner: (date: Date, locale: string) => string): (
+ date: Date, locale: string) => string {
+ return function(date: Date, locale: string): string {
+ var result = inner(date, locale);
+
+ return result.split(' ')[1];
+ };
+}
+
+function hourExtracter(inner: (date: Date, locale: string) => string): (
+ date: Date, locale: string) => string {
+ return function(date: Date, locale: string): string {
+ var result = inner(date, locale);
+
+ return result.split(' ')[0];
+ };
+}
+
+function hour12Modify(
+ options: Intl.DateTimeFormatOptions, value: boolean): Intl.DateTimeFormatOptions {
+ options.hour12 = value;
+ return options;
+}
+
+function digitCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
+ var result = {};
+ (result as any /** TODO #9100 */)[prop] = len == 2 ? '2-digit' : 'numeric';
+ return result;
+}
+function nameCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
+ var result = {};
+ (result as any /** TODO #9100 */)[prop] = len < 4 ? 'short' : 'long';
+ return result;
+}
+
+function combine(options: Intl.DateTimeFormatOptions[]): Intl.DateTimeFormatOptions {
+ var result = {};
+
+ options.forEach(option => { (Object).assign(result, option); });
+
+ return result;
+}
+
+function datePartGetterFactory(ret: Intl.DateTimeFormatOptions): (date: Date, locale: string) =>
+ string {
+ return function(date: Date, locale: string): string {
+ return new Intl.DateTimeFormat(locale, ret).format(date);
+ };
+}
+
+
+var datePartsFormatterCache: Map = new Map();
+
+function dateFormatter(format: string, date: Date, locale: string): string {
+ var text = '';
+ var match: any /** TODO #9100 */;
+ var fn: any /** TODO #9100 */;
+ var parts: string[] = [];
+ if ((PATTERN_ALIASES as any /** TODO #9100 */)[format]) {
+ return (PATTERN_ALIASES as any /** TODO #9100 */)[format](date, locale);
+ }
+
+
+ if (datePartsFormatterCache.has(format)) {
+ parts = datePartsFormatterCache.get(format);
+ } else {
+ var matchs = DATE_FORMATS_SPLIT.exec(format);
+
+ while (format) {
+ match = DATE_FORMATS_SPLIT.exec(format);
+ if (match) {
+ parts = concat(parts, match, 1);
+ format = parts.pop();
+ } else {
+ parts.push(format);
+ format = null;
+ }
+ }
+
+ datePartsFormatterCache.set(format, parts);
+ }
+
+ parts.forEach(part => {
+ fn = (DATE_FORMATS as any /** TODO #9100 */)[part];
+ text += fn ? fn(date, locale) :
+ part === '\'\'' ? '\'' : part.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
+ });
+
+ return text;
+}
+
+var slice = [].slice;
+function concat(
+ array1: any /** TODO #9100 */, array2: any /** TODO #9100 */,
+ index: any /** TODO #9100 */): string[] {
+ return array1.concat(slice.call(array2, index));
+}
+
+export class DateFormatter {
+ static format(date: Date, locale: string, pattern: string): string {
+ return dateFormatter(pattern, date, locale);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/lang.dart b/modules/@angular/forms/src/facade/lang.dart
new file mode 100644
index 0000000000..eb788121af
--- /dev/null
+++ b/modules/@angular/forms/src/facade/lang.dart
@@ -0,0 +1,402 @@
+library angular.core.facade.lang;
+
+export 'dart:core' show Type, RegExp, print, DateTime, Uri;
+import 'dart:math' as math;
+import 'dart:convert' as convert;
+import 'dart:async' show Future, Zone;
+
+String getTypeNameForDebugging(Object type) => type.toString();
+
+class Math {
+ static final _random = new math.Random();
+ static int floor(num n) => n.floor();
+ static double random() => _random.nextDouble();
+ static num min(num a, num b) => math.min(a, b);
+}
+
+const IS_DART = true;
+
+scheduleMicroTask(Function fn) {
+ Zone.current.scheduleMicrotask(fn);
+}
+
+bool isPresent(Object obj) => obj != null;
+bool isBlank(Object obj) => obj == null;
+bool isString(Object obj) => obj is String;
+bool isFunction(Object obj) => obj is Function;
+bool isType(Object obj) => obj is Type;
+bool isStringMap(Object obj) => obj is Map;
+bool isStrictStringMap(Object obj) => obj is Map;
+bool isArray(Object obj) => obj is List;
+bool isPromise(Object obj) => obj is Future;
+bool isNumber(Object obj) => obj is num;
+bool isBoolean(Object obj) => obj is bool;
+bool isDate(Object obj) => obj is DateTime;
+
+String stringify(obj) {
+ final exp = new RegExp(r"from Function '(\w+)'");
+ final str = obj.toString();
+ if (exp.firstMatch(str) != null) {
+ return exp.firstMatch(str).group(1);
+ } else {
+ return str;
+ }
+}
+
+int serializeEnum(val) {
+ return val.index;
+}
+
+/**
+ * Deserializes an enum
+ * val should be the indexed value of the enum (sa returned from @Link{serializeEnum})
+ * values should be a map from indexes to values for the enum that you want to deserialize.
+ */
+dynamic deserializeEnum(num val, Map values) {
+ return values[val];
+}
+
+String resolveEnumToken(enumValue, val) {
+ // turn Enum.Token -> Token
+ return val.toString().replaceFirst(new RegExp('^.+\\.'),'');
+}
+
+class StringWrapper {
+ static String fromCharCode(int code) {
+ return new String.fromCharCode(code);
+ }
+
+ static int charCodeAt(String s, int index) {
+ return s.codeUnitAt(index);
+ }
+
+ static List split(String s, RegExp regExp) {
+ var parts = [];
+ var lastEnd = 0;
+ regExp.allMatches(s).forEach((match) {
+ parts.add(s.substring(lastEnd, match.start));
+ lastEnd = match.end;
+ for (var i = 0; i < match.groupCount; i++) {
+ parts.add(match.group(i + 1));
+ }
+ });
+ parts.add(s.substring(lastEnd));
+ return parts;
+ }
+
+ static bool equals(String s, String s2) {
+ return s == s2;
+ }
+
+ static String stripLeft(String s, String charVal) {
+ if (isPresent(s) && s.length > 0) {
+ var pos = 0;
+ for (var i = 0; i < s.length; i++) {
+ if (s[i] != charVal) break;
+ pos++;
+ }
+ s = s.substring(pos);
+ }
+ return s;
+ }
+
+ static String stripRight(String s, String charVal) {
+ if (isPresent(s) && s.length > 0) {
+ var pos = s.length;
+ for (var i = s.length - 1; i >= 0; i--) {
+ if (s[i] != charVal) break;
+ pos--;
+ }
+ s = s.substring(0, pos);
+ }
+ return s;
+ }
+
+ static String replace(String s, Pattern from, String replace) {
+ return s.replaceFirst(from, replace);
+ }
+
+ static String replaceAll(String s, RegExp from, String replace) {
+ return s.replaceAll(from, replace);
+ }
+
+ static String slice(String s, [int start = 0, int end]) {
+ start = _startOffset(s, start);
+ end = _endOffset(s, end);
+ //in JS if start > end an empty string is returned
+ if (end != null && start > end) {
+ return "";
+ }
+ return s.substring(start, end);
+ }
+
+ static String replaceAllMapped(String s, RegExp from, Function cb) {
+ return s.replaceAllMapped(from, cb);
+ }
+
+ static bool contains(String s, String substr) {
+ return s.contains(substr);
+ }
+
+ static int compare(String a, String b) => a.compareTo(b);
+
+ // JS slice function can take start < 0 which indicates a position relative to
+ // the end of the string
+ static int _startOffset(String s, int start) {
+ int len = s.length;
+ return start < 0 ? math.max(len + start, 0) : math.min(start, len);
+ }
+
+ // JS slice function can take end < 0 which indicates a position relative to
+ // the end of the string
+ static int _endOffset(String s, int end) {
+ int len = s.length;
+ if (end == null) return len;
+ return end < 0 ? math.max(len + end, 0) : math.min(end, len);
+ }
+}
+
+class StringJoiner {
+ final List _parts = [];
+
+ void add(String part) {
+ _parts.add(part);
+ }
+
+ String toString() => _parts.join("");
+}
+
+class NumberWrapper {
+ static String toFixed(num n, int fractionDigits) {
+ return n.toStringAsFixed(fractionDigits);
+ }
+
+ static bool equal(num a, num b) {
+ return a == b;
+ }
+
+ static int parseIntAutoRadix(String text) {
+ return int.parse(text);
+ }
+
+ static int parseInt(String text, int radix) {
+ return int.parse(text, radix: radix);
+ }
+
+ static double parseFloat(String text) {
+ return double.parse(text);
+ }
+
+ static double get NaN => double.NAN;
+
+ static bool isNaN(num value) => value.isNaN;
+
+ static bool isInteger(value) => value is int;
+}
+
+class RegExpWrapper {
+ static RegExp create(regExpStr, [String flags = '']) {
+ bool multiLine = flags.contains('m');
+ bool caseSensitive = !flags.contains('i');
+ return new RegExp(regExpStr,
+ multiLine: multiLine, caseSensitive: caseSensitive);
+ }
+
+ static Match firstMatch(RegExp regExp, String input) {
+ return regExp.firstMatch(input);
+ }
+
+ static bool test(RegExp regExp, String input) {
+ return regExp.hasMatch(input);
+ }
+
+ static Iterator matcher(RegExp regExp, String input) {
+ return regExp.allMatches(input).iterator;
+ }
+
+ static String replaceAll(RegExp regExp, String input, Function replace) {
+ final m = RegExpWrapper.matcher(regExp, input);
+ var res = "";
+ var prev = 0;
+ while(m.moveNext()) {
+ var c = m.current;
+ res += input.substring(prev, c.start);
+ res += replace(c);
+ prev = c.start + c[0].length;
+ }
+ res += input.substring(prev);
+ return res;
+ }
+}
+
+class RegExpMatcherWrapper {
+ static _JSLikeMatch next(Iterator matcher) {
+ if (matcher.moveNext()) {
+ return new _JSLikeMatch(matcher.current);
+ }
+ return null;
+ }
+}
+
+class _JSLikeMatch {
+ Match _m;
+
+ _JSLikeMatch(this._m);
+
+ String operator [](index) => _m[index];
+ int get index => _m.start;
+ int get length => _m.groupCount + 1;
+}
+
+class FunctionWrapper {
+ static apply(Function fn, posArgs) {
+ return Function.apply(fn, posArgs);
+ }
+
+ static Function bind(Function fn, dynamic scope) {
+ return fn;
+ }
+}
+
+const _NAN_KEY = const Object();
+
+// Dart VM implements `identical` as true reference identity. JavaScript does
+// not have this. The closest we have in JS is `===`. However, for strings JS
+// would actually compare the contents rather than references. `dart2js`
+// compiles `identical` to `===` and therefore there is a discrepancy between
+// Dart VM and `dart2js`. The implementation of `looseIdentical` attempts to
+// bridge the gap between the two while retaining good performance
+// characteristics. In JS we use simple `identical`, which compiles to `===`,
+// and in Dart VM we emulate the semantics of `===` by special-casing strings.
+// Note that the VM check is a compile-time constant. This allows `dart2js` to
+// evaluate the conditional during compilation and inline the entire function.
+//
+// See: dartbug.com/22496, dartbug.com/25270
+const _IS_DART_VM = !identical(1.0, 1); // a hack
+bool looseIdentical(a, b) => _IS_DART_VM
+ ? _looseIdentical(a, b)
+ : identical(a, b);
+
+// This function is intentionally separated from `looseIdentical` to keep the
+// number of AST nodes low enough for `dart2js` to inline the code.
+bool _looseIdentical(a, b) =>
+ a is String && b is String ? a == b : identical(a, b);
+
+// Dart compare map keys by equality and we can have NaN != NaN
+dynamic getMapKey(value) {
+ if (value is! num) return value;
+ return value.isNaN ? _NAN_KEY : value;
+}
+
+// TODO: remove with https://github.com/angular/angular/issues/3055
+dynamic normalizeBlank(obj) => obj;
+
+bool normalizeBool(bool obj) {
+ return isBlank(obj) ? false : obj;
+}
+
+bool isJsObject(o) {
+ return false;
+}
+
+warn(o) {
+ print(o);
+}
+
+// Functions below are noop in Dart. Imperatively controlling dev mode kills
+// tree shaking. We should only rely on `assertionsEnabled`.
+@Deprecated('Do not use this function. It is for JS only. There is no alternative.')
+void lockMode() {}
+@Deprecated('Do not use this function. It is for JS only. There is no alternative.')
+void enableDevMode() {}
+@Deprecated('Do not use this function. It is for JS only. There is no alternative.')
+void enableProdMode() {}
+
+/// Use this function to guard debugging code. When Dart is compiled in
+/// production mode, the code guarded using this function will be tree
+/// shaken away, reducing code size.
+///
+/// WARNING: DO NOT CHANGE THIS METHOD! This method is designed to have no
+/// more AST nodes than the maximum allowed by dart2js to inline it. In
+/// addition, the use of `assert` allows the compiler to statically compute
+/// the value returned by this function and tree shake conditions guarded by
+/// it.
+///
+/// Example:
+///
+/// if (assertionsEnabled()) {
+/// ...code here is tree shaken away in prod mode...
+/// }
+bool assertionsEnabled() {
+ var k = false;
+ assert((k = true));
+ return k;
+}
+
+// Can't be all uppercase as our transpiler would think it is a special directive...
+class Json {
+ static parse(String s) => convert.JSON.decode(s);
+ static String stringify(data) {
+ var encoder = new convert.JsonEncoder.withIndent(" ");
+ return encoder.convert(data);
+ }
+}
+
+class DateWrapper {
+ static DateTime create(int year,
+ [int month = 1,
+ int day = 1,
+ int hour = 0,
+ int minutes = 0,
+ int seconds = 0,
+ int milliseconds = 0]) {
+ return new DateTime(year, month, day, hour, minutes, seconds, milliseconds);
+ }
+
+ static DateTime fromISOString(String str) {
+ return DateTime.parse(str);
+ }
+
+ static DateTime fromMillis(int ms) {
+ return new DateTime.fromMillisecondsSinceEpoch(ms, isUtc: true);
+ }
+
+ static int toMillis(DateTime date) {
+ return date.millisecondsSinceEpoch;
+ }
+
+ static DateTime now() {
+ return new DateTime.now();
+ }
+
+ static String toJson(DateTime date) {
+ return date.toUtc().toIso8601String();
+ }
+}
+
+bool isPrimitive(Object obj) => obj is num || obj is bool || obj == null || obj is String;
+
+// needed to match the exports from lang.js
+var global = null;
+
+dynamic evalExpression(String sourceUrl, String expr, String declarations, Map vars) {
+ throw "Dart does not support evaluating expression during runtime!";
+}
+
+bool hasConstructor(Object value, Type type) {
+ return value.runtimeType == type;
+}
+
+num bitWiseOr(List values) {
+ var val = values.reduce((num a, num b) => (a as int) | (b as int));
+ return val as num;
+}
+
+num bitWiseAnd(List values) {
+ var val = values.reduce((num a, num b) => (a as int) & (b as int));
+ return val as num;
+}
+
+String escape(String s) {
+ return Uri.encodeComponent(s);
+}
diff --git a/modules/@angular/forms/src/facade/lang.ts b/modules/@angular/forms/src/facade/lang.ts
new file mode 100644
index 0000000000..b93803ef3f
--- /dev/null
+++ b/modules/@angular/forms/src/facade/lang.ts
@@ -0,0 +1,502 @@
+export interface BrowserNodeGlobal {
+ Object: typeof Object;
+ Array: typeof Array;
+ Map: typeof Map;
+ Set: typeof Set;
+ Date: DateConstructor;
+ RegExp: RegExpConstructor;
+ JSON: typeof JSON;
+ Math: any; // typeof Math;
+ assert(condition: any): void;
+ Reflect: any;
+ getAngularTestability: Function;
+ getAllAngularTestabilities: Function;
+ getAllAngularRootElements: Function;
+ frameworkStabilizers: Array;
+ setTimeout: Function;
+ clearTimeout: Function;
+ setInterval: Function;
+ clearInterval: Function;
+ encodeURI: Function;
+}
+
+// TODO(jteplitz602): Load WorkerGlobalScope from lib.webworker.d.ts file #3492
+declare var WorkerGlobalScope: any /** TODO #9100 */;
+// CommonJS / Node have global context exposed as "global" variable.
+// We don't want to include the whole node.d.ts this this compilation unit so we'll just fake
+// the global "global" var for now.
+declare var global: any /** TODO #9100 */;
+
+var globalScope: BrowserNodeGlobal;
+if (typeof window === 'undefined') {
+ if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
+ // TODO: Replace any with WorkerGlobalScope from lib.webworker.d.ts #3492
+ globalScope = self;
+ } else {
+ globalScope = global;
+ }
+} else {
+ globalScope = window;
+}
+
+export function scheduleMicroTask(fn: Function) {
+ Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
+}
+
+export const IS_DART = false;
+
+// Need to declare a new variable for global here since TypeScript
+// exports the original value of the symbol.
+var _global: BrowserNodeGlobal = globalScope;
+
+export {_global as global};
+
+export var Type = Function;
+
+/**
+ * Runtime representation a type that a Component or other object is instances of.
+ *
+ * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is be represented by
+ * the `MyCustomComponent` constructor function.
+ */
+export interface Type extends Function {}
+
+/**
+ * Runtime representation of a type that is constructable (non-abstract).
+ */
+export interface ConcreteType extends Type { new (...args: any[] /** TODO #9100 */): any; }
+
+export function getTypeNameForDebugging(type: Type): string {
+ if (type['name']) {
+ return type['name'];
+ }
+ return typeof type;
+}
+
+
+export var Math = _global.Math;
+export var Date = _global.Date;
+
+var _devMode: boolean = true;
+var _modeLocked: boolean = false;
+
+export function lockMode() {
+ _modeLocked = true;
+}
+
+/**
+ * Disable Angular's development mode, which turns off assertions and other
+ * checks within the framework.
+ *
+ * One important assertion this disables verifies that a change detection pass
+ * does not result in additional changes to any bindings (also known as
+ * unidirectional data flow).
+ * @stable
+ */
+export function enableProdMode() {
+ if (_modeLocked) {
+ // Cannot use BaseException as that ends up importing from facade/lang.
+ throw 'Cannot enable prod mode after platform setup.';
+ }
+ _devMode = false;
+}
+
+export function assertionsEnabled(): boolean {
+ return _devMode;
+}
+
+// TODO: remove calls to assert in production environment
+// Note: Can't just export this and import in in other files
+// as `assert` is a reserved keyword in Dart
+_global.assert = function assert(condition) {
+ // TODO: to be fixed properly via #2830, noop for now
+};
+
+export function isPresent(obj: any): boolean {
+ return obj !== undefined && obj !== null;
+}
+
+export function isBlank(obj: any): boolean {
+ return obj === undefined || obj === null;
+}
+
+export function isBoolean(obj: any): boolean {
+ return typeof obj === 'boolean';
+}
+
+export function isNumber(obj: any): boolean {
+ return typeof obj === 'number';
+}
+
+export function isString(obj: any): obj is String {
+ return typeof obj === 'string';
+}
+
+export function isFunction(obj: any): boolean {
+ return typeof obj === 'function';
+}
+
+export function isType(obj: any): boolean {
+ return isFunction(obj);
+}
+
+export function isStringMap(obj: any): boolean {
+ return typeof obj === 'object' && obj !== null;
+}
+
+const STRING_MAP_PROTO = Object.getPrototypeOf({});
+export function isStrictStringMap(obj: any): boolean {
+ return isStringMap(obj) && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
+}
+
+export function isPromise(obj: any): boolean {
+ return obj instanceof (_global).Promise;
+}
+
+export function isArray(obj: any): boolean {
+ return Array.isArray(obj);
+}
+
+export function isDate(obj: any): obj is Date {
+ return obj instanceof Date && !isNaN(obj.valueOf());
+}
+
+export function noop() {}
+
+export function stringify(token: any): string {
+ if (typeof token === 'string') {
+ return token;
+ }
+
+ if (token === undefined || token === null) {
+ return '' + token;
+ }
+
+ if (token.name) {
+ return token.name;
+ }
+ if (token.overriddenName) {
+ return token.overriddenName;
+ }
+
+ var res = token.toString();
+ var newLineIndex = res.indexOf('\n');
+ return (newLineIndex === -1) ? res : res.substring(0, newLineIndex);
+}
+
+// serialize / deserialize enum exist only for consistency with dart API
+// enums in typescript don't need to be serialized
+
+export function serializeEnum(val: any): number {
+ return val;
+}
+
+export function deserializeEnum(val: any, values: Map): any {
+ return val;
+}
+
+export function resolveEnumToken(enumValue: any, val: any): string {
+ return enumValue[val];
+}
+
+export class StringWrapper {
+ static fromCharCode(code: number): string { return String.fromCharCode(code); }
+
+ static charCodeAt(s: string, index: number): number { return s.charCodeAt(index); }
+
+ static split(s: string, regExp: RegExp): string[] { return s.split(regExp); }
+
+ static equals(s: string, s2: string): boolean { return s === s2; }
+
+ static stripLeft(s: string, charVal: string): string {
+ if (s && s.length) {
+ var pos = 0;
+ for (var i = 0; i < s.length; i++) {
+ if (s[i] != charVal) break;
+ pos++;
+ }
+ s = s.substring(pos);
+ }
+ return s;
+ }
+
+ static stripRight(s: string, charVal: string): string {
+ if (s && s.length) {
+ var pos = s.length;
+ for (var i = s.length - 1; i >= 0; i--) {
+ if (s[i] != charVal) break;
+ pos--;
+ }
+ s = s.substring(0, pos);
+ }
+ return s;
+ }
+
+ static replace(s: string, from: string, replace: string): string {
+ return s.replace(from, replace);
+ }
+
+ static replaceAll(s: string, from: RegExp, replace: string): string {
+ return s.replace(from, replace);
+ }
+
+ static slice(s: string, from: number = 0, to: number = null): string {
+ return s.slice(from, to === null ? undefined : to);
+ }
+
+ static replaceAllMapped(s: string, from: RegExp, cb: Function): string {
+ return s.replace(from, function(...matches: any[]) {
+ // Remove offset & string from the result array
+ matches.splice(-2, 2);
+ // The callback receives match, p1, ..., pn
+ return cb(matches);
+ });
+ }
+
+ static contains(s: string, substr: string): boolean { return s.indexOf(substr) != -1; }
+
+ static compare(a: string, b: string): number {
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+export class StringJoiner {
+ constructor(public parts: string[] = []) {}
+
+ add(part: string): void { this.parts.push(part); }
+
+ toString(): string { return this.parts.join(''); }
+}
+
+export class NumberParseError extends Error {
+ name: string;
+
+ constructor(public message: string) { super(); }
+
+ toString(): string { return this.message; }
+}
+
+
+export class NumberWrapper {
+ static toFixed(n: number, fractionDigits: number): string { return n.toFixed(fractionDigits); }
+
+ static equal(a: number, b: number): boolean { return a === b; }
+
+ static parseIntAutoRadix(text: string): number {
+ var result: number = parseInt(text);
+ if (isNaN(result)) {
+ throw new NumberParseError('Invalid integer literal when parsing ' + text);
+ }
+ return result;
+ }
+
+ static parseInt(text: string, radix: number): number {
+ if (radix == 10) {
+ if (/^(\-|\+)?[0-9]+$/.test(text)) {
+ return parseInt(text, radix);
+ }
+ } else if (radix == 16) {
+ if (/^(\-|\+)?[0-9ABCDEFabcdef]+$/.test(text)) {
+ return parseInt(text, radix);
+ }
+ } else {
+ var result: number = parseInt(text, radix);
+ if (!isNaN(result)) {
+ return result;
+ }
+ }
+ throw new NumberParseError(
+ 'Invalid integer literal when parsing ' + text + ' in base ' + radix);
+ }
+
+ // TODO: NaN is a valid literal but is returned by parseFloat to indicate an error.
+ static parseFloat(text: string): number { return parseFloat(text); }
+
+ static get NaN(): number { return NaN; }
+
+ static isNaN(value: any): boolean { return isNaN(value); }
+
+ static isInteger(value: any): boolean { return Number.isInteger(value); }
+}
+
+export var RegExp = _global.RegExp;
+
+export class RegExpWrapper {
+ static create(regExpStr: string, flags: string = ''): RegExp {
+ flags = flags.replace(/g/g, '');
+ return new _global.RegExp(regExpStr, flags + 'g');
+ }
+ static firstMatch(regExp: RegExp, input: string): RegExpExecArray {
+ // Reset multimatch regex state
+ regExp.lastIndex = 0;
+ return regExp.exec(input);
+ }
+ static test(regExp: RegExp, input: string): boolean {
+ regExp.lastIndex = 0;
+ return regExp.test(input);
+ }
+ static matcher(regExp: RegExp, input: string): {re: RegExp; input: string} {
+ // Reset regex state for the case
+ // someone did not loop over all matches
+ // last time.
+ regExp.lastIndex = 0;
+ return {re: regExp, input: input};
+ }
+ static replaceAll(regExp: RegExp, input: string, replace: Function): string {
+ let c = regExp.exec(input);
+ let res = '';
+ regExp.lastIndex = 0;
+ let prev = 0;
+ while (c) {
+ res += input.substring(prev, c.index);
+ res += replace(c);
+ prev = c.index + c[0].length;
+ regExp.lastIndex = prev;
+ c = regExp.exec(input);
+ }
+ res += input.substring(prev);
+ return res;
+ }
+}
+
+export class RegExpMatcherWrapper {
+ static next(matcher: {re: RegExp; input: string}): RegExpExecArray {
+ return matcher.re.exec(matcher.input);
+ }
+}
+
+export class FunctionWrapper {
+ static apply(fn: Function, posArgs: any): any { return fn.apply(null, posArgs); }
+
+ static bind(fn: Function, scope: any): Function { return fn.bind(scope); }
+}
+
+// JS has NaN !== NaN
+export function looseIdentical(a: any, b: any): boolean {
+ return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b);
+}
+
+// JS considers NaN is the same as NaN for map Key (while NaN !== NaN otherwise)
+// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
+export function getMapKey(value: T): T {
+ return value;
+}
+
+export function normalizeBlank(obj: Object): any {
+ return isBlank(obj) ? null : obj;
+}
+
+export function normalizeBool(obj: boolean): boolean {
+ return isBlank(obj) ? false : obj;
+}
+
+export function isJsObject(o: any): boolean {
+ return o !== null && (typeof o === 'function' || typeof o === 'object');
+}
+
+export function print(obj: Error | Object) {
+ console.log(obj);
+}
+
+export function warn(obj: Error | Object) {
+ console.warn(obj);
+}
+
+// Can't be all uppercase as our transpiler would think it is a special directive...
+export class Json {
+ static parse(s: string): Object { return _global.JSON.parse(s); }
+ static stringify(data: Object): string {
+ // Dart doesn't take 3 arguments
+ return _global.JSON.stringify(data, null, 2);
+ }
+}
+
+export class DateWrapper {
+ static create(
+ year: number, month: number = 1, day: number = 1, hour: number = 0, minutes: number = 0,
+ seconds: number = 0, milliseconds: number = 0): Date {
+ return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds);
+ }
+ static fromISOString(str: string): Date { return new Date(str); }
+ static fromMillis(ms: number): Date { return new Date(ms); }
+ static toMillis(date: Date): number { return date.getTime(); }
+ static now(): Date { return new Date(); }
+ static toJson(date: Date): string { return date.toJSON(); }
+}
+
+export function setValueOnPath(global: any, path: string, value: any) {
+ var parts = path.split('.');
+ var obj: any = global;
+ while (parts.length > 1) {
+ var name = parts.shift();
+ if (obj.hasOwnProperty(name) && isPresent(obj[name])) {
+ obj = obj[name];
+ } else {
+ obj = obj[name] = {};
+ }
+ }
+ if (obj === undefined || obj === null) {
+ obj = {};
+ }
+ obj[parts.shift()] = value;
+}
+
+// When Symbol.iterator doesn't exist, retrieves the key used in es6-shim
+declare var Symbol: any;
+var _symbolIterator: any = null;
+export function getSymbolIterator(): string|symbol {
+ if (isBlank(_symbolIterator)) {
+ if (isPresent((globalScope).Symbol) && isPresent(Symbol.iterator)) {
+ _symbolIterator = Symbol.iterator;
+ } else {
+ // es6-shim specific logic
+ var keys = Object.getOwnPropertyNames(Map.prototype);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (key !== 'entries' && key !== 'size' &&
+ (Map as any).prototype[key] === Map.prototype['entries']) {
+ _symbolIterator = key;
+ }
+ }
+ }
+ }
+ return _symbolIterator;
+}
+
+export function evalExpression(
+ sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any {
+ var fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
+ var fnArgNames: string[] = [];
+ var fnArgValues: any[] = [];
+ for (var argName in vars) {
+ fnArgNames.push(argName);
+ fnArgValues.push(vars[argName]);
+ }
+ return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
+}
+
+export function isPrimitive(obj: any): boolean {
+ return !isJsObject(obj);
+}
+
+export function hasConstructor(value: Object, type: Type): boolean {
+ return value.constructor === type;
+}
+
+export function bitWiseOr(values: number[]): number {
+ return values.reduce((a, b) => { return a | b; });
+}
+
+export function bitWiseAnd(values: number[]): number {
+ return values.reduce((a, b) => { return a & b; });
+}
+
+export function escape(s: string): string {
+ return _global.encodeURI(s);
+}
diff --git a/modules/@angular/forms/src/facade/math.dart b/modules/@angular/forms/src/facade/math.dart
new file mode 100644
index 0000000000..08adb680d3
--- /dev/null
+++ b/modules/@angular/forms/src/facade/math.dart
@@ -0,0 +1,24 @@
+library angular.core.facade.math;
+
+import 'dart:core' show double, num;
+import 'dart:math' as math;
+
+const NaN = double.NAN;
+
+class Math {
+ static num pow(num x, num exponent) {
+ return math.pow(x, exponent);
+ }
+
+ static num max(num a, num b) => math.max(a, b);
+
+ static num min(num a, num b) => math.min(a, b);
+
+ static num floor(num a) => a.floor();
+
+ static num ceil(num a) => a.ceil();
+
+ static num sqrt(num x) => math.sqrt(x);
+
+ static num round(num x) => x.round();
+}
diff --git a/modules/@angular/forms/src/facade/math.ts b/modules/@angular/forms/src/facade/math.ts
new file mode 100644
index 0000000000..7b191cf5c4
--- /dev/null
+++ b/modules/@angular/forms/src/facade/math.ts
@@ -0,0 +1,4 @@
+import {global} from './lang';
+
+export var Math = global.Math;
+export var NaN: any /** TODO #???? */ = typeof NaN;
diff --git a/modules/@angular/forms/src/facade/promise.dart b/modules/@angular/forms/src/facade/promise.dart
new file mode 100644
index 0000000000..eda21d483a
--- /dev/null
+++ b/modules/@angular/forms/src/facade/promise.dart
@@ -0,0 +1,58 @@
+library angular2.core.facade.promise;
+
+import 'dart:async';
+import 'dart:async' as async;
+
+class PromiseWrapper {
+ static Future/**/ resolve/**/(dynamic /*=T*/ obj) => new Future.value(obj);
+
+ static Future/**/ reject/**/(dynamic /*=T*/ obj, Object stackTrace) => new Future.error(obj,
+ stackTrace != null ? stackTrace : obj is Error ? (obj as Error).stackTrace : null);
+
+ static Future*/> all/**/(List promises) {
+ return Future
+ .wait(promises.map((p) => p is Future ? p as Future/**/ : new Future/**/.value(p)));
+ }
+ static Future/**/ then/**/(Future/**/ promise, dynamic /*=R*/ success(dynamic /*=T*/ value), [Function onError]) {
+ if (success == null) return promise.catchError(onError);
+ return promise.then(success, onError: onError);
+ }
+
+ static Future/**/ wrap/**/(dynamic /*=T*/ fn()) {
+ return new Future(fn);
+ }
+
+ // Note: We can't rename this method to `catch`, as this is not a valid
+ // method name in Dart.
+ static Future catchError(Future promise, Function onError) {
+ return promise.catchError(onError);
+ }
+
+ static void scheduleMicrotask(fn) {
+ async.scheduleMicrotask(fn);
+ }
+
+ static bool isPromise(obj) {
+ return obj is Future;
+ }
+
+ static PromiseCompleter/**/ completer/**/() =>
+ new PromiseCompleter();
+}
+
+class PromiseCompleter {
+ final Completer c = new Completer();
+
+ Future get promise => c.future;
+
+ void resolve(v) {
+ c.complete(v);
+ }
+
+ void reject(error, stack) {
+ if (stack == null && error is Error) {
+ stack = error.stackTrace;
+ }
+ c.completeError(error, stack);
+ }
+}
diff --git a/modules/@angular/forms/src/facade/promise.js b/modules/@angular/forms/src/facade/promise.js
new file mode 100644
index 0000000000..83930f30c2
--- /dev/null
+++ b/modules/@angular/forms/src/facade/promise.js
@@ -0,0 +1,49 @@
+"use strict";
+var PromiseCompleter = (function () {
+ function PromiseCompleter() {
+ var _this = this;
+ this.promise = new Promise(function (res, rej) {
+ _this.resolve = res;
+ _this.reject = rej;
+ });
+ }
+ return PromiseCompleter;
+}());
+exports.PromiseCompleter = PromiseCompleter;
+var PromiseWrapper = (function () {
+ function PromiseWrapper() {
+ }
+ PromiseWrapper.resolve = function (obj) { return Promise.resolve(obj); };
+ PromiseWrapper.reject = function (obj, _) { return Promise.reject(obj); };
+ // Note: We can't rename this method into `catch`, as this is not a valid
+ // method name in Dart.
+ PromiseWrapper.catchError = function (promise, onError) {
+ return promise.catch(onError);
+ };
+ PromiseWrapper.all = function (promises) {
+ if (promises.length == 0)
+ return Promise.resolve([]);
+ return Promise.all(promises);
+ };
+ PromiseWrapper.then = function (promise, success, rejection) {
+ return promise.then(success, rejection);
+ };
+ PromiseWrapper.wrap = function (computation) {
+ return new Promise(function (res, rej) {
+ try {
+ res(computation());
+ }
+ catch (e) {
+ rej(e);
+ }
+ });
+ };
+ PromiseWrapper.scheduleMicrotask = function (computation) {
+ PromiseWrapper.then(PromiseWrapper.resolve(null), computation, function (_) { });
+ };
+ PromiseWrapper.isPromise = function (obj) { return obj instanceof Promise; };
+ PromiseWrapper.completer = function () { return new PromiseCompleter(); };
+ return PromiseWrapper;
+}());
+exports.PromiseWrapper = PromiseWrapper;
+//# sourceMappingURL=promise.js.map
\ No newline at end of file
diff --git a/modules/@angular/forms/src/facade/promise.ts b/modules/@angular/forms/src/facade/promise.ts
new file mode 100644
index 0000000000..5747a209a9
--- /dev/null
+++ b/modules/@angular/forms/src/facade/promise.ts
@@ -0,0 +1,55 @@
+
+export class PromiseCompleter {
+ promise: Promise;
+ resolve: (value?: R|PromiseLike) => void;
+ reject: (error?: any, stackTrace?: string) => void;
+
+ constructor() {
+ this.promise = new Promise((res, rej) => {
+ this.resolve = res;
+ this.reject = rej;
+ });
+ }
+}
+
+export class PromiseWrapper {
+ static resolve(obj: T): Promise { return Promise.resolve(obj); }
+
+ static reject(obj: any, _: any): Promise { return Promise.reject(obj); }
+
+ // Note: We can't rename this method into `catch`, as this is not a valid
+ // method name in Dart.
+ static catchError(promise: Promise, onError: (error: any) => T | PromiseLike):
+ Promise {
+ return promise.catch(onError);
+ }
+
+ static all