Compare commits

..

2 Commits

396 changed files with 10548 additions and 7411 deletions

View File

@ -1,16 +1,38 @@
**IMPORTANT**: This repository's issues are reserved for feature requests and bug reports. Do not submit support requests here, see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question.
**Note: for support questions, please use one of these channels:** https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.
* **I'm submitting a ... **
[ ] bug report
[ ] feature request
[ ] support request => Please do not submit support request here, see note at the top of this template.
**Steps to reproduce and a minimal demo of the problem**
_Use https://plnkr.co or similar -- try this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5_
_What steps should we try in your demo to see the problem?_
**Current behavior**
* **Do you want to request a *feature* or report a *bug*?**
**Expected/desired behavior**
* **What is the current behavior?**
**Other information**
* **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
* **What is the expected behavior?**
* **What is the motivation / use case for changing the behavior?**
* **Please tell us about your environment:**
- Angular version: 2.0.0-beta.X
- Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
- Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart]
* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc)

View File

@ -1,71 +1,3 @@
<a name="2.0.0-beta.12"></a>
# 2.0.0-beta.12 (2016-03-23)
### Bug Fixes
* **angular_1_router:** ng-link is generating wrong hrefs ([69c1405](https://github.com/angular/angular/commit/69c1405)), closes [#7423](https://github.com/angular/angular/issues/7423)
* **angular1_router:** support link generation with custom hashPrefixes ([0f8efce](https://github.com/angular/angular/commit/0f8efce))
* **package.json:** remove es6-promise from the peerDependency list ([8b67b07](https://github.com/angular/angular/commit/8b67b07))
### Features
* **dart/transform:** Use angular2/platform/browser as bootstrap lib ([b6507e3](https://github.com/angular/angular/commit/b6507e3)), closes [#7647](https://github.com/angular/angular/issues/7647)
<a name="2.0.0-beta.11"></a>
# 2.0.0-beta.11 (2016-03-18)
### Bug Fixes
* make sure that Zone does not show up in angular2.d.ts ([d4e9b55](https://github.com/angular/angular/commit/d4e9b55fb69d87f948d02905d34fc78221adb11a))
* **common:** remove @internal annotation on SwitchView ([967ae3e](https://github.com/angular/angular/commit/967ae3e)), closes [#7657](https://github.com/angular/angular/issues/7657)
* **router:** RouterOutlet loads component twice in a race condition ([2f581ff](https://github.com/angular/angular/commit/2f581ff)), closes [#7497](https://github.com/angular/angular/issues/7497) [#7545](https://github.com/angular/angular/issues/7545)
### Features
* **i18n:** add a simple dart script extracting all i18n messages from a package ([8326ab3](https://github.com/angular/angular/commit/8326ab3)), closes [#7620](https://github.com/angular/angular/issues/7620)
* **i18n:** create i18n barrel ([a7fe983](https://github.com/angular/angular/commit/a7fe983))
* **i18n:** implement xmb serializer ([e1f8e54](https://github.com/angular/angular/commit/e1f8e54))
<a name="2.0.0-beta.10"></a>
# 2.0.0-beta.10 (2016-03-17)
### Bug Fixes
* **change_detection:** fix a memory leak ([128acbb](https://github.com/angular/angular/commit/128acbb))
* **closure:** don't throw from top-level ([5824866](https://github.com/angular/angular/commit/5824866))
* **router:** handle URL that does not match a route ([8e3e450](https://github.com/angular/angular/commit/8e3e450)), closes [#7349](https://github.com/angular/angular/issues/7349) [#7203](https://github.com/angular/angular/issues/7203)
* **router/instruction:** ensure toLinkUrl includes extra params ([0d58b13](https://github.com/angular/angular/commit/0d58b13)), closes [#7367](https://github.com/angular/angular/issues/7367)
### Features
* **compiler:** change html parser to preserve comments ([70d18b5](https://github.com/angular/angular/commit/70d18b5))
* **core:** introduce a CSS lexer/parser ([b72bab4](https://github.com/angular/angular/commit/b72bab4))
* **core:** introduce a CSS lexer/parser ([293fa55](https://github.com/angular/angular/commit/293fa55))
* **facade:** add .values to StringMapWrapper ([f1796d6](https://github.com/angular/angular/commit/f1796d6))
* **i18n:** add ngPlural directive ([df1f78e](https://github.com/angular/angular/commit/df1f78e))
* **i18n:** implement a simple version of message extractor ([095db67](https://github.com/angular/angular/commit/095db67)), closes [#7454](https://github.com/angular/angular/issues/7454)
* **shadow_css:** support `/deep/` and `>>>` ([cb38d72](https://github.com/angular/angular/commit/cb38d72)), closes [#7562](https://github.com/angular/angular/issues/7562) [#7563](https://github.com/angular/angular/issues/7563)
* **TAG_DEFINITIONS:** include <meta> and <base> ([2c7c3e3](https://github.com/angular/angular/commit/2c7c3e3)), closes [#7455](https://github.com/angular/angular/issues/7455)
### BREAKING CHANGES
Removed deprecated API from NgZone
- `NgZone.overrideOnTurnStart`
- `NgZone.overrideOnTurnDone`
- `NgZone.overrideOnEventDone`
- `NgZone.overrideOnErrorHandler`
Rename NgZone API
- `NgZone.onTurnStart` => `NgZone.onUnstable`
- `NgZone.onTurnDone` => `NgZone.onMicrotaskEmpty`
- `NgZone.onEventDone` => `NgZone.onStable`
<a name="2.0.0-beta.9"></a>
# 2.0.0-beta.9 (2016-03-09)

View File

@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
kinds of computation. Example:
```typescript
@Component({
@View({
template: '<button [enabled]="isEnabled">{{title}}</button>'
})
class FancyButton {

View File

@ -4,9 +4,7 @@
var CIconfiguration = {
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
// FirefoxBeta should be required:true
// https://github.com/angular/angular/issues/7560
'FirefoxBeta': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: false}},
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},

View File

@ -40,9 +40,9 @@ if (cliArgs.projects) {
cliArgs.projects.split(',').sort().join(',');
}
// --projects=angular2 => {angular2: true}
// --projects=angular2,angular2_material => {angular2: true, angular2_material: true}
var allProjects =
'angular1_router,angular2,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
var cliArgsProjects = (cliArgs.projects || allProjects)
.split(',')
.reduce((map, projectName) => {
@ -57,7 +57,7 @@ function printModulesWarning() {
console.warn(
"Pro Tip: Did you know that you can speed up your build by specifying project name(s)?");
console.warn(" It's like pressing the turbo button in the old days, but better!");
console.warn(" Examples: --project=angular2 or --project=angular2");
console.warn(" Examples: --project=angular2 or --project=angular2,angular2_material");
}
}
@ -132,8 +132,7 @@ var CONFIG = {
dev: {es6: 'dist/js/dev/es6', es5: 'dist/js/dev/es5'},
prod: {es6: 'dist/js/prod/es6', es5: 'dist/js/prod/es5'},
cjs: 'dist/js/cjs',
dart2js: 'dist/js/dart2js',
dart_dev_compiler: 'dist/js/ddc'
dart2js: 'dist/js/dart2js'
},
dart: 'dist/dart',
docs: 'dist/docs',
@ -370,10 +369,6 @@ function jsServeDartJs() {
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart2js, port: 8002})();
}
function jsServeDartDevCompiler() {
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart_dev_compiler, port: 8003})();
}
function proxyServeDart() {
return jsserve(gulp, gulpPlugins, {
port: 8002,
@ -387,7 +382,7 @@ function proxyServeDart() {
// ------------------
// web servers
gulp.task('serve.js.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
gulp.task('serve.js.dev', ['build.js.dev'], function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, '!broccoli.js.dev');
@ -396,24 +391,24 @@ gulp.task('serve.js.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone)
gulp.task('serve.js.prod', jsServeProd);
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
var watch = require('./tools/build/watch');
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs', 'build.css.material'],
function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
jsServeDev();
});
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
jsServeDev();
});
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs'], function(neverDone) {
var watch = require('./tools/build/watch');
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs', 'build.css.material'],
function(neverDone) {
var watch = require('./tools/build/watch');
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
jsServeProd();
});
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
jsServeProd();
});
gulp.task('serve.js.dart2js', jsServeDartJs);
gulp.task('serve.js.ddc', jsServeDartDevCompiler);
gulp.task('!proxyServeDart', proxyServeDart);
gulp.task('serve.dart', function(done) {
@ -445,7 +440,7 @@ gulp.task('serve.e2e.dart', ['build.js.cjs'], function(neverDone) {
// Note: we are not using build.dart as the dart analyzer takes too long...
watch('modules/**', {ignoreInitial: true}, ['!build/tree.dart', '!build.js.cjs']);
runSequence('build/packages.dart', 'build/pubspec.dart', 'serve.dart');
runSequence('build/packages.dart', 'build/pubspec.dart', 'build.dart.material.css', 'serve.dart');
});
@ -711,7 +706,7 @@ gulp.task('!build.payload.js.webpack', function() {
.then(function() { // pad bundle with mandatory dependencies
return new Promise(function(resolve, reject) {
gulp.src([
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/reflect-metadata/Reflect.js',
CASE_PATH + '/app-bundle.js'
@ -779,7 +774,8 @@ gulp.task('!checkAndReport.payload.js', function() {
gulp.task('watch.dart.dev', function(done) {
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
function(error) {
var watch = require('./tools/build/watch');
// if initial build failed (likely due to build or formatting step) then exit
@ -1057,7 +1053,7 @@ gulp.task('build/packages.dart', function(done) {
// Builds and compiles all Dart packages
gulp.task('build.dart', function(done) {
runSequence('build/packages.dart', 'build/pubspec.dart', 'build/analyze.dart',
'build/check.apidocs.dart', sequenceComplete(done));
'build/check.apidocs.dart', 'build.dart.material.css', sequenceComplete(done));
});
@ -1116,8 +1112,9 @@ gulp.task('!broccoli.js.prod', () => angularBuilder.rebuildBrowserProdTree({
useBundles: cliArgs.useBundles
}));
gulp.task('build.js.dev', ['build/clean.js'],
function(done) { runSequence('broccoli.js.dev', sequenceComplete(done)); });
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.js.prod', ['build.tools'],
function(done) { runSequence('!broccoli.js.prod', sequenceComplete(done)); });
@ -1358,7 +1355,7 @@ gulp.task('!bundle.ng.polyfills', ['clean'],
var JS_DEV_DEPS = [
licenseWrap('node_modules/zone.js/LICENSE', true),
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
licenseWrap('node_modules/reflect-metadata/LICENSE', true),
'node_modules/reflect-metadata/Reflect.js'
@ -1474,6 +1471,41 @@ gulp.task('!build/change_detect.dart', function(done) {
});
// ------------
// angular material testing rules
gulp.task('build.css.material', function() {
var autoprefixer = require('gulp-autoprefixer');
var sass = require('gulp-sass');
return gulp.src('modules/*/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest(CONFIG.dest.js.prod.es5))
.pipe(gulp.dest(CONFIG.dest.js.dev.es5))
.pipe(gulp.dest(CONFIG.dest.js.dart2js + '/examples/packages'));
});
gulp.task('build.js.material', function(done) {
runSequence('build.js.dev', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.dart2js.material', function(done) {
runSequence('build.dart', 'build.css.material', sequenceComplete(done));
});
gulp.task('build.dart.material.css', function() {
var autoprefixer = require('gulp-autoprefixer');
var sass = require('gulp-sass');
return gulp.src('dist/dart/angular2_material/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('dist/dart/angular2_material/lib/src'));
});
gulp.task('build.dart.material', ['build/packages.dart'], function(done) {
runSequence('build/packages.dart', 'build.dart.material.css', sequenceComplete(done));
});
gulp.task('cleanup.builder', function() { return angularBuilder.cleanup(); });

View File

@ -17,7 +17,8 @@ module.exports = function(config) {
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js',
'node_modules/zone.js/dist/zone.js',
// zone-microtask must be included first as it contains a Promise monkey patch
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/jasmine-patch.js',

View File

@ -40,10 +40,8 @@ function main(modulesDirectory) {
return prev + transform(fs.readFileSync(dir + file, 'utf8'));
}, '');
// we have to use a function callback for replace to prevent it from interpreting `$`
// as a replacement command character
var out = moduleTemplate.replace('//{{FACADES}}', function() { return facades; })
.replace('//{{SHARED_CODE}}', function() { return sharedCode; });
var out = moduleTemplate.replace('//{{FACADES}}', facades)
.replace('//{{SHARED_CODE}}', sharedCode);
return PRELUDE + transform(directives) + out + POSTLUDE;
}

View File

@ -316,9 +316,3 @@ Location.prototype.path = function () {
Location.prototype.go = function (path, query) {
return $location.url(path + query);
};
Location.prototype.prepareExternalUrl = function(url) {
if (url.length > 0 && !url.startsWith('/')) {
url = '/' + url;
}
return $location.$$html5 ? '.' + url : '#' + $locationHashPrefix + url;
};

View File

@ -4,33 +4,9 @@ angular.module('ngComponentRouter').
// Because Angular 1 has no notion of a root component, we use an object with unique identity
// to represent this. Can be overloaded with a component name
value('$routerRootComponent', new Object()).
factory('$rootRouter', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
// Unfortunately, $location doesn't expose what the current hashPrefix is
// So we have to monkey patch the $locationProvider to capture this value
provider('$locationHashPrefix', ['$locationProvider', $locationHashPrefixProvider]).
factory('$rootRouter', ['$q', '$location', '$browser', '$rootScope', '$injector', '$routerRootComponent', '$locationHashPrefix', routerFactory]);
function $locationHashPrefixProvider($locationProvider) {
// Get hold of the original hashPrefix method
var hashPrefixFn = $locationProvider.hashPrefix.bind($locationProvider);
// Read the current hashPrefix (in case it was set before this monkey-patch occurred)
var hashPrefix = hashPrefixFn();
// Override the helper so that we can read any changes to the prefix (after this monkey-patch)
$locationProvider.hashPrefix = function(prefix) {
if (angular.isDefined(prefix)) {
hashPrefix = prefix;
}
return hashPrefixFn(prefix);
}
// Return the final hashPrefix as the value of this service
this.$get = function() { return hashPrefix; };
}
function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRootComponent, $locationHashPrefix) {
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector, $routerRootComponent) {
// When this file is processed, the line below is replaced with
// the contents of `../lib/facades.es5`.
@ -47,24 +23,11 @@ function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRo
// the contents of the compiled TypeScript classes.
//{{SHARED_CODE}}
function getComponentConstructor(name) {
var serviceName = name + 'Directive';
if ($injector.has(serviceName)) {
var definitions = $injector.get(serviceName);
if (definitions.length > 1) {
throw new BaseException('too many directives named "' + name + '"');
}
return definitions[0].controller;
} else {
throw new BaseException('directive "' + name + '" is not registered');
}
}
//TODO: this is a hack to replace the exiting implementation at run-time
exports.getCanActivateHook = function (directiveName) {
var controller = getComponentConstructor(directiveName);
return controller.$canActivate && function (next, prev) {
return $injector.invoke(controller.$canActivate, null, {
var factory = $$directiveIntrospector.getTypeByName(directiveName);
return factory && factory.$canActivate && function (next, prev) {
return $injector.invoke(factory.$canActivate, null, {
$nextInstruction: next,
$prevInstruction: prev
});
@ -82,32 +45,17 @@ function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRo
var RouteRegistry = exports.RouteRegistry;
var RootRouter = exports.RootRouter;
// Override this method to actually get hold of the child routes
RouteRegistry.prototype.configFromComponent = function (component) {
var that = this;
if (isString(component)) {
// Don't read the annotations component a type more than once
// this prevents an infinite loop if a component routes recursively.
if (this._rules.has(component)) {
return;
}
var controller = getComponentConstructor(component);
if (angular.isArray(controller.$routeConfig)) {
controller.$routeConfig.forEach(function (config) {
var loader = config.loader;
if (isPresent(loader)) {
config = angular.extend({}, config, { loader: () => $injector.invoke(loader) });
}
that.config(component, config);
});
}
}
}
var registry = new RouteRegistry($routerRootComponent);
var location = new Location();
$$directiveIntrospector(function (name, factory) {
if (angular.isArray(factory.$routeConfig)) {
factory.$routeConfig.forEach(function (config) {
registry.config(name, config);
});
}
});
var router = new RootRouter(registry, location, $routerRootComponent);
$rootScope.$watch(function () { return $location.url(); }, function (path) {
if (router.lastNavigationAttempt !== path) {

View File

@ -1,6 +1,51 @@
///<reference path="../typings/angularjs/angular.d.ts"/>
/*
* decorates $compileProvider so that we have access to routing metadata
*/
function compilerProviderDecorator($compileProvider,
$$directiveIntrospectorProvider: DirectiveIntrospectorProvider) {
let directive = $compileProvider.directive;
$compileProvider.directive = function(name: string, factory: Function) {
$$directiveIntrospectorProvider.register(name, factory);
return directive.apply(this, arguments);
};
}
/*
* private service that holds route mappings for each controller
*/
class DirectiveIntrospectorProvider {
private directiveBuffer: any[] = [];
private directiveFactoriesByName: {[name: string]: Function} = {};
private onDirectiveRegistered: (name: string, factory: Function) => any = null;
register(name: string, factory: Function) {
if (angular.isArray(factory)) {
factory = factory[factory.length - 1];
}
this.directiveFactoriesByName[name] = factory;
if (this.onDirectiveRegistered) {
this.onDirectiveRegistered(name, factory);
} else {
this.directiveBuffer.push({name: name, factory: factory});
}
}
$get() {
let fn: any = newOnControllerRegistered => {
this.onDirectiveRegistered = newOnControllerRegistered;
while (this.directiveBuffer.length > 0) {
let directive = this.directiveBuffer.pop();
this.onDirectiveRegistered(directive.name, directive.factory);
}
};
fn.getTypeByName = name => this.directiveFactoriesByName[name];
return fn;
}
}
/**
* @name ngOutlet
@ -207,13 +252,13 @@ function ngLinkDirective($rootRouter, $parse) {
return;
}
let navigationInstruction = null;
let instruction = null;
let link = attrs.ngLink || '';
function getLink(params) {
navigationInstruction = router.generate(params);
instruction = router.generate(params);
scope.$watch(function() { return router.isRouteActive(navigationInstruction); },
scope.$watch(function() { return router.isRouteActive(instruction); },
function(active) {
if (active) {
element.addClass('ng-link-active');
@ -222,8 +267,7 @@ function ngLinkDirective($rootRouter, $parse) {
}
});
const navigationHref = navigationInstruction.toLinkUrl();
return $rootRouter._location.prepareExternalUrl(navigationHref);
return './' + angular.stringifyInstruction(instruction);
}
let routeParamsGetter = $parse(link);
@ -237,11 +281,11 @@ function ngLinkDirective($rootRouter, $parse) {
}
element.on('click', event => {
if (event.which !== 1 || !navigationInstruction) {
if (event.which !== 1 || !instruction) {
return;
}
$rootRouter.navigateByInstruction(navigationInstruction);
$rootRouter.navigateByInstruction(instruction);
event.preventDefault();
});
}
@ -259,3 +303,10 @@ angular.module('ngComponentRouter', [])
.directive('ngOutlet', ['$compile', ngOutletFillContentDirective])
.directive('ngLink', ['$rootRouter', '$parse', ngLinkDirective])
.directive('$router', ['$q', routerTriggerDirective]);
/*
* A module for inspecting controller constructors
*/
angular.module('ng')
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
.config(['$compileProvider', '$$directiveIntrospectorProvider', compilerProviderDecorator]);

View File

@ -150,7 +150,7 @@
}];
// we resolve the locals in a canActivate block
componentDefinition.controller.$canActivate = function() {
componentDefinition.$canActivate = function() {
var locals = angular.extend({}, routeCopy.resolve);
angular.forEach(locals, function(value, key) {

View File

@ -0,0 +1,38 @@
'use strict';
describe('$$directiveIntrospector', function () {
var $compileProvider;
beforeEach(function() {
module('ng');
module('ngComponentRouter');
module(function(_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
});
it('should call the introspector function whenever a directive factory is registered', inject(function ($$directiveIntrospector) {
var spy = jasmine.createSpy();
$$directiveIntrospector(spy);
function myDir(){}
$compileProvider.directive('myDir', myDir);
expect(spy).toHaveBeenCalledWith('myDir', myDir);
}));
it('should call the introspector function whenever a directive factory is registered with array annotations', inject(function ($$directiveIntrospector) {
var spy = jasmine.createSpy();
$$directiveIntrospector(spy);
function myDir(){}
$compileProvider.directive('myDir', ['foo', myDir]);
expect(spy).toHaveBeenCalledWith('myDir', myDir);
}));
it('should retrieve a factory based on directive name', inject(function ($$directiveIntrospector) {
function myDir(){}
$compileProvider.directive('myDir', ['foo', myDir]);
expect($$directiveIntrospector.getTypeByName('myDir')).toBe(myDir);
}));
});

View File

@ -466,10 +466,10 @@ describe('Navigation lifecycle', function () {
}
if (options.$canActivate) {
controller.$canActivate = options.$canActivate;
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
controller.$routeConfig = options.$routeConfig;
factory.$routeConfig = options.$routeConfig;
}
$compileProvider.directive(name, factory);

View File

@ -306,15 +306,14 @@ describe('navigation', function () {
}));
function registerDirective(name, options) {
var controller = getController(options);
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
controller: getController(options)
};
}
applyStaticProperties(controller, options);
applyStaticProperties(factory, options);
$compileProvider.directive(name, factory);
}
@ -324,7 +323,7 @@ describe('navigation', function () {
template: options.template || '',
controller: getController(options),
}
applyStaticProperties(definition.controller, options);
applyStaticProperties(definition, options);
$compileProvider.component(name, definition);
}

View File

@ -2,14 +2,30 @@
describe('router', function () {
var elt, testMod;
var elt,
$compile,
$rootScope,
$rootRouter,
$compileProvider;
beforeEach(function () {
testMod = angular.module('testMod', ['ngComponentRouter'])
.value('$routerRootComponent', 'app');
module('ng');
module('ngComponentRouter');
module(function($provide) {
$provide.value('$routerRootComponent', 'app');
});
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootRouter = _$rootRouter_;
});
});
it('should work with a provided root component', function() {
it('should work with a provided root component', inject(function($location) {
registerComponent('homeCmp', {
template: 'Home'
});
@ -21,17 +37,14 @@ describe('router', function () {
]
});
module('testMod');
compileApp();
compile('<app></app>');
inject(function($location, $rootScope) {
$location.path('/');
$rootScope.$digest();
expect(elt.text()).toBe('Home');
});
});
$location.path('/');
$rootScope.$digest();
expect(elt.text()).toBe('Home');
}));
it('should bind the component to the current router', function() {
it('should bind the component to the current router', inject(function($location) {
var router;
registerComponent('homeCmp', {
bindings: { $router: '=' },
@ -50,48 +63,41 @@ describe('router', function () {
]
});
module('testMod');
compileApp();
compile('<app></app>');
inject(function($location, $rootScope) {
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect(homeElement.isolateScope().$ctrl.$router).toBeDefined();
expect(router).toBeDefined();
})
});
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect(homeElement.isolateScope().$ctrl.$router).toBeDefined();
expect(router).toBeDefined();
}));
it('should work when an async route is provided route data', function() {
registerComponent('homeCmp', {
template: 'Home ({{$ctrl.isAdmin}})',
it('should work when an async route is provided route data', inject(function($location, $q) {
registerDirective('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})',
$routerOnActivate: function(next, prev) {
this.isAdmin = next.routeData.data.isAdmin;
}
});
registerComponent('app', {
registerDirective('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', loader: function($q) { return $q.when('homeCmp'); }, data: { isAdmin: true } }
{ path: '/', loader: function() { return $q.when('homeCmp'); }, data: { isAdmin: true } }
]
});
module('testMod');
compileApp();
compile('<app></app>');
inject(function($location, $rootScope) {
$location.path('/');
$rootScope.$digest();
expect(elt.text()).toBe('Home (true)');
});
});
it('should work with a templateUrl component', function() {
$location.path('/');
$rootScope.$digest();
expect(elt.text()).toBe('Home (true)');
}));
it('should work with a templateUrl component', inject(function($location, $httpBackend) {
var $routerOnActivate = jasmine.createSpy('$routerOnActivate');
$httpBackend.expectGET('homeCmp.html').respond('Home');
registerComponent('homeCmp', {
templateUrl: 'homeCmp.html',
$routerOnActivate: $routerOnActivate
@ -104,24 +110,17 @@ describe('router', function () {
]
});
module('testMod');
compile('<app></app>');
inject(function($location, $rootScope, $httpBackend) {
$location.path('/');
$rootScope.$digest();
$httpBackend.flush();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect($routerOnActivate).toHaveBeenCalled();
}));
$httpBackend.expectGET('homeCmp.html').respond('Home');
compileApp();
$location.path('/');
$rootScope.$digest();
$httpBackend.flush();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect($routerOnActivate).toHaveBeenCalled();
})
});
it('should provide the current instruction', function() {
it('should provide the current instruction', inject(function($location, $q) {
registerComponent('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})'
});
@ -132,20 +131,15 @@ describe('router', function () {
{ path: '/', component: 'homeCmp', name: 'Home' }
]
});
compile('<app></app>');
module('testMod');
$location.path('/');
$rootScope.$digest();
var instruction = $rootRouter.generate(['/Home']);
expect($rootRouter.currentInstruction).toEqual(instruction);
}));
inject(function($rootScope, $rootRouter, $location) {
compileApp();
$location.path('/');
$rootScope.$digest();
var instruction = $rootRouter.generate(['/Home']);
expect($rootRouter.currentInstruction).toEqual(instruction);
});
});
it('should provide the root level router', function() {
it('should provide the root level router', inject(function($location, $q) {
registerComponent('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})',
bindings: {
@ -159,18 +153,25 @@ describe('router', function () {
{ path: '/', component: 'homeCmp', name: 'Home' }
]
});
compile('<app></app>');
module('testMod');
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.isolateScope().$ctrl.$router.root).toEqual($rootRouter);
}));
inject(function($rootScope, $rootRouter, $location) {
compileApp();
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.isolateScope().$ctrl.$router.root).toEqual($rootRouter);
});
});
function registerDirective(name, options) {
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: getController(options)
};
}
applyStaticProperties(factory, options);
$compileProvider.directive(name, factory);
}
function registerComponent(name, options) {
@ -181,15 +182,13 @@ describe('router', function () {
if (options.template) definition.template = options.template;
if (options.templateUrl) definition.templateUrl = options.templateUrl;
applyStaticProperties(definition.controller, options);
angular.module('testMod').component(name, definition);
applyStaticProperties(definition, options);
$compileProvider.component(name, definition);
}
function compileApp() {
inject(function($compile, $rootScope) {
elt = $compile('<div><app></app</div>')($rootScope);
$rootScope.$digest();
});
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}

View File

@ -2,191 +2,183 @@
describe('ngLink', function () {
describe('html5Mode enabled', function () {
runHrefTestsAndExpectPrefix('/', true);
});
var elt,
$compile,
$rootScope,
$rootRouter,
$compileProvider;
describe('html5Mode disabled', function () {
runHrefTestsAndExpectPrefix('', false, '');
});
describe('html5Mode disabled, with hash prefix', function () {
runHrefTestsAndExpectPrefix('', false, '!');
});
describe('html5Mode enabled', function () {
runHrefTestsAndExpectPrefix('/moo', true);
});
describe('html5Mode disabled', function () {
runHrefTestsAndExpectPrefix('/moo', false, '');
});
describe('html5Mode disabled, with hash prefix', function () {
runHrefTestsAndExpectPrefix('/moo', false, '!');
});
function runHrefTestsAndExpectPrefix(baseHref, html5Mode, hashPrefix) {
var prefix = html5Mode ? '.' : '#' + hashPrefix;
it('should allow linking from the parent to the child', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
var elt = compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
navigateTo('/a');
expect(elt.find('a').attr('href')).toBe(prefix + '/b');
});
it('should allow linking from the child and the parent', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
var elt = compile('outer { <div ng-outlet></div> }');
navigateTo('/b');
expect(elt.find('a').attr('href')).toBe(prefix + '/b');
});
it('should allow params in routerLink directive', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
configureRouter([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
var elt = compile('<div ng-outlet></div>');
navigateTo('/a');
expect(elt.find('a').attr('href')).toBe(prefix + '/b/lol');
});
it('should update the href of links with bound params', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: $ctrl.number}]">{{$ctrl.number}}</a></div>', function () {this.number = 43});
configureRouter([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
var elt = compile('<div ng-outlet></div>');
navigateTo('/a');
expect(elt.find('a').text()).toBe('43');
expect(elt.find('a').attr('href')).toBe(prefix + '/b/43');
});
it('should navigate on left-mouse click when a link url matches a route', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
var elt = compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
expect(elt.text()).toBe('link | one');
expect(elt.find('a').attr('href')).toBe(prefix + '/two');
elt.find('a')[0].click();
inject(function($rootScope) { $rootScope.$digest(); });
expect(elt.text()).toBe('link | two');
});
it('should not navigate on non-left mouse click when a link url matches a route', function() {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
var elt = compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
expect(elt.text()).toBe('link | one');
elt.find('a').triggerHandler({ type: 'click', which: 3 });
inject(function($rootScope) { $rootScope.$digest(); });
expect(elt.text()).toBe('link | one');
});
// See https://github.com/angular/router/issues/206
it('should not navigate a link without an href', function () {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
expect(function () {
var elt = compile('<a>link</a>');
expect(elt.text()).toBe('link');
elt.find('a')[0].click();
inject(function($rootScope) { $rootScope.$digest(); });
}).not.toThrow();
});
it('should add an ng-link-active class on the current link', function() {
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
configureRouter([
{ path: '/', component: 'oneCmp', name: 'One' }
]);
var elt = compile('<a ng-link="[\'/One\']">one</a> | <div ng-outlet></div>');
navigateTo('/');
expect(elt.find('a').attr('class')).toBe('ng-link-active');
});
}
function registerComponent(name, template, controller) {
module(function($compileProvider) {
$compileProvider.component(name, {
template: template,
controller: controller
});
});
}
function setup(config) {
module(function($provide) {
$provide.decorator('$browser', function($delegate) {
$delegate.baseHref = function() { return config.baseHref; };
return $delegate;
});
});
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module(function($locationProvider) {
$locationProvider.html5Mode(config.html5Mode);
$locationProvider.hashPrefix(config.hashPrefix);
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
registerComponent('userCmp', '<div>hello {{$ctrl.$routeParams.name}}</div>', function () {});
registerComponent('oneCmp', '<div>{{$ctrl.number}}</div>', function () {this.number = 'one'});
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{$ctrl.number}}</a></div>', function () {this.number = 'two'});
}
function configureRouter(routeConfig) {
inject(function($rootRouter) {
$rootRouter.config(routeConfig);
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootRouter = _$rootRouter_;
});
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
registerComponent('oneCmp', '<div>{{oneCmp.number}}</div>', function () {this.number = 'one'});
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>', function () {this.number = 'two'});
});
it('should allow linking from the parent to the child', function () {
$rootRouter.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
});
it('should allow linking from the child and the parent', function () {
$rootRouter.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
compile('outer { <div ng-outlet></div> }');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
});
it('should allow params in routerLink directive', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
$rootRouter.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
compile('<div ng-outlet></div>');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/lol');
});
it('should update the href of links with bound params', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
$rootRouter.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
compile('<div ng-outlet></div>');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/param');
});
it('should navigate on left-mouse click when a link url matches a route', function () {
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
expect(elt.find('a').attr('href')).toBe('./two');
elt.find('a')[0].click();
$rootScope.$digest();
expect(elt.text()).toBe('link | two');
});
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($rootRouter) {
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
elt.find('a').triggerHandler({ type: 'click', which: 3 });
$rootScope.$digest();
expect(elt.text()).toBe('link | one');
}));
// See https://github.com/angular/router/issues/206
it('should not navigate a link without an href', function () {
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
expect(function () {
compile('<a>link</a>');
$rootScope.$digest();
expect(elt.text()).toBe('link');
elt.find('a')[0].click();
$rootScope.$digest();
}).not.toThrow();
});
it('should add an ng-link-active class on the current link', inject(function ($rootRouter) {
$rootRouter.config([
{ path: '/', component: 'oneCmp', name: 'One' }
]);
compile('<a ng-link="[\'/One\']">one</a> | <div ng-outlet></div>');
$rootScope.$digest();
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.find('a').attr('class')).toBe('ng-link-active');
}));
function registerComponent(name, template, config) {
var controller = function () {};
function factory() {
return {
template: template,
controllerAs: name,
controller: controller
};
}
if (!template) {
template = '';
}
if (angular.isArray(config)) {
factory.annotations = [new angular.annotations.RouteConfig(config)];
} else if (typeof config === 'function') {
controller = config;
} else if (typeof config === 'object') {
if (config.canActivate) {
controller.$canActivate = config.canActivate;
}
}
$compileProvider.directive(name, factory);
}
function compile(template) {
var elt;
inject(function($compile, $rootScope) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
});
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
function navigateTo(url) {
inject(function($rootRouter, $rootScope) {
$rootRouter.navigateByUrl(url);
$rootScope.$digest();
});
}
});

View File

@ -72,7 +72,7 @@ An example of an Angular 2 project built with WebPack can be found in the [angul
Polyfills are required for Angular 2 to function properly (the exact list depends on the browser used) and external dependencies ([zone.js](https://github.com/angular/zone.js)).
To ease setup of Angular 2 applications there is one file - `angular2-polyfills.js` - that combines:
* a polyfill mandatory for all browsers: [reflect-metadata](https://www.npmjs.com/package/reflect-metadata)
* a pollyfill mandatory for all browsers: [reflect-metadata](https://www.npmjs.com/package/reflect-metadata)
* [zone.js](https://github.com/angular/zone.js)
**Note**: `angular2-polyfills.js` contains code that should be loaded into the browser as the very first code of the web application even before the module loader. The preferred solution is to load the mentioned file in a `script` tag as early as possible.

View File

@ -4,7 +4,7 @@ Bootstrapping
@description
{@target ts}`import {bootstrap} from 'angular2/platform/browser';`{@endtarget}
{@target js}Available from the `ng.platform.browser` namespace{@endtarget}
{@target dart}`import 'package:angular2/platform/browser.dart';`{@endtarget}
{@target dart}`import 'package:angular2/bootstrap.dart';`{@endtarget}
@cheatsheetItem
syntax(ts dart):

View File

@ -112,7 +112,9 @@ Example of a component:
'title', | - title mapped to component title
'open' | - open attribute mapped to component's open property
], |
templateUrl: 'pane.html' | URL of template HTML
}) |
@View({ | View annotation
templateUrl: 'pane.html' | - URL of template HTML
}) |
class Pane { | Component controller class
title:string; | - title property
@ -225,9 +227,11 @@ class MyService {} | Assume a service which needs to be inject
|
@Component({ | Assume a top level application component which
selector: 'my-app', | configures the services to be injected.
viewBindings: [MyService], |
templateUrl: 'my_app.html', | Assume we have a template that needs to be
directives: [House] | configured with directives to be injected.
viewBindings: [MyService] |
}) |
@View({ | Assume we have a template that needs to be
templateUrl: 'my_app.html', | configured with directives to be injected.
directives: [House] |
}) |
class MyApp {} |
|
@ -269,6 +273,8 @@ Here is an example of the kinds of injections which can be achieved:
```
@Component({ |
selector: 'my-app' |
}) |
@View({ |
templateUrl: 'my_app.html', |
directives: [Form, FieldSet, |
Field, Primary] |
@ -323,6 +329,8 @@ Shadow DOM provides an encapsulation for components, so as a general rule it doe
```
@Component({
selector: '[kid]'
})
@View({
templateUrl: 'kid.html',
directives: []
})
@ -339,6 +347,8 @@ class Kid {
@Component({
selector: '[dad]'
})
@View({
templateUrl: 'dad.html',
directives: [Kid]
})
@ -351,7 +361,9 @@ class Dad {
@Component({
selector: '[grandpa]',
viewBindings: [],
viewBindings: []
})
@View({
templateUrl: 'grandpa.html',
directives: [Dad]
})

View File

@ -179,7 +179,9 @@ Both `MyComponent` and `MyDirective` are created on the same element.
],
viewBindings: [
bind('viewService').toValue('View_MyComponentService')
],
]
})
@View({
template: `<needs-view-service></needs-view-service>`,
directives: [NeedsViewService]
})

View File

@ -1,5 +1,5 @@
import {provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {UrlResolver} from 'angular2/compiler';
var MyApp: any;

View File

@ -1,4 +1,4 @@
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {NG_VALIDATORS} from 'angular2/common';
import {Provider} from 'angular2/core';

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {Observable, Subscriber} from 'rxjs/Rx';
// #docregion AsyncPipe

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
// #docregion DatePipe
@Component({

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
// #docregion JsonPipe
@Component({

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
// #docregion LowerUpperPipe
@Component({

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
// #docregion NumberPipe
@Component({

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
// #docregion SlicePipe_string
@Component({

View File

@ -1,6 +1,6 @@
// #docregion enableProdMode
import {enableProdMode} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {MyComponent} from './my_component';
enableProdMode();

View File

@ -1,5 +1,5 @@
import {provide, Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {
CanActivate,
RouteConfig,

View File

@ -1,5 +1,5 @@
import {provide, Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {
CanDeactivate,
RouteConfig,

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {
OnActivate,
ComponentInstruction,

View File

@ -1,5 +1,5 @@
import {Component, Injectable, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {
OnDeactivate,
ComponentInstruction,

View File

@ -1,5 +1,5 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {bootstrap} from 'angular2/bootstrap';
import {
CanActivate,
RouteConfig,

View File

@ -1,8 +0,0 @@
/**
* @module
* @description
* Entry point to i18n
*/
export * from './src/i18n/message';
export * from './src/i18n/xmb_serializer';
export * from './src/i18n/message_extractor';

View File

@ -9,6 +9,7 @@
"repository": <%= JSON.stringify(packageJson.repository) %>,
"devDependencies": <%= JSON.stringify(packageJson.defaultDevDependencies) %>,
"peerDependencies": {
"es6-promise": "<%= packageJson.dependencies['es6-promise'] %>",
"es6-shim": "<%= packageJson.dependencies['es6-shim'] %>",
"reflect-metadata": "<%= packageJson.dependencies['reflect-metadata'] %>",
"rxjs": "<%= packageJson.dependencies['rxjs'] %>",

View File

@ -20,7 +20,6 @@ dependencies:
protobuf: '^0.5.0'
source_span: '^1.0.0'
stack_trace: '^1.1.1'
build: '>=0.0.1'
dev_dependencies:
transformer_test: '^0.2.0'
guinness: '^0.1.18'

View File

@ -9,7 +9,7 @@ import {CORE_DIRECTIVES} from './directives';
* NgModel).
*
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
* property of the `@Component` decorator.
* property of the `@Component` or `@View` decorators.
*
* ### Example
*

View File

@ -8,6 +8,5 @@ export {NgFor} from './directives/ng_for';
export {NgIf} from './directives/ng_if';
export {NgStyle} from './directives/ng_style';
export {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './directives/ng_switch';
export {NgPlural, NgPluralCase, NgLocalization} from './directives/ng_plural';
export * from './directives/observable_list_diff';
export {CORE_DIRECTIVES} from './directives/core_directives';

View File

@ -4,14 +4,13 @@ import {NgFor} from './ng_for';
import {NgIf} from './ng_if';
import {NgStyle} from './ng_style';
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
import {NgPlural, NgPluralCase} from './ng_plural';
/**
* A collection of Angular core directives that are likely to be used in each and every Angular
* application.
*
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
* property of the `@Component` annotation.
* property of the `@View` annotation.
*
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
*
@ -46,14 +45,5 @@ import {NgPlural, NgPluralCase} from './ng_plural';
* }
* ```
*/
export const CORE_DIRECTIVES: Type[] = CONST_EXPR([
NgClass,
NgFor,
NgIf,
NgStyle,
NgSwitch,
NgSwitchWhen,
NgSwitchDefault,
NgPlural,
NgPluralCase
]);
export const CORE_DIRECTIVES: Type[] =
CONST_EXPR([NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchWhen, NgSwitchDefault]);

View File

@ -1,146 +0,0 @@
import {
Directive,
ViewContainerRef,
TemplateRef,
ContentChildren,
QueryList,
Attribute,
AfterContentInit,
Input
} from 'angular2/core';
import {isPresent, NumberWrapper} from 'angular2/src/facade/lang';
import {Map} from 'angular2/src/facade/collection';
import {SwitchView} from './ng_switch';
const _CATEGORY_DEFAULT = 'other';
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
/**
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
*
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
* switch expression.
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
* expression.
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
* matches the switch expression exactly.
* - Otherwise, the view will be treated as a "category match", and will only display if exact
* value matches aren't found and the value maps to its category using the `getPluralCategory`
* function provided.
*
* If no matching views are found for a switch expression, inner elements marked
* `[ngPluralCase]="other"` will be displayed.
*
* ```typescript
* class MyLocalization extends NgLocalization {
* getPluralCategory(value: any) {
* if(value < 5) {
* return 'few';
* }
* }
* }
*
* @Component({
* selector: 'app',
* providers: [provide(NgLocalization, {useClass: MyLocalization})]
* })
* @View({
* template: `
* <p>Value = {{value}}</p>
* <button (click)="inc()">Increment</button>
*
* <div [ngPlural]="value">
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* <template ngPluralCase="other">there is some number</template>
* </div>
* `,
* directives: [NgPlural, NgPluralCase]
* })
* export class App {
* value = 'init';
*
* inc() {
* this.value = this.value === 'init' ? 0 : this.value + 1;
* }
* }
*
* ```
*/
@Directive({selector: '[ngPluralCase]'})
export class NgPluralCase {
_view: SwitchView;
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef,
viewContainer: ViewContainerRef) {
this._view = new SwitchView(viewContainer, template);
}
}
@Directive({selector: '[ngPlural]'})
export class NgPlural implements AfterContentInit {
private _switchValue: number;
private _activeView: SwitchView;
private _caseViews = new Map<any, SwitchView>();
@ContentChildren(NgPluralCase) cases: QueryList<NgPluralCase> = null;
constructor(private _localization: NgLocalization) {}
@Input()
set ngPlural(value: number) {
this._switchValue = value;
this._updateView();
}
ngAfterContentInit() {
this.cases.forEach((pluralCase: NgPluralCase): void => {
this._caseViews.set(this._formatValue(pluralCase), pluralCase._view);
});
this._updateView();
}
/** @internal */
_updateView(): void {
this._clearViews();
var view: SwitchView = this._caseViews.get(this._switchValue);
if (!isPresent(view)) view = this._getCategoryView(this._switchValue);
this._activateView(view);
}
/** @internal */
_clearViews() {
if (isPresent(this._activeView)) this._activeView.destroy();
}
/** @internal */
_activateView(view: SwitchView) {
if (!isPresent(view)) return;
this._activeView = view;
this._activeView.create();
}
/** @internal */
_getCategoryView(value: number): SwitchView {
var category: string = this._localization.getPluralCategory(value);
var categoryView: SwitchView = this._caseViews.get(category);
return isPresent(categoryView) ? categoryView : this._caseViews.get(_CATEGORY_DEFAULT);
}
/** @internal */
_isValueView(pluralCase: NgPluralCase): boolean { return pluralCase.value[0] === "="; }
/** @internal */
_formatValue(pluralCase: NgPluralCase): any {
return this._isValueView(pluralCase) ? this._stripValue(pluralCase.value) : pluralCase.value;
}
/** @internal */
_stripValue(value: string): number { return NumberWrapper.parseInt(value.substring(1), 10); }
}

View File

@ -4,6 +4,7 @@ import {ListWrapper, Map} from 'angular2/src/facade/collection';
const _WHEN_DEFAULT = CONST_EXPR(new Object());
/** @internal */
export class SwitchView {
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
@ -31,8 +32,8 @@ export class SwitchView {
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
*
* ```typescript
* @Component({
* selector: 'app',
* @Component({selector: 'app'})
* @View({
* template: `
* <p>Value = {{value}}</p>
* <button (click)="inc()">Increment</button>

View File

@ -50,7 +50,7 @@ export {ControlValueAccessor} from './directives/control_value_accessor';
/**
*
* A list of all the form directives used as part of a `@Component` annotation.
* A list of all the form directives used as part of a `@View` annotation.
*
* This is a shorthand for importing them each individually.
*

View File

@ -1,7 +1,6 @@
import {ControlValueAccessor} from './control_value_accessor';
import {AbstractControlDirective} from './abstract_control_directive';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {AsyncValidatorFn, ValidatorFn} from './validators';
/**
* A base class that all control directive extend.
@ -13,8 +12,8 @@ export abstract class NgControl extends AbstractControlDirective {
name: string = null;
valueAccessor: ControlValueAccessor = null;
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
get validator(): Function { return unimplemented(); }
get asyncValidator(): Function { return unimplemented(); }
abstract viewToModelUpdate(newValue: any): void;
}

View File

@ -16,8 +16,7 @@ import {ControlContainer} from './control_container';
import {controlPath, composeValidators, composeAsyncValidators} from './shared';
import {ControlGroup} from '../model';
import {Form} from './form_interface';
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
import {AsyncValidatorFn, ValidatorFn} from './validators';
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
const controlGroupProvider =
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgControlGroup)}));
@ -33,6 +32,8 @@ const controlGroupProvider =
* @Component({
* selector: 'my-app',
* directives: [FORM_DIRECTIVES],
* })
* @View({
* template: `
* <div>
* <h2>Angular2 Control &amp; ControlGroup Example</h2>
@ -105,7 +106,7 @@ export class NgControlGroup extends ControlContainer implements OnInit,
*/
get formDirective(): Form { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get validator(): Function { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
}

View File

@ -27,8 +27,7 @@ import {
selectValueAccessor
} from './shared';
import {Control} from '../model';
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
import {ValidatorFn, AsyncValidatorFn} from './validators';
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
const controlNameBinding =
@ -137,9 +136,9 @@ export class NgControlName extends NgControl implements OnChanges,
get formDirective(): any { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get validator(): Function { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
get control(): Control { return this.formDirective.getControl(this); }
}

View File

@ -23,7 +23,6 @@ import {
isPropertyUpdated,
selectValueAccessor
} from './shared';
import {ValidatorFn, AsyncValidatorFn} from './validators';
const formControlBinding =
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgFormControl)}));
@ -111,9 +110,9 @@ export class NgFormControl extends NgControl implements OnChanges {
get path(): string[] { return []; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get validator(): Function { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
get control(): Control { return this.form; }

View File

@ -3,6 +3,7 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {
OnChanges,
SimpleChange,
Query,
Directive,
forwardRef,
Provider,
@ -13,7 +14,7 @@ import {
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
import {Control} from '../model';
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
import {
setUpControl,
isPropertyUpdated,
@ -21,7 +22,6 @@ import {
composeValidators,
composeAsyncValidators
} from './shared';
import {ValidatorFn, AsyncValidatorFn} from './validators';
const formControlBinding =
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgModel)}));
@ -88,9 +88,9 @@ export class NgModel extends NgControl implements OnChanges {
get path(): string[] { return []; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get validator(): Function { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;

View File

@ -10,11 +10,3 @@ Function normalizeValidator(dynamic validator){
}
}
Function normalizeAsyncValidator(dynamic validator){
if (validator is Validator) {
return (c) => validator.validate(c);
} else {
return validator;
}
}

View File

@ -1,18 +1,14 @@
import {AbstractControl} from "../model";
import {Validator, ValidatorFn, AsyncValidatorFn} from './validators';
import {Validator} from './validators';
import {Control} from "../model";
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
export type ctrlFunc = ((c: Control) => {
[key: string]: any
});
export function normalizeValidator(validator: (ctrlFunc | Validator)): ctrlFunc {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => (<Validator>validator).validate(c);
return (c: Control) => (<Validator>validator).validate(c);
} else {
return <ValidatorFn>validator;
}
}
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => Promise.resolve((<Validator>validator).validate(c));
} else {
return <AsyncValidatorFn>validator;
return <ctrlFunc>validator;
}
}

View File

@ -14,8 +14,7 @@ import {NumberValueAccessor} from './number_value_accessor';
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {normalizeValidator, normalizeAsyncValidator} from './normalize_validator';
import {ValidatorFn, AsyncValidatorFn} from './validators';
import {normalizeValidator} from './normalize_validator';
export function controlPath(name: string, parent: ControlContainer): string[] {
@ -57,14 +56,13 @@ function _throwError(dir: AbstractControlDirective, message: string): void {
throw new BaseException(`${message} '${path}'`);
}
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): Function {
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
}
export function composeAsyncValidators(
validators: /* Array<Validator|Function> */ any[]): AsyncValidatorFn {
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
null;
validators: /* Array<Validator|Function> */ any[]): Function {
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeValidator)) : null;
}
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {

View File

@ -1,12 +1,11 @@
import {forwardRef, Provider, Attribute, Directive} from 'angular2/core';
import {forwardRef, Provider, OpaqueToken, Attribute, Directive} from 'angular2/core';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {Validators, NG_VALIDATORS} from '../validators';
import {AbstractControl} from '../model';
import {Control} from '../model';
import * as modelModule from '../model';
import {NumberWrapper} from "angular2/src/facade/lang";
/**
* An interface that can be implemented by classes that can act as validators.
*
@ -24,7 +23,7 @@ import {NumberWrapper} from "angular2/src/facade/lang";
* }
* ```
*/
export interface Validator { validate(c: modelModule.AbstractControl): {[key: string]: any}; }
export interface Validator { validate(c: modelModule.Control): {[key: string]: any}; }
const REQUIRED_VALIDATOR =
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: Validators.required, multi: true}));
@ -46,11 +45,6 @@ const REQUIRED_VALIDATOR =
export class RequiredValidator {
}
export interface ValidatorFn { (c: AbstractControl): {[key: string]: any}; }
export interface AsyncValidatorFn {
(c: AbstractControl): any /*Promise<{[key: string]: any}>|Observable<{[key: string]: any}>*/;
}
/**
* Provivder which adds {@link MinLengthValidator} to {@link NG_VALIDATORS}.
*
@ -70,13 +64,13 @@ const MIN_LENGTH_VALIDATOR = CONST_EXPR(
providers: [MIN_LENGTH_VALIDATOR]
})
export class MinLengthValidator implements Validator {
private _validator: ValidatorFn;
private _validator: Function;
constructor(@Attribute("minlength") minLength: string) {
this._validator = Validators.minLength(NumberWrapper.parseInt(minLength, 10));
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
validate(c: Control): {[key: string]: any} { return this._validator(c); }
}
/**
@ -98,13 +92,13 @@ const MAX_LENGTH_VALIDATOR = CONST_EXPR(
providers: [MAX_LENGTH_VALIDATOR]
})
export class MaxLengthValidator implements Validator {
private _validator: ValidatorFn;
private _validator: Function;
constructor(@Attribute("maxlength") maxLength: string) {
this._validator = Validators.maxLength(NumberWrapper.parseInt(maxLength, 10));
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
validate(c: Control): {[key: string]: any} { return this._validator(c); }
}
@ -127,11 +121,11 @@ const PATTERN_VALIDATOR = CONST_EXPR(
providers: [PATTERN_VALIDATOR]
})
export class PatternValidator implements Validator {
private _validator: ValidatorFn;
private _validator: Function;
constructor(@Attribute("pattern") pattern: string) {
this._validator = Validators.pattern(pattern);
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
validate(c: Control): {[key: string]: any} { return this._validator(c); }
}

View File

@ -2,7 +2,6 @@ import {Injectable} from 'angular2/core';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isArray, CONST_EXPR, Type} from 'angular2/src/facade/lang';
import * as modelModule from './model';
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
/**
@ -57,18 +56,16 @@ export class FormBuilder {
group(controlsConfig: {[key: string]: any},
extra: {[key: string]: any} = null): modelModule.ControlGroup {
var controls = this._reduceControls(controlsConfig);
var optionals = <{[key: string]: boolean}>(
isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null);
var validator: ValidatorFn = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
var asyncValidator: AsyncValidatorFn =
isPresent(extra) ? StringMapWrapper.get(extra, "asyncValidator") : null;
var optionals = isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null;
var validator = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
var asyncValidator = isPresent(extra) ? StringMapWrapper.get(extra, "asyncValidator") : null;
return new modelModule.ControlGroup(controls, optionals, validator, asyncValidator);
}
/**
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
*/
control(value: Object, validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null): modelModule.Control {
control(value: Object, validator: Function = null,
asyncValidator: Function = null): modelModule.Control {
return new modelModule.Control(value, validator, asyncValidator);
}
@ -76,8 +73,8 @@ export class FormBuilder {
* Construct an array of {@link Control}s from the given `controlsConfig` array of
* configuration, with the given optional `validator` and `asyncValidator`.
*/
array(controlsConfig: any[], validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null): modelModule.ControlArray {
array(controlsConfig: any[], validator: Function = null,
asyncValidator: Function = null): modelModule.ControlArray {
var controls = controlsConfig.map(c => this._createControl(c));
return new modelModule.ControlArray(controls, validator, asyncValidator);
}
@ -101,8 +98,8 @@ export class FormBuilder {
} else if (isArray(controlConfig)) {
var value = controlConfig[0];
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
var validator = controlConfig.length > 1 ? controlConfig[1] : null;
var asyncValidator = controlConfig.length > 2 ? controlConfig[2] : null;
return this.control(value, validator, asyncValidator);
} else {

View File

@ -1,8 +1,7 @@
import {isPresent, isBlank, normalizeBool} from 'angular2/src/facade/lang';
import {StringWrapper, isPresent, isBlank, normalizeBool} from 'angular2/src/facade/lang';
import {Observable, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {PromiseWrapper} from 'angular2/src/facade/promise';
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
/**
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
@ -65,7 +64,7 @@ export abstract class AbstractControl {
private _parent: ControlGroup | ControlArray;
private _asyncValidationSubscription: any;
constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}
constructor(public validator: Function, public asyncValidator: Function) {}
get value(): any { return this._value; }
@ -138,17 +137,15 @@ export abstract class AbstractControl {
}
}
private _runValidator(): {[key: string]: any} {
return isPresent(this.validator) ? this.validator(this) : null;
}
private _runValidator() { return isPresent(this.validator) ? this.validator(this) : null; }
private _runAsyncValidator(emitEvent: boolean): void {
if (isPresent(this.asyncValidator)) {
this._status = PENDING;
this._cancelExistingSubscription();
var obs = toObservable(this.asyncValidator(this));
this._asyncValidationSubscription = ObservableWrapper.subscribe(
obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
this._asyncValidationSubscription =
ObservableWrapper.subscribe(obs, res => this.setErrors(res, {emitEvent: emitEvent}));
}
}
@ -271,8 +268,7 @@ export class Control extends AbstractControl {
/** @internal */
_onChange: Function;
constructor(value: any = null, validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null) {
constructor(value: any = null, validator: Function = null, asyncValidator: Function = null) {
super(validator, asyncValidator);
this._value = value;
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
@ -335,8 +331,8 @@ export class ControlGroup extends AbstractControl {
private _optionals: {[key: string]: boolean};
constructor(public controls: {[key: string]: AbstractControl},
optionals: {[key: string]: boolean} = null, validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null) {
optionals: {[key: string]: boolean} = null, validator: Function = null,
asyncValidator: Function = null) {
super(validator, asyncValidator);
this._optionals = isPresent(optionals) ? optionals : {};
this._initObservables();
@ -448,8 +444,8 @@ export class ControlGroup extends AbstractControl {
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
*/
export class ControlArray extends AbstractControl {
constructor(public controls: AbstractControl[], validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null) {
constructor(public controls: AbstractControl[], validator: Function = null,
asyncValidator: Function = null) {
super(validator, asyncValidator);
this._initObservables();
this._setParentForControls();

View File

@ -5,7 +5,6 @@ import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {OpaqueToken} from 'angular2/core';
import * as modelModule from './model';
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
/**
* Providers for validators to be used for {@link Control}s in a form.
@ -44,7 +43,7 @@ export class Validators {
/**
* Validator that requires controls to have a non-empty value.
*/
static required(control: modelModule.AbstractControl): {[key: string]: boolean} {
static required(control: modelModule.Control): {[key: string]: boolean} {
return isBlank(control.value) || (isString(control.value) && control.value == "") ?
{"required": true} :
null;
@ -53,8 +52,8 @@ export class Validators {
/**
* Validator that requires controls to have a value of a minimum length.
*/
static minLength(minLength: number): ValidatorFn {
return (control: modelModule.AbstractControl): {[key: string]: any} => {
static minLength(minLength: number): Function {
return (control: modelModule.Control): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length < minLength ?
@ -66,8 +65,8 @@ export class Validators {
/**
* Validator that requires controls to have a value of a maximum length.
*/
static maxLength(maxLength: number): ValidatorFn {
return (control: modelModule.AbstractControl): {[key: string]: any} => {
static maxLength(maxLength: number): Function {
return (control: modelModule.Control): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length > maxLength ?
@ -79,8 +78,8 @@ export class Validators {
/**
* Validator that requires a control to match a regex to its value.
*/
static pattern(pattern: string): ValidatorFn {
return (control: modelModule.AbstractControl): {[key: string]: any} => {
static pattern(pattern: string): Function {
return (control: modelModule.Control): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
let regex = new RegExp(`^${pattern}$`);
let v: string = control.value;
@ -92,13 +91,13 @@ export class Validators {
/**
* No-op validator.
*/
static nullValidator(c: modelModule.AbstractControl): {[key: string]: boolean} { return null; }
static nullValidator(c: any): {[key: string]: boolean} { return null; }
/**
* Compose multiple validators into a single function that returns the union
* of the individual error maps.
*/
static compose(validators: ValidatorFn[]): ValidatorFn {
static compose(validators: Function[]): Function {
if (isBlank(validators)) return null;
var presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
@ -108,13 +107,13 @@ export class Validators {
};
}
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
static composeAsync(validators: Function[]): Function {
if (isBlank(validators)) return null;
var presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
return function(control: modelModule.AbstractControl) {
let promises = _executeAsyncValidators(control, presentValidators).map(_convertToPromise);
let promises = _executeValidators(control, presentValidators).map(_convertToPromise);
return PromiseWrapper.all(promises).then(_mergeErrors);
};
}
@ -124,20 +123,13 @@ function _convertToPromise(obj: any): any {
return PromiseWrapper.isPromise(obj) ? obj : ObservableWrapper.toPromise(obj);
}
function _executeValidators(control: modelModule.AbstractControl,
validators: ValidatorFn[]): any[] {
return validators.map(v => v(control));
}
function _executeAsyncValidators(control: modelModule.AbstractControl,
validators: AsyncValidatorFn[]): any[] {
function _executeValidators(control: modelModule.AbstractControl, validators: Function[]): any[] {
return validators.map(v => v(control));
}
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
var res: {[key: string]: any} =
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
}, {});
var res = arrayOfErrors.reduce((res, errors) => {
return isPresent(errors) ? StringMapWrapper.merge(<any>res, <any>errors) : res;
}, {});
return StringMapWrapper.isEmpty(res) ? null : res;
}

View File

@ -22,7 +22,7 @@ class ObservableStrategy {
}
class PromiseStrategy {
createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): any {
createSubscription(async: any, updateLatestValue: any): any {
return async.then(updateLatestValue);
}

View File

@ -20,7 +20,7 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
* application.
*
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
* property of the `@Component` decorator.
* property of the `@Component` or `@View` decorators.
*/
export const COMMON_PIPES = CONST_EXPR([
AsyncPipe,

View File

@ -48,7 +48,7 @@ export class I18nPluralPipe implements PipeTransform {
transform(value: number, args: any[] = null): string {
var key: string;
var valueStr: string;
var pluralMap: {[count: string]: string} = <{[count: string]: string}>(args[0]);
var pluralMap: {[count: string]: string} = args[0];
if (!isStringMap(pluralMap)) {
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);

View File

@ -37,7 +37,7 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
@Injectable()
export class I18nSelectPipe implements PipeTransform {
transform(value: string, args: any[] = null): string {
var mapping: {[key: string]: string} = <{[count: string]: string}>(args[0]);
var mapping: {[key: string]: string} = args[0];
if (!isStringMap(mapping)) {
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
}

View File

@ -146,9 +146,8 @@ class ProtoViewVisitor implements TemplateAstVisitor {
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
var directiveMetadata = ast.directive;
var outputsArray = [];
StringMapWrapper.forEach(
ast.directive.outputs,
(eventName: string, dirProperty: string) => outputsArray.push([dirProperty, eventName]));
StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
[dirProperty, eventName]));
var directiveRecord = new DirectiveRecord({
directiveIndex: directiveIndex,
callAfterContentInit:

View File

@ -1,64 +0,0 @@
export const $EOF = 0;
export const $TAB = 9;
export const $LF = 10;
export const $VTAB = 11;
export const $FF = 12;
export const $CR = 13;
export const $SPACE = 32;
export const $BANG = 33;
export const $DQ = 34;
export const $HASH = 35;
export const $$ = 36;
export const $PERCENT = 37;
export const $AMPERSAND = 38;
export const $SQ = 39;
export const $LPAREN = 40;
export const $RPAREN = 41;
export const $STAR = 42;
export const $PLUS = 43;
export const $COMMA = 44;
export const $MINUS = 45;
export const $PERIOD = 46;
export const $SLASH = 47;
export const $COLON = 58;
export const $SEMICOLON = 59;
export const $LT = 60;
export const $EQ = 61;
export const $GT = 62;
export const $QUESTION = 63;
export const $0 = 48;
export const $9 = 57;
export const $A = 65;
export const $E = 69;
export const $Z = 90;
export const $LBRACKET = 91;
export const $BACKSLASH = 92;
export const $RBRACKET = 93;
export const $CARET = 94;
export const $_ = 95;
export const $a = 97;
export const $e = 101;
export const $f = 102;
export const $n = 110;
export const $r = 114;
export const $t = 116;
export const $u = 117;
export const $v = 118;
export const $z = 122;
export const $LBRACE = 123;
export const $BAR = 124;
export const $RBRACE = 125;
export const $NBSP = 160;
export const $PIPE = 124;
export const $TILDA = 126;
export const $AT = 64;
export function isWhitespace(code: number): boolean {
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
}

View File

@ -1,751 +0,0 @@
import {NumberWrapper, StringWrapper, isPresent, resolveEnumToken} from "angular2/src/facade/lang";
import {BaseException} from 'angular2/src/facade/exceptions';
import {
isWhitespace,
$EOF,
$HASH,
$TILDA,
$CARET,
$PERCENT,
$$,
$_,
$COLON,
$SQ,
$DQ,
$EQ,
$SLASH,
$BACKSLASH,
$PERIOD,
$STAR,
$PLUS,
$LPAREN,
$RPAREN,
$LBRACE,
$RBRACE,
$LBRACKET,
$RBRACKET,
$PIPE,
$COMMA,
$SEMICOLON,
$MINUS,
$BANG,
$QUESTION,
$AT,
$AMPERSAND,
$GT,
$a,
$A,
$z,
$Z,
$0,
$9,
$FF,
$CR,
$LF,
$VTAB
} from "angular2/src/compiler/chars";
export {
$EOF,
$AT,
$RBRACE,
$LBRACE,
$LBRACKET,
$RBRACKET,
$LPAREN,
$RPAREN,
$COMMA,
$COLON,
$SEMICOLON,
isWhitespace
} from "angular2/src/compiler/chars";
export enum CssTokenType {
EOF,
String,
Comment,
Identifier,
Number,
IdentifierOrNumber,
AtKeyword,
Character,
Whitespace,
Invalid
}
export enum CssLexerMode {
ALL,
ALL_TRACK_WS,
SELECTOR,
PSEUDO_SELECTOR,
ATTRIBUTE_SELECTOR,
AT_RULE_QUERY,
MEDIA_QUERY,
BLOCK,
KEYFRAME_BLOCK,
STYLE_BLOCK,
STYLE_VALUE,
STYLE_VALUE_FUNCTION,
STYLE_CALC_FUNCTION
}
export class LexedCssResult {
constructor(public error: CssScannerError, public token: CssToken) {}
}
export function generateErrorMessage(input, message, errorValue, index, row, column) {
return `${message} at column ${row}:${column} in expression [` +
findProblemCode(input, errorValue, index, column) + ']';
}
export function findProblemCode(input, errorValue, index, column) {
var endOfProblemLine = index;
var current = charCode(input, index);
while (current > 0 && !isNewline(current)) {
current = charCode(input, ++endOfProblemLine);
}
var choppedString = input.substring(0, endOfProblemLine);
var pointerPadding = "";
for (var i = 0; i < column; i++) {
pointerPadding += " ";
}
var pointerString = "";
for (var i = 0; i < errorValue.length; i++) {
pointerString += "^";
}
return choppedString + "\n" + pointerPadding + pointerString + "\n";
}
export class CssToken {
numValue: number;
constructor(public index: number, public column: number, public line: number,
public type: CssTokenType, public strValue: string) {
this.numValue = charCode(strValue, 0);
}
}
export class CssLexer {
scan(text: string, trackComments: boolean = false): CssScanner {
return new CssScanner(text, trackComments);
}
}
export class CssScannerError extends BaseException {
public rawMessage: string;
public message: string;
constructor(public token: CssToken, message) {
super('Css Parse Error: ' + message);
this.rawMessage = message;
}
toString(): string { return this.message; }
}
function _trackWhitespace(mode: CssLexerMode) {
switch (mode) {
case CssLexerMode.SELECTOR:
case CssLexerMode.ALL_TRACK_WS:
case CssLexerMode.STYLE_VALUE:
return true;
default:
return false;
}
}
export class CssScanner {
peek: number;
peekPeek: number;
length: number = 0;
index: number = -1;
column: number = -1;
line: number = 0;
_currentMode: CssLexerMode = CssLexerMode.BLOCK;
_currentError: CssScannerError = null;
constructor(public input: string, private _trackComments: boolean = false) {
this.length = this.input.length;
this.peekPeek = this.peekAt(0);
this.advance();
}
getMode(): CssLexerMode { return this._currentMode; }
setMode(mode: CssLexerMode) {
if (this._currentMode != mode) {
if (_trackWhitespace(this._currentMode)) {
this.consumeWhitespace();
}
this._currentMode = mode;
}
}
advance(): void {
if (isNewline(this.peek)) {
this.column = 0;
this.line++;
} else {
this.column++;
}
this.index++;
this.peek = this.peekPeek;
this.peekPeek = this.peekAt(this.index + 1);
}
peekAt(index): number {
return index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, index);
}
consumeEmptyStatements(): void {
this.consumeWhitespace();
while (this.peek == $SEMICOLON) {
this.advance();
this.consumeWhitespace();
}
}
consumeWhitespace(): void {
while (isWhitespace(this.peek) || isNewline(this.peek)) {
this.advance();
if (!this._trackComments && isCommentStart(this.peek, this.peekPeek)) {
this.advance(); // /
this.advance(); // *
while (!isCommentEnd(this.peek, this.peekPeek)) {
if (this.peek == $EOF) {
this.error('Unterminated comment');
}
this.advance();
}
this.advance(); // *
this.advance(); // /
}
}
}
consume(type: CssTokenType, value: string = null): LexedCssResult {
var mode = this._currentMode;
this.setMode(CssLexerMode.ALL);
var previousIndex = this.index;
var previousLine = this.line;
var previousColumn = this.column;
var output = this.scan();
// just incase the inner scan method returned an error
if (isPresent(output.error)) {
this.setMode(mode);
return output;
}
var next = output.token;
if (!isPresent(next)) {
next = new CssToken(0, 0, 0, CssTokenType.EOF, "end of file");
}
var isMatchingType;
if (type == CssTokenType.IdentifierOrNumber) {
// TODO (matsko): implement array traversal for lookup here
isMatchingType = next.type == CssTokenType.Number || next.type == CssTokenType.Identifier;
} else {
isMatchingType = next.type == type;
}
// before throwing the error we need to bring back the former
// mode so that the parser can recover...
this.setMode(mode);
var error = null;
if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
var errorMessage = resolveEnumToken(CssTokenType, next.type) + " does not match expected " +
resolveEnumToken(CssTokenType, type) + " value";
if (isPresent(value)) {
errorMessage += ' ("' + next.strValue + '" should match "' + value + '")';
}
error = new CssScannerError(
next, generateErrorMessage(this.input, errorMessage, next.strValue, previousIndex,
previousLine, previousColumn));
}
return new LexedCssResult(error, next);
}
scan(): LexedCssResult {
var trackWS = _trackWhitespace(this._currentMode);
if (this.index == 0 && !trackWS) { // first scan
this.consumeWhitespace();
}
var token = this._scan();
if (token == null) return null;
var error = this._currentError;
this._currentError = null;
if (!trackWS) {
this.consumeWhitespace();
}
return new LexedCssResult(error, token);
}
_scan(): CssToken {
var peek = this.peek;
var peekPeek = this.peekPeek;
if (peek == $EOF) return null;
if (isCommentStart(peek, peekPeek)) {
// even if comments are not tracked we still lex the
// comment so we can move the pointer forward
var commentToken = this.scanComment();
if (this._trackComments) {
return commentToken;
}
}
if (_trackWhitespace(this._currentMode) && (isWhitespace(peek) || isNewline(peek))) {
return this.scanWhitespace();
}
peek = this.peek;
peekPeek = this.peekPeek;
if (peek == $EOF) return null;
if (isStringStart(peek, peekPeek)) {
return this.scanString();
}
// something like url(cool)
if (this._currentMode == CssLexerMode.STYLE_VALUE_FUNCTION) {
return this.scanCssValueFunction();
}
var isModifier = peek == $PLUS || peek == $MINUS;
var digitA = isModifier ? false : isDigit(peek);
var digitB = isDigit(peekPeek);
if (digitA || (isModifier && (peekPeek == $PERIOD || digitB)) || (peek == $PERIOD && digitB)) {
return this.scanNumber();
}
if (peek == $AT) {
return this.scanAtExpression();
}
if (isIdentifierStart(peek, peekPeek)) {
return this.scanIdentifier();
}
if (isValidCssCharacter(peek, this._currentMode)) {
return this.scanCharacter();
}
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
}
scanComment() {
if (this.assertCondition(isCommentStart(this.peek, this.peekPeek),
"Expected comment start value")) {
return null;
}
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
this.advance(); // /
this.advance(); // *
while (!isCommentEnd(this.peek, this.peekPeek)) {
if (this.peek == $EOF) {
this.error('Unterminated comment');
}
this.advance();
}
this.advance(); // *
this.advance(); // /
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str);
}
scanWhitespace() {
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
while (isWhitespace(this.peek) && this.peek != $EOF) {
this.advance();
}
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str);
}
scanString() {
if (this.assertCondition(isStringStart(this.peek, this.peekPeek),
"Unexpected non-string starting value")) {
return null;
}
var target = this.peek;
var start = this.index;
var startingColumn = this.column;
var startingLine = this.line;
var previous = target;
this.advance();
while (!isCharMatch(target, previous, this.peek)) {
if (this.peek == $EOF || isNewline(this.peek)) {
this.error('Unterminated quote');
}
previous = this.peek;
this.advance();
}
if (this.assertCondition(this.peek == target, "Unterminated quote")) {
return null;
}
this.advance();
var str = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str);
}
scanNumber() {
var start = this.index;
var startingColumn = this.column;
if (this.peek == $PLUS || this.peek == $MINUS) {
this.advance();
}
var periodUsed = false;
while (isDigit(this.peek) || this.peek == $PERIOD) {
if (this.peek == $PERIOD) {
if (periodUsed) {
this.error('Unexpected use of a second period value');
}
periodUsed = true;
}
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue);
}
scanIdentifier() {
if (this.assertCondition(isIdentifierStart(this.peek, this.peekPeek),
'Expected identifier starting value')) {
return null;
}
var start = this.index;
var startingColumn = this.column;
while (isIdentifierPart(this.peek)) {
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
}
scanCssValueFunction() {
var start = this.index;
var startingColumn = this.column;
while (this.peek != $EOF && this.peek != $RPAREN) {
this.advance();
}
var strValue = this.input.substring(start, this.index);
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
}
scanCharacter() {
var start = this.index;
var startingColumn = this.column;
if (this.assertCondition(isValidCssCharacter(this.peek, this._currentMode),
charStr(this.peek) + ' is not a valid CSS character')) {
return null;
}
var c = this.input.substring(start, start + 1);
this.advance();
return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c);
}
scanAtExpression() {
if (this.assertCondition(this.peek == $AT, 'Expected @ value')) {
return null;
}
var start = this.index;
var startingColumn = this.column;
this.advance();
if (isIdentifierStart(this.peek, this.peekPeek)) {
var ident = this.scanIdentifier();
var strValue = '@' + ident.strValue;
return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue);
} else {
return this.scanCharacter();
}
}
assertCondition(status: boolean, errorMessage: string): boolean {
if (!status) {
this.error(errorMessage);
return true;
}
return false;
}
error(message: string, errorTokenValue: string = null, doNotAdvance: boolean = false): CssToken {
var index: number = this.index;
var column: number = this.column;
var line: number = this.line;
errorTokenValue =
isPresent(errorTokenValue) ? errorTokenValue : StringWrapper.fromCharCode(this.peek);
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
var errorMessage =
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
if (!doNotAdvance) {
this.advance();
}
this._currentError = new CssScannerError(invalidToken, errorMessage);
return invalidToken;
}
}
function isAtKeyword(current: CssToken, next: CssToken): boolean {
return current.numValue == $AT && next.type == CssTokenType.Identifier;
}
function isCharMatch(target: number, previous: number, code: number) {
return code == target && previous != $BACKSLASH;
}
function isDigit(code: number): boolean {
return $0 <= code && code <= $9;
}
function isCommentStart(code: number, next: number) {
return code == $SLASH && next == $STAR;
}
function isCommentEnd(code: number, next: number) {
return code == $STAR && next == $SLASH;
}
function isStringStart(code: number, next: number): boolean {
var target = code;
if (target == $BACKSLASH) {
target = next;
}
return target == $DQ || target == $SQ;
}
function isIdentifierStart(code: number, next: number): boolean {
var target = code;
if (target == $MINUS) {
target = next;
}
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
target == $MINUS || target == $_;
}
function isIdentifierPart(target: number) {
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
target == $MINUS || target == $_ || isDigit(target);
}
function isValidPseudoSelectorCharacter(code: number) {
switch (code) {
case $LPAREN:
case $RPAREN:
return true;
default:
return false;
}
}
function isValidKeyframeBlockCharacter(code: number) {
return code == $PERCENT;
}
function isValidAttributeSelectorCharacter(code: number) {
// value^*|$~=something
switch (code) {
case $$:
case $PIPE:
case $CARET:
case $TILDA:
case $STAR:
case $EQ:
return true;
default:
return false;
}
}
function isValidSelectorCharacter(code: number) {
// selector [ key = value ]
// IDENT C IDENT C IDENT C
// #id, .class, *+~>
// tag:PSEUDO
switch (code) {
case $HASH:
case $PERIOD:
case $TILDA:
case $STAR:
case $PLUS:
case $GT:
case $COLON:
case $PIPE:
case $COMMA:
return true;
default:
return false;
}
}
function isValidStyleBlockCharacter(code: number) {
// key:value;
// key:calc(something ... )
switch (code) {
case $HASH:
case $SEMICOLON:
case $COLON:
case $PERCENT:
case $SLASH:
case $BACKSLASH:
case $BANG:
case $PERIOD:
case $LPAREN:
case $RPAREN:
return true;
default:
return false;
}
}
function isValidMediaQueryRuleCharacter(code: number) {
// (min-width: 7.5em) and (orientation: landscape)
switch (code) {
case $LPAREN:
case $RPAREN:
case $COLON:
case $PERCENT:
case $PERIOD:
return true;
default:
return false;
}
}
function isValidAtRuleCharacter(code: number) {
// @document url(http://www.w3.org/page?something=on#hash),
switch (code) {
case $LPAREN:
case $RPAREN:
case $COLON:
case $PERCENT:
case $PERIOD:
case $SLASH:
case $BACKSLASH:
case $HASH:
case $EQ:
case $QUESTION:
case $AMPERSAND:
case $STAR:
case $COMMA:
case $MINUS:
case $PLUS:
return true;
default:
return false;
}
}
function isValidStyleFunctionCharacter(code: number) {
switch (code) {
case $PERIOD:
case $MINUS:
case $PLUS:
case $STAR:
case $SLASH:
case $LPAREN:
case $RPAREN:
case $COMMA:
return true;
default:
return false;
}
}
function isValidBlockCharacter(code: number) {
// @something { }
// IDENT
return code == $AT;
}
function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
switch (mode) {
case CssLexerMode.ALL:
case CssLexerMode.ALL_TRACK_WS:
return true;
case CssLexerMode.SELECTOR:
return isValidSelectorCharacter(code);
case CssLexerMode.PSEUDO_SELECTOR:
return isValidPseudoSelectorCharacter(code);
case CssLexerMode.ATTRIBUTE_SELECTOR:
return isValidAttributeSelectorCharacter(code);
case CssLexerMode.MEDIA_QUERY:
return isValidMediaQueryRuleCharacter(code);
case CssLexerMode.AT_RULE_QUERY:
return isValidAtRuleCharacter(code);
case CssLexerMode.KEYFRAME_BLOCK:
return isValidKeyframeBlockCharacter(code);
case CssLexerMode.STYLE_BLOCK:
case CssLexerMode.STYLE_VALUE:
return isValidStyleBlockCharacter(code);
case CssLexerMode.STYLE_CALC_FUNCTION:
return isValidStyleFunctionCharacter(code);
case CssLexerMode.BLOCK:
return isValidBlockCharacter(code);
default:
return false;
}
}
function charCode(input, index): number {
return index >= input.length ? $EOF : StringWrapper.charCodeAt(input, index);
}
function charStr(code: number): string {
return StringWrapper.fromCharCode(code);
}
export function isNewline(code): boolean {
switch (code) {
case $FF:
case $CR:
case $LF:
case $VTAB:
return true;
default:
return false;
}
}

View File

@ -1,721 +0,0 @@
import {
ParseSourceSpan,
ParseSourceFile,
ParseLocation,
ParseError
} from "angular2/src/compiler/parse_util";
import {
bitWiseOr,
bitWiseAnd,
NumberWrapper,
StringWrapper,
isPresent
} from "angular2/src/facade/lang";
import {
CssLexerMode,
CssToken,
CssTokenType,
CssScanner,
CssScannerError,
generateErrorMessage,
$AT,
$EOF,
$RBRACE,
$LBRACE,
$LBRACKET,
$RBRACKET,
$LPAREN,
$RPAREN,
$COMMA,
$COLON,
$SEMICOLON,
isNewline
} from "angular2/src/compiler/css/lexer";
export {CssToken} from "angular2/src/compiler/css/lexer";
export enum BlockType {
Import,
Charset,
Namespace,
Supports,
Keyframes,
MediaQuery,
Selector,
FontFace,
Page,
Document,
Viewport,
Unsupported
}
const EOF_DELIM = 1;
const RBRACE_DELIM = 2;
const LBRACE_DELIM = 4;
const COMMA_DELIM = 8;
const COLON_DELIM = 16;
const SEMICOLON_DELIM = 32;
const NEWLINE_DELIM = 64;
const RPAREN_DELIM = 128;
function mergeTokens(tokens: CssToken[], separator: string = ""): CssToken {
var mainToken = tokens[0];
var str = mainToken.strValue;
for (var i = 1; i < tokens.length; i++) {
str += separator + tokens[i].strValue;
}
return new CssToken(mainToken.index, mainToken.column, mainToken.line, mainToken.type, str);
}
function getDelimFromToken(token: CssToken): number {
return getDelimFromCharacter(token.numValue);
}
function getDelimFromCharacter(code: number): number {
switch (code) {
case $EOF:
return EOF_DELIM;
case $COMMA:
return COMMA_DELIM;
case $COLON:
return COLON_DELIM;
case $SEMICOLON:
return SEMICOLON_DELIM;
case $RBRACE:
return RBRACE_DELIM;
case $LBRACE:
return LBRACE_DELIM;
case $RPAREN:
return RPAREN_DELIM;
default:
return isNewline(code) ? NEWLINE_DELIM : 0;
}
}
function characterContainsDelimiter(code: number, delimiters: number) {
return bitWiseAnd([getDelimFromCharacter(code), delimiters]) > 0;
}
export class CssAST {
visit(visitor: CssASTVisitor, context?: any): void {}
}
export interface CssASTVisitor {
visitCssValue(ast: CssStyleValueAST, context?: any): void;
visitInlineCssRule(ast: CssInlineRuleAST, context?: any): void;
visitCssKeyframeRule(ast: CssKeyframeRuleAST, context?: any): void;
visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAST, context?: any): void;
visitCssMediaQueryRule(ast: CssMediaQueryRuleAST, context?: any): void;
visitCssSelectorRule(ast: CssSelectorRuleAST, context?: any): void;
visitCssSelector(ast: CssSelectorAST, context?: any): void;
visitCssDefinition(ast: CssDefinitionAST, context?: any): void;
visitCssBlock(ast: CssBlockAST, context?: any): void;
visitCssStyleSheet(ast: CssStyleSheetAST, context?: any): void;
visitUnkownRule(ast: CssUnknownTokenListAST, context?: any): void;
}
export class ParsedCssResult {
constructor(public errors: CssParseError[], public ast: CssStyleSheetAST) {}
}
export class CssParser {
private _errors: CssParseError[] = [];
private _file: ParseSourceFile;
constructor(private _scanner: CssScanner, private _fileName: string) {
this._file = new ParseSourceFile(this._scanner.input, _fileName);
}
_resolveBlockType(token: CssToken): BlockType {
switch (token.strValue) {
case '@-o-keyframes':
case '@-moz-keyframes':
case '@-webkit-keyframes':
case '@keyframes':
return BlockType.Keyframes;
case '@charset':
return BlockType.Charset;
case '@import':
return BlockType.Import;
case '@namespace':
return BlockType.Namespace;
case '@page':
return BlockType.Page;
case '@document':
return BlockType.Document;
case '@media':
return BlockType.MediaQuery;
case '@font-face':
return BlockType.FontFace;
case '@viewport':
return BlockType.Viewport;
case '@supports':
return BlockType.Supports;
default:
return BlockType.Unsupported;
}
}
parse(): ParsedCssResult {
var delimiters: number = EOF_DELIM;
var ast = this._parseStyleSheet(delimiters);
var errors = this._errors;
this._errors = [];
return new ParsedCssResult(errors, ast);
}
_parseStyleSheet(delimiters): CssStyleSheetAST {
var results = [];
this._scanner.consumeEmptyStatements();
while (this._scanner.peek != $EOF) {
this._scanner.setMode(CssLexerMode.BLOCK);
results.push(this._parseRule(delimiters));
}
return new CssStyleSheetAST(results);
}
_parseRule(delimiters: number): CssRuleAST {
if (this._scanner.peek == $AT) {
return this._parseAtRule(delimiters);
}
return this._parseSelectorRule(delimiters);
}
_parseAtRule(delimiters: number): CssRuleAST {
this._scanner.setMode(CssLexerMode.BLOCK);
var token = this._scan();
this._assertCondition(token.type == CssTokenType.AtKeyword,
`The CSS Rule ${token.strValue} is not a valid [@] rule.`, token);
var block, type = this._resolveBlockType(token);
switch (type) {
case BlockType.Charset:
case BlockType.Namespace:
case BlockType.Import:
var value = this._parseValue(delimiters);
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssInlineRuleAST(type, value);
case BlockType.Viewport:
case BlockType.FontFace:
block = this._parseStyleBlock(delimiters);
return new CssBlockRuleAST(type, block);
case BlockType.Keyframes:
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
// keyframes only have one identifier name
var name = tokens[0];
return new CssKeyframeRuleAST(name, this._parseKeyframeBlock(delimiters));
case BlockType.MediaQuery:
this._scanner.setMode(CssLexerMode.MEDIA_QUERY);
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
return new CssMediaQueryRuleAST(tokens, this._parseBlock(delimiters));
case BlockType.Document:
case BlockType.Supports:
case BlockType.Page:
this._scanner.setMode(CssLexerMode.AT_RULE_QUERY);
var tokens = this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]));
return new CssBlockDefinitionRuleAST(type, tokens, this._parseBlock(delimiters));
// if a custom @rule { ... } is used it should still tokenize the insides
default:
var listOfTokens = [];
this._scanner.setMode(CssLexerMode.ALL);
this._error(generateErrorMessage(
this._scanner.input,
`The CSS "at" rule "${token.strValue}" is not allowed to used here`,
token.strValue, token.index, token.line, token.column),
token);
this._collectUntilDelim(bitWiseOr([delimiters, LBRACE_DELIM, SEMICOLON_DELIM]))
.forEach((token) => { listOfTokens.push(token); });
if (this._scanner.peek == $LBRACE) {
this._consume(CssTokenType.Character, '{');
this._collectUntilDelim(bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]))
.forEach((token) => { listOfTokens.push(token); });
this._consume(CssTokenType.Character, '}');
}
return new CssUnknownTokenListAST(token, listOfTokens);
}
}
_parseSelectorRule(delimiters: number): CssSelectorRuleAST {
var selectors = this._parseSelectors(delimiters);
var block = this._parseStyleBlock(delimiters);
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssSelectorRuleAST(selectors, block);
}
_parseSelectors(delimiters: number): CssSelectorAST[] {
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
var selectors = [];
var isParsingSelectors = true;
while (isParsingSelectors) {
selectors.push(this._parseSelector(delimiters));
isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
if (isParsingSelectors) {
this._consume(CssTokenType.Character, ',');
isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters);
}
}
return selectors;
}
_scan(): CssToken {
var output = this._scanner.scan();
var token = output.token;
var error = output.error;
if (isPresent(error)) {
this._error(error.rawMessage, token);
}
return token;
}
_consume(type: CssTokenType, value: string = null): CssToken {
var output = this._scanner.consume(type, value);
var token = output.token;
var error = output.error;
if (isPresent(error)) {
this._error(error.rawMessage, token);
}
return token;
}
_parseKeyframeBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
this._consume(CssTokenType.Character, '{');
var definitions = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
definitions.push(this._parseKeyframeDefinition(delimiters));
}
this._consume(CssTokenType.Character, '}');
return new CssBlockAST(definitions);
}
_parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAST {
var stepTokens = [];
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
stepTokens.push(this._parseKeyframeLabel(bitWiseOr([delimiters, COMMA_DELIM])));
if (this._scanner.peek != $LBRACE) {
this._consume(CssTokenType.Character, ',');
}
}
var styles = this._parseStyleBlock(bitWiseOr([delimiters, RBRACE_DELIM]));
this._scanner.setMode(CssLexerMode.BLOCK);
return new CssKeyframeDefinitionAST(stepTokens, styles);
}
_parseKeyframeLabel(delimiters: number): CssToken {
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
return mergeTokens(this._collectUntilDelim(delimiters));
}
_parseSelector(delimiters: number): CssSelectorAST {
delimiters = bitWiseOr([delimiters, COMMA_DELIM, LBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.SELECTOR);
var selectorCssTokens = [];
var isComplex = false;
var wsCssToken;
var previousToken;
var parenCount = 0;
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var code = this._scanner.peek;
switch (code) {
case $LPAREN:
parenCount++;
break;
case $RPAREN:
parenCount--;
break;
case $COLON:
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR);
previousToken = this._consume(CssTokenType.Character, ':');
selectorCssTokens.push(previousToken);
continue;
case $LBRACKET:
// if we are already inside an attribute selector then we can't
// jump into the mode again. Therefore this error will get picked
// up when the scan method is called below.
if (this._scanner.getMode() != CssLexerMode.ATTRIBUTE_SELECTOR) {
selectorCssTokens.push(this._consume(CssTokenType.Character, '['));
this._scanner.setMode(CssLexerMode.ATTRIBUTE_SELECTOR);
continue;
}
break;
case $RBRACKET:
selectorCssTokens.push(this._consume(CssTokenType.Character, ']'));
this._scanner.setMode(CssLexerMode.SELECTOR);
continue;
}
var token = this._scan();
// special case for the ":not(" selector since it
// contains an inner selector that needs to be parsed
// in isolation
if (this._scanner.getMode() == CssLexerMode.PSEUDO_SELECTOR && isPresent(previousToken) &&
previousToken.numValue == $COLON && token.strValue == "not" &&
this._scanner.peek == $LPAREN) {
selectorCssTokens.push(token);
selectorCssTokens.push(this._consume(CssTokenType.Character, '('));
// the inner selector inside of :not(...) can only be one
// CSS selector (no commas allowed) therefore we parse only
// one selector by calling the method below
this._parseSelector(bitWiseOr([delimiters, RPAREN_DELIM]))
.tokens.forEach(
(innerSelectorToken) => { selectorCssTokens.push(innerSelectorToken); });
selectorCssTokens.push(this._consume(CssTokenType.Character, ')'));
continue;
}
previousToken = token;
if (token.type == CssTokenType.Whitespace) {
wsCssToken = token;
} else {
if (isPresent(wsCssToken)) {
selectorCssTokens.push(wsCssToken);
wsCssToken = null;
isComplex = true;
}
selectorCssTokens.push(token);
}
}
if (this._scanner.getMode() == CssLexerMode.ATTRIBUTE_SELECTOR) {
this._error(
`Unbalanced CSS attribute selector at column ${previousToken.line}:${previousToken.column}`,
previousToken);
} else if (parenCount > 0) {
this._error(
`Unbalanced pseudo selector function value at column ${previousToken.line}:${previousToken.column}`,
previousToken);
}
return new CssSelectorAST(selectorCssTokens, isComplex);
}
_parseValue(delimiters: number): CssStyleValueAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, SEMICOLON_DELIM, NEWLINE_DELIM]);
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
var strValue = "";
var tokens = [];
var previous: CssToken;
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var token;
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
this._scanner.peek == $LPAREN) {
token = this._consume(CssTokenType.Character, '(');
tokens.push(token);
strValue += token.strValue;
this._scanner.setMode(CssLexerMode.STYLE_VALUE_FUNCTION);
token = this._scan();
tokens.push(token);
strValue += token.strValue;
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
token = this._consume(CssTokenType.Character, ')');
tokens.push(token);
strValue += token.strValue;
} else {
token = this._scan();
if (token.type != CssTokenType.Whitespace) {
tokens.push(token);
}
strValue += token.strValue;
}
previous = token;
}
this._scanner.consumeWhitespace();
var code = this._scanner.peek;
if (code == $SEMICOLON) {
this._consume(CssTokenType.Character, ';');
} else if (code != $RBRACE) {
this._error(
generateErrorMessage(this._scanner.input,
`The CSS key/value definition did not end with a semicolon`,
previous.strValue, previous.index, previous.line, previous.column),
previous);
}
return new CssStyleValueAST(tokens, strValue);
}
_collectUntilDelim(delimiters: number, assertType: CssTokenType = null): CssToken[] {
var tokens = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
var val = isPresent(assertType) ? this._consume(assertType) : this._scan();
tokens.push(val);
}
return tokens;
}
_parseBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.BLOCK);
this._consume(CssTokenType.Character, '{');
this._scanner.consumeEmptyStatements();
var results = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
results.push(this._parseRule(delimiters));
}
this._consume(CssTokenType.Character, '}');
this._scanner.setMode(CssLexerMode.BLOCK);
this._scanner.consumeEmptyStatements();
return new CssBlockAST(results);
}
_parseStyleBlock(delimiters: number): CssBlockAST {
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]);
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
this._consume(CssTokenType.Character, '{');
this._scanner.consumeEmptyStatements();
var definitions = [];
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
definitions.push(this._parseDefinition(delimiters));
this._scanner.consumeEmptyStatements();
}
this._consume(CssTokenType.Character, '}');
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
this._scanner.consumeEmptyStatements();
return new CssBlockAST(definitions);
}
_parseDefinition(delimiters: number): CssDefinitionAST {
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
var prop = this._consume(CssTokenType.Identifier);
var parseValue, value = null;
// the colon value separates the prop from the style.
// there are a few cases as to what could happen if it
// is missing
switch (this._scanner.peek) {
case $COLON:
this._consume(CssTokenType.Character, ':');
parseValue = true;
break;
case $SEMICOLON:
case $RBRACE:
case $EOF:
parseValue = false;
break;
default:
var propStr = [prop.strValue];
if (this._scanner.peek != $COLON) {
// this will throw the error
var nextValue = this._consume(CssTokenType.Character, ':');
propStr.push(nextValue.strValue);
var remainingTokens = this._collectUntilDelim(
bitWiseOr([delimiters, COLON_DELIM, SEMICOLON_DELIM]), CssTokenType.Identifier);
if (remainingTokens.length > 0) {
remainingTokens.forEach((token) => { propStr.push(token.strValue); });
}
prop = new CssToken(prop.index, prop.column, prop.line, prop.type, propStr.join(" "));
}
// this means we've reached the end of the definition and/or block
if (this._scanner.peek == $COLON) {
this._consume(CssTokenType.Character, ':');
parseValue = true;
} else {
parseValue = false;
}
break;
}
if (parseValue) {
value = this._parseValue(delimiters);
} else {
this._error(generateErrorMessage(this._scanner.input,
`The CSS property was not paired with a style value`,
prop.strValue, prop.index, prop.line, prop.column),
prop);
}
return new CssDefinitionAST(prop, value);
}
_assertCondition(status: boolean, errorMessage: string, problemToken: CssToken): boolean {
if (!status) {
this._error(errorMessage, problemToken);
return true;
}
return false;
}
_error(message: string, problemToken: CssToken) {
var length = problemToken.strValue.length;
var error = CssParseError.create(this._file, 0, problemToken.line, problemToken.column, length,
message);
this._errors.push(error);
}
}
export class CssStyleValueAST extends CssAST {
constructor(public tokens: CssToken[], public strValue: string) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssValue(this); }
}
export class CssRuleAST extends CssAST {}
export class CssBlockRuleAST extends CssRuleAST {
constructor(public type: BlockType, public block: CssBlockAST, public name: CssToken = null) {
super();
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this.block, context); }
}
export class CssKeyframeRuleAST extends CssBlockRuleAST {
constructor(name: CssToken, block: CssBlockAST) { super(BlockType.Keyframes, block, name); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssKeyframeRule(this, context); }
}
export class CssKeyframeDefinitionAST extends CssBlockRuleAST {
public steps;
constructor(_steps: CssToken[], block: CssBlockAST) {
super(BlockType.Keyframes, block, mergeTokens(_steps, ","));
this.steps = _steps;
}
visit(visitor: CssASTVisitor, context?: any) {
visitor.visitCssKeyframeDefinition(this, context);
}
}
export class CssBlockDefinitionRuleAST extends CssBlockRuleAST {
public strValue: string;
constructor(type: BlockType, public query: CssToken[], block: CssBlockAST) {
super(type, block);
this.strValue = query.map(token => token.strValue).join("");
var firstCssToken: CssToken = query[0];
this.name = new CssToken(firstCssToken.index, firstCssToken.column, firstCssToken.line,
CssTokenType.Identifier, this.strValue);
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this.block, context); }
}
export class CssMediaQueryRuleAST extends CssBlockDefinitionRuleAST {
constructor(query: CssToken[], block: CssBlockAST) { super(BlockType.MediaQuery, query, block); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssMediaQueryRule(this, context); }
}
export class CssInlineRuleAST extends CssRuleAST {
constructor(public type: BlockType, public value: CssStyleValueAST) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitInlineCssRule(this, context); }
}
export class CssSelectorRuleAST extends CssBlockRuleAST {
public strValue: string;
constructor(public selectors: CssSelectorAST[], block: CssBlockAST) {
super(BlockType.Selector, block);
this.strValue = selectors.map(selector => selector.strValue).join(",");
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssSelectorRule(this, context); }
}
export class CssDefinitionAST extends CssAST {
constructor(public property: CssToken, public value: CssStyleValueAST) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssDefinition(this, context); }
}
export class CssSelectorAST extends CssAST {
public strValue;
constructor(public tokens: CssToken[], public isComplex: boolean = false) {
super();
this.strValue = tokens.map(token => token.strValue).join("");
}
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssSelector(this, context); }
}
export class CssBlockAST extends CssAST {
constructor(public entries: CssAST[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssBlock(this, context); }
}
export class CssStyleSheetAST extends CssAST {
constructor(public rules: CssAST[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitCssStyleSheet(this, context); }
}
export class CssParseError extends ParseError {
static create(file: ParseSourceFile, offset: number, line: number, col: number, length: number,
errMsg: string): CssParseError {
var start = new ParseLocation(file, offset, line, col);
var end = new ParseLocation(file, offset, line, col + length);
var span = new ParseSourceSpan(start, end);
return new CssParseError(span, "CSS Parse Error: " + errMsg);
}
constructor(span: ParseSourceSpan, message: string) { super(span, message); }
}
export class CssUnknownTokenListAST extends CssRuleAST {
constructor(public name, public tokens: CssToken[]) { super(); }
visit(visitor: CssASTVisitor, context?: any) { visitor.visitUnkownRule(this, context); }
}

View File

@ -31,7 +31,7 @@ export abstract class CompileMetadataWithIdentifier {
abstract toJson(): {[key: string]: any};
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
get identifier(): CompileIdentifierMetadata { return unimplemented(); }
}
export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier {
@ -41,9 +41,9 @@ export abstract class CompileMetadataWithType extends CompileMetadataWithIdentif
abstract toJson(): {[key: string]: any};
get type(): CompileTypeMetadata { return <CompileTypeMetadata>unimplemented(); }
get type(): CompileTypeMetadata { return unimplemented(); }
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
get identifier(): CompileIdentifierMetadata { return unimplemented(); }
}
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {

View File

@ -23,16 +23,10 @@ export class HtmlElementAst implements HtmlAst {
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
}
export class HtmlCommentAst implements HtmlAst {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitComment(this, context); }
}
export interface HtmlAstVisitor {
visitElement(ast: HtmlElementAst, context: any): any;
visitAttr(ast: HtmlAttrAst, context: any): any;
visitText(ast: HtmlTextAst, context: any): any;
visitComment(ast: HtmlCommentAst, context: any): any;
}
export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[], context: any = null): any[] {

View File

@ -11,7 +11,7 @@ import {
import {ListWrapper} from 'angular2/src/facade/collection';
import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlCommentAst, HtmlElementAst} from './html_ast';
import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
import {Injectable} from 'angular2/src/core/di';
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
@ -98,11 +98,9 @@ class TreeBuilder {
this._advanceIf(HtmlTokenType.CDATA_END);
}
private _consumeComment(token: HtmlToken) {
var text = this._advanceIf(HtmlTokenType.RAW_TEXT);
private _consumeComment(startToken: HtmlToken) {
this._advanceIf(HtmlTokenType.RAW_TEXT);
this._advanceIf(HtmlTokenType.COMMENT_END);
var value = isPresent(text) ? text.parts[0].trim() : null;
this._addToParent(new HtmlCommentAst(value, token.sourceSpan));
}
private _consumeText(token: HtmlToken) {

View File

@ -327,8 +327,6 @@ export class HtmlTagDefinition {
// see http://www.w3.org/TR/html51/syntax.html#optional-tags
// This implementation does not fully conform to the HTML5 spec.
var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
'base': new HtmlTagDefinition({isVoid: true}),
'meta': new HtmlTagDefinition({isVoid: true}),
'area': new HtmlTagDefinition({isVoid: true}),
'embed': new HtmlTagDefinition({isVoid: true}),
'link': new HtmlTagDefinition({isVoid: true}),

View File

@ -8,14 +8,7 @@ import {
isPresent
} from 'angular2/src/facade/lang';
import {
HtmlAstVisitor,
HtmlAttrAst,
HtmlElementAst,
HtmlTextAst,
HtmlCommentAst,
HtmlAst
} from './html_ast';
import {HtmlAstVisitor, HtmlAttrAst, HtmlElementAst, HtmlTextAst, HtmlAst} from './html_ast';
import {HtmlParser, HtmlParseTreeResult} from './html_parser';
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
@ -44,8 +37,6 @@ export class LegacyHtmlAstTransformer implements HtmlAstVisitor {
constructor(private dashCaseSelectors?: string[]) {}
visitComment(ast: HtmlCommentAst, context: any): any { return ast; }
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
this.visitingTemplateEl = ast.name.toLowerCase() == 'template';
let attrs = ast.attrs.map(attr => attr.visit(this, null));

View File

@ -234,7 +234,7 @@ class ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implement
attrAsts: TemplateAst[]): string[][] {
var attrs = visitAndReturnContext(this, attrAsts, {});
directives.forEach(directiveMeta => {
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value: string, name: string) => {
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
var prevValue = attrs[name];
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
});
@ -330,14 +330,12 @@ class ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implement
}
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
var entryArray: string[][] = [];
StringMapWrapper.forEach(data,
(value: string, name: string) => { entryArray.push([name, value]); });
var entryArray = [];
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
// We need to sort to get a defined output order
// for tests and for caching generated artifacts...
ListWrapper.sort<string[]>(entryArray, (entry1: string[], entry2: string[]) =>
StringWrapper.compare(entry1[0], entry2[0]));
var keyValueArray: string[][] = [];
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
var keyValueArray = [];
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
return keyValueArray;
}

View File

@ -343,16 +343,14 @@ export class ShadowCss {
strict: boolean): string {
var r = [], parts = selector.split(',');
for (var i = 0; i < parts.length; i++) {
var p = parts[i].trim();
var deepParts = StringWrapper.split(p, _shadowDeepSelectors);
var shallowPart = deepParts[0];
if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
deepParts[0] = strict && !StringWrapper.contains(shallowPart, _polyfillHostNoCombinator) ?
this._applyStrictSelectorScope(shallowPart, scopeSelector) :
this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
var p = parts[i];
p = p.trim();
if (this._selectorNeedsScoping(p, scopeSelector)) {
p = strict && !StringWrapper.contains(p, _polyfillHostNoCombinator) ?
this._applyStrictSelectorScope(p, scopeSelector) :
this._applySelectorScope(p, scopeSelector, hostSelector);
}
// replace /deep/ with a space for child selectors
r.push(deepParts.join(' '));
r.push(p);
}
return r.join(', ');
}
@ -436,16 +434,17 @@ var _cssColonHostRe = RegExpWrapper.create('(' + _polyfillHost + _parenSuffix, '
var _cssColonHostContextRe = RegExpWrapper.create('(' + _polyfillHostContext + _parenSuffix, 'im');
var _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
var _shadowDOMSelectorsRe = [
/>>>/g,
/::shadow/g,
/::content/g,
// Deprecated selectors
// TODO(vicb): see https://github.com/angular/clang-format/issues/16
// clang-format off
/\/deep\//g, // former >>>
/\/shadow-deep\//g, // former /deep/
/\/shadow\//g, // former ::shadow
// clanf-format on
];
var _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)/g;
var _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
var _polyfillHostRe = RegExpWrapper.create(_polyfillHost, 'im');
var _colonHostRe = /:host/gim;

View File

@ -53,9 +53,9 @@ export class StyleCompiler {
private _loadStyles(plainStyles: string[], absUrls: string[],
encapsulate: boolean): Promise<Array<string | any[]>> {
var promises: Promise<string[]>[] = absUrls.map((absUrl: string): Promise<string[]> => {
var promises = absUrls.map((absUrl) => {
var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`;
var result: Promise<string[]> = this._styleCache.get(cacheKey);
var result = this._styleCache.get(cacheKey);
if (isBlank(result)) {
result = this._xhr.get(absUrl).then((style) => {
var styleWithImports = extractStyleUrls(this._urlResolver, absUrl, style);
@ -66,7 +66,7 @@ export class StyleCompiler {
}
return result;
});
return PromiseWrapper.all<string[]>(promises).then((nestedStyles: string[][]) => {
return PromiseWrapper.all(promises).then((nestedStyles: string[][]) => {
var result: Array<string | any[]> =
plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
nestedStyles.forEach(styles => result.push(styles));

View File

@ -20,7 +20,6 @@ import {
HtmlTextAst,
HtmlAttrAst,
HtmlAst,
HtmlCommentAst,
htmlVisitAll
} from './html_ast';
import {HtmlParser} from './html_parser';
@ -127,7 +126,6 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
}
return null;
}
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
visitText(ast: HtmlTextAst, context: any): any { return null; }
}

View File

@ -41,7 +41,6 @@ import {
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
htmlVisitAll
} from './html_ast';
@ -210,8 +209,6 @@ class TemplateParseVisitor implements HtmlAstVisitor {
return new AttrAst(ast.name, ast.value, ast.sourceSpan);
}
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitElement(element: HtmlElementAst, component: Component): any {
var nodeName = element.name;
var preparsedElement = preparseElement(element);
@ -508,7 +505,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
sourceSpan: ParseSourceSpan,
targetPropertyAsts: BoundElementPropertyAst[]) {
if (isPresent(hostProps)) {
StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => {
StringMapWrapper.forEach(hostProps, (expression, propName) => {
var exprAst = this._parseBinding(expression, sourceSpan);
targetPropertyAsts.push(
this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan));
@ -520,7 +517,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
sourceSpan: ParseSourceSpan,
targetEventAsts: BoundEventAst[]) {
if (isPresent(hostListeners)) {
StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => {
StringMapWrapper.forEach(hostListeners, (expression, propName) => {
this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
});
}
@ -648,7 +645,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var allDirectiveEvents = new Set<string>();
directives.forEach(directive => {
StringMapWrapper.forEach(directive.directive.outputs,
(eventName: string, _) => { allDirectiveEvents.add(eventName); });
(eventName, _) => { allDirectiveEvents.add(eventName); });
});
events.forEach(event => {
if (isPresent(event.target) || !SetWrapper.has(allDirectiveEvents, event.name)) {
@ -679,7 +676,6 @@ class NonBindableVisitor implements HtmlAstVisitor {
return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children,
ngContentIndex, ast.sourceSpan);
}
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitAttr(ast: HtmlAttrAst, context: any): AttrAst {
return new AttrAst(ast.name, ast.value, ast.sourceSpan);
}

View File

@ -1,4 +1,4 @@
import {NgZone, NgZoneError} from 'angular2/src/core/zone/ng_zone';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {
Type,
isBlank,
@ -128,7 +128,7 @@ function _createPlatform(providers?: Array<Type | Provider | any[]>): PlatformRe
}
function _runPlatformInitializers(injector: Injector): void {
let inits: Function[] = <Function[]>injector.getOptional(PLATFORM_INITIALIZER);
let inits: Function[] = injector.getOptional(PLATFORM_INITIALIZER);
if (isPresent(inits)) inits.forEach(init => init());
}
@ -150,7 +150,7 @@ export abstract class PlatformRef {
* Retrieve the platform {@link Injector}, which is the parent injector for
* every Angular application on the page and provides singleton providers.
*/
get injector(): Injector { throw unimplemented(); };
get injector(): Injector { return unimplemented(); };
/**
* Instantiate a new Angular application on the page.
@ -222,7 +222,7 @@ export class PlatformRef_ extends PlatformRef {
asyncApplication(bindingFn: (zone: NgZone) => Promise<Array<Type | Provider | any[]>>,
additionalProviders?: Array<Type | Provider | any[]>): Promise<ApplicationRef> {
var zone = createNgZone();
var completer = PromiseWrapper.completer<ApplicationRef>();
var completer = PromiseWrapper.completer();
if (bindingFn === null) {
completer.resolve(this._initApp(zone, additionalProviders));
} else {
@ -254,9 +254,7 @@ export class PlatformRef_ extends PlatformRef {
try {
injector = this.injector.resolveAndCreateChild(providers);
exceptionHandler = injector.get(ExceptionHandler);
ObservableWrapper.subscribe(zone.onError, (error: NgZoneError) => {
exceptionHandler.call(error.error, error.stackTrace);
});
zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s));
} catch (e) {
if (isPresent(exceptionHandler)) {
exceptionHandler.call(e, e.stack);
@ -344,12 +342,12 @@ export abstract class ApplicationRef {
/**
* Retrieve the application {@link Injector}.
*/
get injector(): Injector { return <Injector>unimplemented(); };
get injector(): Injector { return unimplemented(); };
/**
* Retrieve the application {@link NgZone}.
*/
get zone(): NgZone { return <NgZone>unimplemented(); };
get zone(): NgZone { return unimplemented(); };
/**
* Dispose of this application and all of its components.
@ -371,7 +369,7 @@ export abstract class ApplicationRef {
/**
* Get a list of component types registered to this application.
*/
get componentTypes(): Type[] { return <Type[]>unimplemented(); };
get componentTypes(): Type[] { return unimplemented(); };
}
export class ApplicationRef_ extends ApplicationRef {
@ -396,7 +394,7 @@ export class ApplicationRef_ extends ApplicationRef {
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
super();
if (isPresent(this._zone)) {
ObservableWrapper.subscribe(this._zone.onMicrotaskEmpty,
ObservableWrapper.subscribe(this._zone.onTurnDone,
(_) => { this._zone.run(() => { this.tick(); }); });
}
this._enforceNoNewChanges = assertionsEnabled();
@ -436,22 +434,28 @@ export class ApplicationRef_ extends ApplicationRef {
var tickResult = PromiseWrapper.then(compRefToken, tick);
PromiseWrapper.then(tickResult, null, (err, stackTrace) => {
completer.reject(err, stackTrace);
exceptionHandler.call(err, stackTrace);
});
// THIS MUST ONLY RUN IN DART.
// This is required to report an error when no components with a matching selector found.
// Otherwise the promise will never be completed.
// Doing this in JS causes an extra error message to appear.
if (IS_DART) {
PromiseWrapper.then(tickResult, (_) => {});
}
PromiseWrapper.then(tickResult, null,
(err, stackTrace) => completer.reject(err, stackTrace));
} catch (e) {
exceptionHandler.call(e, e.stack);
completer.reject(e, e.stack);
}
});
return completer.promise.then<ComponentRef>((ref: ComponentRef) => {
return completer.promise.then(_ => {
let c = this._injector.get(Console);
if (assertionsEnabled()) {
c.log(
"Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.");
}
return ref;
return _;
});
}

View File

@ -73,8 +73,36 @@ export class SimpleChange {
isFirstChange(): boolean { return this.previousValue === ChangeDetectionUtil.uninitialized; }
}
var _simpleChangesIndex = 0;
var _simpleChanges = [
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null)
];
function _simpleChange(previousValue, currentValue): SimpleChange {
return new SimpleChange(previousValue, currentValue);
var index = _simpleChangesIndex++ % 20;
var s = _simpleChanges[index];
s.previousValue = previousValue;
s.currentValue = currentValue;
return s;
}
/* tslint:disable:requireParameterType */

View File

@ -153,6 +153,7 @@ export const $BAR = 124;
export const $RBRACE = 125;
const $NBSP = 160;
export class ScannerError extends BaseException {
constructor(public message) { super(); }

View File

@ -64,9 +64,8 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEachWithIndex(
definition.bindingRecords,
(b: BindingRecord, index: number) => recordBuilder.add(b, definition.variableNames, index));
ListWrapper.forEachWithIndex(definition.bindingRecords,
(b, index) => recordBuilder.add(b, definition.variableNames, index));
return coalesce(recordBuilder.records);
}

View File

@ -91,19 +91,19 @@ export class DebugElement extends DebugNode {
}
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
var matches: DebugElement[] = [];
var matches = [];
_queryElementChildren(this, predicate, matches);
return matches;
}
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
var matches: DebugNode[] = [];
var matches = [];
_queryNodeChildren(this, predicate, matches);
return matches;
}
get children(): DebugElement[] {
var children: DebugElement[] = [];
var children = [];
this.childNodes.forEach((node) => {
if (node instanceof DebugElement) {
children.push(node);

View File

@ -73,7 +73,7 @@ export class DebugDomRenderer implements Renderer {
if (isPresent(debugNode)) {
var debugParent = debugNode.parent;
if (viewRootNodes.length > 0 && isPresent(debugParent)) {
var debugViewRootNodes: DebugNode[] = [];
var debugViewRootNodes = [];
viewRootNodes.forEach((rootNode) => debugViewRootNodes.push(getDebugNode(rootNode)));
debugParent.insertChildrenAfter(debugNode, debugViewRootNodes);
}

View File

@ -513,7 +513,7 @@ export class ProviderBuilder {
*/
export function resolveFactory(provider: Provider): ResolvedFactory {
var factoryFn: Function;
var resolvedDeps: Dependency[];
var resolvedDeps;
if (isPresent(provider.useClass)) {
var useClass = resolveForwardRef(provider.useClass);
factoryFn = reflector.factory(useClass);
@ -619,7 +619,7 @@ function _constructDependencies(factoryFunction: Function, dependencies: any[]):
}
function _dependenciesFor(typeOrFunc): Dependency[] {
var params: any[][] = reflector.parameters(typeOrFunc);
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (params.some(isBlank)) {
throw new NoAnnotationError(typeOrFunc, params);

View File

@ -59,7 +59,7 @@ export abstract class ComponentRef {
*
* TODO(i): rename to destroy to be consistent with AppViewManager and ViewContainerRef
*/
abstract dispose(): void;
abstract dispose();
}
export class ComponentRef_ extends ComponentRef {
@ -84,7 +84,7 @@ export class ComponentRef_ extends ComponentRef {
*/
get hostComponentType(): Type { return this.componentType; }
dispose(): void { this._dispose(); }
dispose() { this._dispose(); }
}
/**

View File

@ -115,9 +115,8 @@ export class AppView implements ChangeDispatcher {
this.disposables = disposables;
this.appElements = appElements;
var localsMap = new Map<string, any>();
StringMapWrapper.forEach(
this.proto.templateVariableBindings,
(templateName: string, _: string) => { localsMap.set(templateName, null); });
StringMapWrapper.forEach(this.proto.templateVariableBindings,
(templateName, _) => { localsMap.set(templateName, null); });
for (var i = 0; i < appElements.length; i++) {
var appEl = appElements[i];
var providerTokens = [];
@ -126,14 +125,13 @@ export class AppView implements ChangeDispatcher {
providerTokens.push(appEl.proto.protoInjector.getProviderAtIndex(j).key.token);
}
}
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings,
(directiveIndex: number, name: string) => {
if (isBlank(directiveIndex)) {
localsMap.set(name, appEl.nativeElement);
} else {
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
}
});
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
if (isBlank(directiveIndex)) {
localsMap.set(name, appEl.nativeElement);
} else {
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
}
});
this.renderer.setElementDebugInfo(
appEl.nativeElement, new RenderDebugInfo(appEl.getInjector(), appEl.getComponent(),
providerTokens, localsMap));

View File

@ -41,7 +41,7 @@ export abstract class ViewContainerRef {
* Anchor element that specifies the location of this container in the containing View.
* <!-- TODO: rename to anchorElement -->
*/
get element(): ElementRef { return <ElementRef>unimplemented(); }
get element(): ElementRef { return unimplemented(); }
/**
* Destroys all Views in this container.
@ -60,7 +60,7 @@ export abstract class ViewContainerRef {
/**
* Returns the number of Views currently attached to this container.
*/
get length(): number { return <number>unimplemented(); };
get length(): number { return unimplemented(); };
/**
* Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it

View File

@ -6,9 +6,9 @@ export abstract class ViewRef {
/**
* @internal
*/
get changeDetectorRef(): ChangeDetectorRef { return <ChangeDetectorRef>unimplemented(); };
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); };
get destroyed(): boolean { return <boolean>unimplemented(); }
get destroyed(): boolean { return unimplemented(); }
}
/**
@ -21,7 +21,7 @@ export abstract class ViewRef {
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
*/
export abstract class HostViewRef extends ViewRef {
get rootNodes(): any[] { return <any[]>unimplemented(); };
get rootNodes(): any[] { return unimplemented(); };
}
/**
@ -88,7 +88,7 @@ export abstract class EmbeddedViewRef extends ViewRef {
*/
abstract hasLocal(variableName: string): boolean;
get rootNodes(): any[] { return <any[]>unimplemented(); };
get rootNodes(): any[] { return unimplemented(); };
}
export class ViewRef_ implements EmbeddedViewRef, HostViewRef {

View File

@ -45,7 +45,7 @@ export class ViewResolver {
if (isPresent(compMeta)) {
if (isBlank(compMeta.template) && isBlank(compMeta.templateUrl) && isBlank(viewMeta)) {
throw new BaseException(
`Component '${stringify(component)}' must have either 'template' or 'templateUrl' set.`);
`Component '${stringify(component)}' must have either 'template', 'templateUrl', or '@View' set.`);
} else if (isPresent(compMeta.template) && isPresent(viewMeta)) {
this._throwMixingViewAndComponent("template", component);
@ -84,8 +84,7 @@ export class ViewResolver {
}
} else {
if (isBlank(viewMeta)) {
throw new BaseException(
`Could not compile '${stringify(component)}' because it is not a component.`);
throw new BaseException(`No View decorator found on component '${stringify(component)}'`);
} else {
return viewMeta;
}

View File

@ -254,6 +254,7 @@ export interface ComponentFactory {
* import {Component, View} from "angular2/core";
*
* @Component({...})
* @View({...})
* class MyComponent {
* constructor() {
* ...
@ -480,7 +481,8 @@ export interface HostListenerFactory {
/**
* Declare reusable UI building blocks for an application.
*
* Each Angular component requires a single `@Component` annotation. The `@Component`
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
* `@Component`
* annotation specifies when a component is instantiated, and which properties and hostListeners it
* binds to.
*
@ -491,6 +493,8 @@ export interface HostListenerFactory {
*
* All template expressions and statements are then evaluated against the component instance.
*
* For details on the `@View` annotation, see {@link ViewMetadata}.
*
* ## Lifecycle hooks
*
* When the component class implements some {@link angular2/lifecycle_hooks} the callbacks are
@ -914,7 +918,8 @@ export var Directive: DirectiveFactory = <DirectiveFactory>makeDecorator(Directi
* }
* ```
*/
var View: ViewFactory = <ViewFactory>makeDecorator(ViewMetadata, (fn: any) => fn.View = View);
export var View: ViewFactory =
<ViewFactory>makeDecorator(ViewMetadata, (fn: any) => fn.View = View);
/**
* Specifies that a constant attribute value should be injected.
@ -969,7 +974,7 @@ export var Attribute: AttributeFactory = makeParamDecorator(AttributeMetadata);
* <ul>
* <li *ngFor="#pane of panes">{{pane.title}}</li>
* </ul>
* <ng-content></ng-content>
* <content></content>
* `
* })
* class Tabs {
@ -1149,8 +1154,8 @@ export var ViewChild: ViewChildFactory = makePropDecorator(ViewChildMetadata);
* ### Example ([live demo](http://plnkr.co/edit/eNsFHDf7YjyM6IzKxM1j?p=preview))
*
* ```javascript
* @Component({
* ...,
* @Component({...})
* @View({
* template: `
* <item> a </item>
* <item> b </item>

View File

@ -67,7 +67,7 @@ export class AttributeMetadata extends DependencyMetadata {
* <ul>
* <li *ngFor="#pane of panes">{{pane.title}}</li>
* </ul>
* <ng-content></ng-content>
* <content></content>
* `
* })
* class Tabs {
@ -242,8 +242,8 @@ export class ContentChildMetadata extends QueryMetadata {
* ### Example ([live demo](http://plnkr.co/edit/eNsFHDf7YjyM6IzKxM1j?p=preview))
*
* ```javascript
* @Component({
* ...,
* @Component({...})
* @View({
* template: `
* <item> a </item>
* <item> b </item>

View File

@ -1001,7 +1001,7 @@ export class PipeMetadata extends InjectableMetadata {
export class InputMetadata {
constructor(
/**
* Name used when instantiating a component in the template.
* Name used when instantiating a component in the temlate.
*/
public bindingPropertyName?: string) {}
}

View File

@ -87,7 +87,7 @@ export class Reflector {
}
}
parameters(typeOrFunc: /*Type*/ any): any[][] {
parameters(typeOrFunc: /*Type*/ any): any[] {
if (this._injectableInfo.has(typeOrFunc)) {
var res = this._getReflectionInfo(typeOrFunc).parameters;
return isPresent(res) ? res : [];
@ -148,7 +148,7 @@ export class Reflector {
}
/** @internal */
_getReflectionInfo(typeOrFunc: any): ReflectionInfo {
_getReflectionInfo(typeOrFunc: any) {
if (isPresent(this._usedKeys)) {
this._usedKeys.add(typeOrFunc);
}

View File

@ -1,9 +1,9 @@
import {Injectable} from 'angular2/src/core/di';
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {CONST, CONST_EXPR, scheduleMicroTask} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {CONST, CONST_EXPR} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {NgZone} from '../zone/ng_zone';
import {ObservableWrapper} from 'angular2/src/facade/async';
import {PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async';
/**
@ -15,7 +15,6 @@ import {ObservableWrapper} from 'angular2/src/facade/async';
export class Testability {
/** @internal */
_pendingCount: number = 0;
_isZoneStable: boolean = true;
/**
* Whether any work was done since the last 'whenStable' callback. This is
* useful to detect if this could have potentially destabilized another
@ -25,22 +24,23 @@ export class Testability {
_didWork: boolean = false;
/** @internal */
_callbacks: Function[] = [];
constructor(private _ngZone: NgZone) { this._watchAngularEvents(); }
/** @internal */
_isAngularEventPending: boolean = false;
constructor(_ngZone: NgZone) { this._watchAngularEvents(_ngZone); }
/** @internal */
_watchAngularEvents(): void {
ObservableWrapper.subscribe(this._ngZone.onUnstable, (_) => {
_watchAngularEvents(_ngZone: NgZone): void {
ObservableWrapper.subscribe(_ngZone.onTurnStart, (_) => {
this._didWork = true;
this._isZoneStable = false;
this._isAngularEventPending = true;
});
this._ngZone.runOutsideAngular(() => {
ObservableWrapper.subscribe(this._ngZone.onStable, (_) => {
NgZone.assertNotInAngularZone();
scheduleMicroTask(() => {
this._isZoneStable = true;
_ngZone.runOutsideAngular(() => {
ObservableWrapper.subscribe(_ngZone.onEventDone, (_) => {
if (!_ngZone.hasPendingTimers) {
this._isAngularEventPending = false;
this._runCallbacksIfReady();
});
}
});
});
}
@ -60,24 +60,22 @@ export class Testability {
return this._pendingCount;
}
isStable(): boolean {
return this._isZoneStable && this._pendingCount == 0 && !this._ngZone.hasPendingMacrotasks;
}
isStable(): boolean { return this._pendingCount == 0 && !this._isAngularEventPending; }
/** @internal */
_runCallbacksIfReady(): void {
if (this.isStable()) {
// Schedules the call backs in a new frame so that it is always async.
scheduleMicroTask(() => {
while (this._callbacks.length !== 0) {
(this._callbacks.pop())(this._didWork);
}
this._didWork = false;
});
} else {
// Not Ready
if (!this.isStable()) {
this._didWork = true;
return; // Not ready
}
// Schedules the call backs in a new frame so that it is always async.
PromiseWrapper.resolve(null).then((_) => {
while (this._callbacks.length !== 0) {
(this._callbacks.pop())(this._didWork);
}
this._didWork = false;
});
}
whenStable(callback: Function): void {
@ -87,6 +85,10 @@ export class Testability {
getPendingRequestCount(): number { return this._pendingCount; }
// This only accounts for ngZone, and not pending counts. Use `whenStable` to
// check for stability.
isAngularEventPending(): boolean { return this._isAngularEventPending; }
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
// TODO(juliemr): implement.
return [];

View File

@ -238,13 +238,9 @@ export function Class(clsDef: ClassDefinition): ConcreteType {
}
var Reflect = global.Reflect;
// Throw statement at top-level is disallowed by closure compiler in ES6 input.
// Wrap in an IIFE as a work-around.
(function checkReflect() {
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
})();
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
export function makeDecorator(
annotationCls, chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {

View File

@ -1,2 +1,2 @@
// Public API for Zone
export {NgZone, NgZoneError} from './zone/ng_zone';
export {NgZone, ZeroArgFunction, ErrorHandlingFn, NgZoneError} from './zone/ng_zone';

View File

@ -0,0 +1,394 @@
library angular.zone;
import 'dart:async';
import 'package:stack_trace/stack_trace.dart' show Chain;
typedef void ZeroArgFunction();
typedef void ErrorHandlingFn(error, stackTrace);
/**
* A `Timer` wrapper that lets you specify additional functions to call when it
* is cancelled.
*/
class WrappedTimer implements Timer {
Timer _timer;
ZeroArgFunction _onCancelCb;
WrappedTimer(Timer timer) {
_timer = timer;
}
void addOnCancelCb(ZeroArgFunction onCancelCb) {
if (this._onCancelCb != null) {
throw "On cancel cb already registered";
}
this._onCancelCb = onCancelCb;
}
void cancel() {
if (this._onCancelCb != null) {
this._onCancelCb();
}
_timer.cancel();
}
bool get isActive => _timer.isActive;
}
/**
* Stores error information; delivered via [NgZone.onError] stream.
*/
class NgZoneError {
/// Error object thrown.
final error;
/// Either long or short chain of stack traces.
final List stackTrace;
NgZoneError(this.error, this.stackTrace);
}
/**
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
* before the next "VM turn", i.e. event loop iteration.
*
* This lets you freely schedule microtasks that prepare data, and set an {@link onTurnDone} handler that
* will consume that data after it's ready but before the browser has a chance to re-render.
*
* A VM turn consist of a single macrotask followed 0 to many microtasks.
*
* The wrapper maintains an "inner" and "mount" `Zone`. The application code will executes
* in the "inner" zone unless `runOutsideAngular` is explicitely called.
*
* A typical application will create a singleton `NgZone`. The mount zone is the `Zone` where the singleton has been
* instantiated. The default `onTurnDone` runs the Angular change detection.
*/
class NgZone {
ZeroArgFunction _onTurnStart;
ZeroArgFunction _onTurnDone;
ZeroArgFunction _onEventDone;
ErrorHandlingFn _onErrorHandler;
final _onTurnStartCtrl = new StreamController.broadcast(sync: true);
final _onTurnDoneCtrl = new StreamController.broadcast(sync: true);
final _onEventDoneCtrl = new StreamController.broadcast(sync: true);
final _onErrorCtrl =
new StreamController<NgZoneError>.broadcast(sync: true);
// Code executed in _mountZone does not trigger the onTurnDone.
Zone _mountZone;
// _innerZone is the child of _mountZone. Any code executed in this zone will trigger the
// onTurnDone hook at the end of the current VM turn.
Zone _innerZone;
// Number of microtasks pending from _innerZone (& descendants)
int _pendingMicrotasks = 0;
// Whether some code has been executed in the _innerZone (& descendants) in the current turn
bool _hasExecutedCodeInInnerZone = false;
// _outerRun() call depth. 0 at the end of a macrotask
// zone.run(() => { // top-level call
// zone.run(() => {}); // nested call -> in-turn
// }); // we should only check for the end of a turn once the top-level run ends
int _nestedRun = 0;
bool _inVmTurnDone = false;
List<Timer> _pendingTimers = [];
/**
* Associates with this
*
* - a "mount" [Zone], which is a the one that instantiated this.
* - an "inner" [Zone], which is a child of the mount [Zone].
*
* @param {bool} enableLongStackTrace whether to enable long stack trace. They should only be
* enabled in development mode as they significantly impact perf.
*/
NgZone({bool enableLongStackTrace}) {
_mountZone = Zone.current;
if (enableLongStackTrace) {
_innerZone = Chain.capture(() => _createInnerZone(Zone.current),
onError: _onErrorWithLongStackTrace);
} else {
_innerZone = _createInnerZone(Zone.current,
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
error, StackTrace trace) =>
_onErrorWithoutLongStackTrace(error, trace));
}
}
/**
* Sets the zone hook that is called just before Angular event turn starts.
* It is called once per browser event.
*/
@Deprecated('Use onTurnStart Stream instead')
void overrideOnTurnStart(ZeroArgFunction onTurnStartFn) {
_onTurnStart = onTurnStartFn;
}
void _notifyOnTurnStart() {
this._onTurnStartCtrl.add(null);
}
/**
* Notifies subscribers just before Angular event turn starts.
*
* Emits an event once per browser task that is handled by Angular.
*/
Stream get onTurnStart => _onTurnStartCtrl.stream;
/**
* Sets the zone hook that is called immediately after Angular processes
* all pending microtasks.
*/
@Deprecated('Use onTurnDone Stream instead')
void overrideOnTurnDone(ZeroArgFunction onTurnDoneFn) {
_onTurnDone = onTurnDoneFn;
}
/**
* Notifies subscribers immediately after the Angular zone is done processing
* the current turn and any microtasks scheduled from that turn.
*
* Used by Angular as a signal to kick off change-detection.
*/
Stream get onTurnDone => _onTurnDoneCtrl.stream;
void _notifyOnTurnDone() {
this._onTurnDoneCtrl.add(null);
}
/**
* Sets the zone hook that is called immediately after the last turn in
* an event completes. At this point Angular will no longer attempt to
* sync the UI. Any changes to the data model will not be reflected in the
* DOM. `onEventDoneFn` is executed outside Angular zone.
*
* This hook is useful for validating application state (e.g. in a test).
*/
@Deprecated('Use onEventDone Stream instead')
void overrideOnEventDone(ZeroArgFunction onEventDoneFn,
[bool waitForAsync = false]) {
_onEventDone = onEventDoneFn;
if (waitForAsync) {
_onEventDone = () {
if (_pendingTimers.length == 0) {
onEventDoneFn();
}
};
}
}
/**
* Notifies subscribers immediately after the final `onTurnDone` callback
* before ending VM event.
*
* This event is useful for validating application state (e.g. in a test).
*/
Stream get onEventDone => _onEventDoneCtrl.stream;
void _notifyOnEventDone() {
this._onEventDoneCtrl.add(null);
}
/**
* Whether there are any outstanding microtasks.
*/
bool get hasPendingMicrotasks => _pendingMicrotasks > 0;
/**
* Whether there are any outstanding timers.
*/
bool get hasPendingTimers => _pendingTimers.isNotEmpty;
/**
* Whether there are any outstanding asychnronous tasks of any kind that are
* scheduled to run within Angular zone.
*
* Useful as a signal of UI stability. For example, when a test reaches a
* point when [hasPendingAsyncTasks] is `false` it might be a good time to run
* test expectations.
*/
bool get hasPendingAsyncTasks => hasPendingMicrotasks || hasPendingTimers;
/**
* Sets the zone hook that is called when an error is uncaught in the
* Angular zone. The first argument is the error. The second argument is
* the stack trace.
*/
@Deprecated('Use onError Stream instead')
void overrideOnErrorHandler(ErrorHandlingFn errorHandlingFn) {
_onErrorHandler = errorHandlingFn;
}
/**
* Notifies subscribers whenever an error happens within the zone.
*
* Useful for logging.
*/
Stream get onError => _onErrorCtrl.stream;
/**
* Runs `fn` in the inner zone and returns whatever it returns.
*
* In a typical app where the inner zone is the Angular zone, this allows one to make use of the
* Angular's auto digest mechanism.
*
* ```
* NgZone zone = <ref to the application zone>;
*
* void functionCalledFromJS() {
* zone.run(() {
* // auto-digest will run after this function is called from JS
* });
* }
* ```
*/
dynamic run(fn()) {
// Using runGuarded() is required when executing sync code with Dart otherwise handleUncaughtError()
// would not be called on exceptions.
// see https://code.google.com/p/dart/issues/detail?id=19566 for details.
return _innerZone.runGuarded(fn);
}
/**
* Runs `fn` in the mount zone and returns whatever it returns.
*
* In a typical app where the inner zone is the Angular zone, this allows one to escape Angular's
* auto-digest mechanism.
*
* ```
* void myFunction(NgZone zone, Element element) {
* element.onClick.listen(() {
* // auto-digest will run after element click.
* });
* zone.runOutsideAngular(() {
* element.onMouseMove.listen(() {
* // auto-digest will NOT run after mouse move
* });
* });
* }
* ```
*/
dynamic runOutsideAngular(fn()) {
return _mountZone.run(fn);
}
void _maybeStartVmTurn(ZoneDelegate parent) {
if (!_hasExecutedCodeInInnerZone) {
_hasExecutedCodeInInnerZone = true;
parent.run(_innerZone, _notifyOnTurnStart);
if (_onTurnStart != null) {
parent.run(_innerZone, _onTurnStart);
}
}
}
dynamic _run(Zone self, ZoneDelegate parent, Zone zone, fn()) {
try {
_nestedRun++;
_maybeStartVmTurn(parent);
return parent.run(zone, fn);
} finally {
_nestedRun--;
// If there are no more pending microtasks and we are not in a recursive call, this is the end of a turn
if (_pendingMicrotasks == 0 && _nestedRun == 0 && !_inVmTurnDone) {
if (_hasExecutedCodeInInnerZone) {
// Trigger onTurnDone at the end of a turn if _innerZone has executed some code
try {
_inVmTurnDone = true;
_notifyOnTurnDone();
if (_onTurnDone != null) {
parent.run(_innerZone, _onTurnDone);
}
} finally {
_inVmTurnDone = false;
_hasExecutedCodeInInnerZone = false;
}
}
if (_pendingMicrotasks == 0) {
_notifyOnEventDone();
if (_onEventDone != null) {
runOutsideAngular(_onEventDone);
}
}
}
}
}
dynamic _runUnary(Zone self, ZoneDelegate parent, Zone zone, fn(arg), arg) =>
_run(self, parent, zone, () => fn(arg));
dynamic _runBinary(Zone self, ZoneDelegate parent, Zone zone, fn(arg1, arg2),
arg1, arg2) =>
_run(self, parent, zone, () => fn(arg1, arg2));
void _scheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, fn) {
_pendingMicrotasks++;
var microtask = () {
try {
fn();
} finally {
_pendingMicrotasks--;
}
};
parent.scheduleMicrotask(zone, microtask);
}
// Called by Chain.capture() on errors when long stack traces are enabled
void _onErrorWithLongStackTrace(error, Chain chain) {
if (_onErrorHandler != null || _onErrorCtrl.hasListener) {
final traces = chain.terse.traces.map((t) => t.toString()).toList();
if (_onErrorCtrl.hasListener) {
_onErrorCtrl.add(new NgZoneError(error, traces));
}
if (_onErrorHandler != null) {
_onErrorHandler(error, traces);
}
} else {
throw error;
}
}
// Outer zone handleUnchaughtError when long stack traces are not used
void _onErrorWithoutLongStackTrace(error, StackTrace trace) {
if (_onErrorHandler != null || _onErrorCtrl.hasListener) {
if (_onErrorHandler != null) {
_onErrorHandler(error, [trace.toString()]);
}
if (_onErrorCtrl.hasListener) {
_onErrorCtrl.add(new NgZoneError(error, [trace.toString()]));
}
} else {
throw error;
}
}
Timer _createTimer(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, fn()) {
WrappedTimer wrappedTimer;
var cb = () {
fn();
_pendingTimers.remove(wrappedTimer);
};
Timer timer = parent.createTimer(zone, duration, cb);
wrappedTimer = new WrappedTimer(timer);
wrappedTimer.addOnCancelCb(() => _pendingTimers.remove(wrappedTimer));
_pendingTimers.add(wrappedTimer);
return wrappedTimer;
}
Zone _createInnerZone(Zone zone, {handleUncaughtError}) {
return zone.fork(
specification: new ZoneSpecification(
scheduleMicrotask: _scheduleMicrotask,
run: _run,
runUnary: _runUnary,
runBinary: _runBinary,
handleUncaughtError: handleUncaughtError,
createTimer: _createTimer),
zoneValues: {'_innerZone': true});
}
}

View File

@ -1,8 +1,29 @@
import {EventEmitter} from 'angular2/src/facade/async';
import {NgZoneImpl, NgZoneError} from './ng_zone_impl';
import {BaseException} from '../../facade/exceptions';
export {NgZoneError} from './ng_zone_impl';
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {normalizeBlank, isPresent, global, ZoneLike} from 'angular2/src/facade/lang';
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
import {wtfLeave, wtfCreateScope, WtfScopeFn} from '../profile/profile';
export interface NgZoneZone extends ZoneLike {
/** @internal */
_innerZone: boolean;
}
/**
* Interface for a function with zero arguments.
*/
export interface ZeroArgFunction { (): void; }
/**
* Function type for an error handler, which takes an error and a stack trace.
*/
export interface ErrorHandlingFn { (error: any, stackTrace: any): void; }
/**
* Stores error information; delivered via [NgZone.onError] stream.
*/
export class NgZoneError {
constructor(public error: any, public stackTrace: any) {}
}
/**
* An injectable service for executing work inside or outside of the Angular zone.
@ -76,120 +97,206 @@ export {NgZoneError} from './ng_zone_impl';
* ```
*/
export class NgZone {
static isInAngularZone(): boolean { return NgZoneImpl.isInAngularZone(); }
static assertInAngularZone(): void {
if (!NgZoneImpl.isInAngularZone()) {
throw new BaseException('Expected to be in Angular Zone, but it is not!');
}
}
static assertNotInAngularZone(): void {
if (NgZoneImpl.isInAngularZone()) {
throw new BaseException('Expected to not be in Angular Zone, but it is!');
}
}
/** @internal */
_runScope: WtfScopeFn = wtfCreateScope(`NgZone#run()`);
/** @internal */
_microtaskScope: WtfScopeFn = wtfCreateScope(`NgZone#microtask()`);
private _zoneImpl: NgZoneImpl;
private _hasPendingMicrotasks: boolean = false;
private _hasPendingMacrotasks: boolean = false;
// Code executed in _mountZone does not trigger the onTurnDone.
/** @internal */
_mountZone;
// _innerZone is the child of _mountZone. Any code executed in this zone will trigger the
// onTurnDone hook at the end of the current VM turn.
/** @internal */
_innerZone;
/** @internal */
private _isStable = true;
_onTurnStart: ZeroArgFunction;
/** @internal */
private _nesting = 0;
_onTurnDone: ZeroArgFunction;
/** @internal */
private _onUnstable: EventEmitter<any> = new EventEmitter(false);
_onEventDone: ZeroArgFunction;
/** @internal */
private _onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false);
_onErrorHandler: ErrorHandlingFn;
/** @internal */
private _onStable: EventEmitter<any> = new EventEmitter(false);
_onTurnStartEvents: EventEmitter<any>;
/** @internal */
private _onErrorEvents: EventEmitter<any> = new EventEmitter(false);
_onTurnDoneEvents: EventEmitter<any>;
/** @internal */
_onEventDoneEvents: EventEmitter<any>;
/** @internal */
_onErrorEvents: EventEmitter<any>;
// Number of microtasks pending from _innerZone (& descendants)
/** @internal */
_pendingMicrotasks: number = 0;
// Whether some code has been executed in the _innerZone (& descendants) in the current turn
/** @internal */
_hasExecutedCodeInInnerZone: boolean = false;
// run() call depth in _mountZone. 0 at the end of a macrotask
// zone.run(() => { // top-level call
// zone.run(() => {}); // nested call -> in-turn
// });
/** @internal */
_nestedRun: number = 0;
// TODO(vicb): implement this class properly for node.js environment
// This disabled flag is only here to please cjs tests
/** @internal */
_disabled: boolean;
/** @internal */
_inVmTurnDone: boolean = false;
/** @internal */
_pendingTimeouts: number[] = [];
/**
* @param {bool} enableLongStackTrace whether to enable long stack trace. They should only be
* enabled in development mode as they significantly impact perf.
*/
constructor({enableLongStackTrace = false}) {
this._zoneImpl = new NgZoneImpl({
trace: enableLongStackTrace,
onEnter: () => {
// console.log('ZONE.enter', this._nesting, this._isStable);
this._nesting++;
if (this._isStable) {
this._isStable = false;
this._onUnstable.emit(null);
}
},
onLeave: () => {
this._nesting--;
// console.log('ZONE.leave', this._nesting, this._isStable);
this._checkStable();
},
setMicrotask: (hasMicrotasks: boolean) => {
this._hasPendingMicrotasks = hasMicrotasks;
this._checkStable();
},
setMacrotask: (hasMacrotasks: boolean) => { this._hasPendingMacrotasks = hasMacrotasks; },
onError: (error: NgZoneError) => this._onErrorEvents.emit(error)
});
constructor({enableLongStackTrace}) {
if (global.zone) {
this._disabled = false;
this._mountZone = global.zone;
this._innerZone = this._createInnerZone(this._mountZone, enableLongStackTrace);
} else {
this._disabled = true;
this._mountZone = null;
}
this._onTurnStartEvents = new EventEmitter(false);
this._onTurnDoneEvents = new EventEmitter(false);
this._onEventDoneEvents = new EventEmitter(false);
this._onErrorEvents = new EventEmitter(false);
}
private _checkStable() {
if (this._nesting == 0) {
if (!this._hasPendingMicrotasks && !this._isStable) {
try {
// console.log('ZONE.microtaskEmpty');
this._nesting++;
this._onMicrotaskEmpty.emit(null);
} finally {
this._nesting--;
if (!this._hasPendingMicrotasks) {
try {
// console.log('ZONE.stable', this._nesting, this._isStable);
this.runOutsideAngular(() => this._onStable.emit(null));
} finally {
this._isStable = true;
}
}
/**
* Sets the zone hook that is called just before a browser task that is handled by Angular
* executes.
*
* The hook is called once per browser task that is handled by Angular.
*
* Setting the hook overrides any previously set hook.
*
* @deprecated this API will be removed in the future. Use `onTurnStart` instead.
*/
overrideOnTurnStart(onTurnStartHook: ZeroArgFunction): void {
this._onTurnStart = normalizeBlank(onTurnStartHook);
}
/**
* Notifies subscribers just before Angular event turn starts.
*
* Emits an event once per browser task that is handled by Angular.
*/
get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
/** @internal */
_notifyOnTurnStart(parentRun): void {
parentRun.call(this._innerZone, () => { this._onTurnStartEvents.emit(null); });
}
/**
* Sets the zone hook that is called immediately after Angular zone is done processing the current
* task and any microtasks scheduled from that task.
*
* This is where we typically do change-detection.
*
* The hook is called once per browser task that is handled by Angular.
*
* Setting the hook overrides any previously set hook.
*
* @deprecated this API will be removed in the future. Use `onTurnDone` instead.
*/
overrideOnTurnDone(onTurnDoneHook: ZeroArgFunction): void {
this._onTurnDone = normalizeBlank(onTurnDoneHook);
}
/**
* Notifies subscribers immediately after Angular zone is done processing
* the current turn and any microtasks scheduled from that turn.
*
* Used by Angular as a signal to kick off change-detection.
*/
get onTurnDone() { return this._onTurnDoneEvents; }
/** @internal */
_notifyOnTurnDone(parentRun): void {
parentRun.call(this._innerZone, () => { this._onTurnDoneEvents.emit(null); });
}
/**
* Sets the zone hook that is called immediately after the `onTurnDone` callback is called and any
* microstasks scheduled from within that callback are drained.
*
* `onEventDoneFn` is executed outside Angular zone, which means that we will no longer attempt to
* sync the UI with any model changes that occur within this callback.
*
* This hook is useful for validating application state (e.g. in a test).
*
* Setting the hook overrides any previously set hook.
*
* @deprecated this API will be removed in the future. Use `onEventDone` instead.
*/
overrideOnEventDone(onEventDoneFn: ZeroArgFunction, opt_waitForAsync: boolean = false): void {
var normalizedOnEventDone = normalizeBlank(onEventDoneFn);
if (opt_waitForAsync) {
this._onEventDone = () => {
if (!this._pendingTimeouts.length) {
normalizedOnEventDone();
}
}
};
} else {
this._onEventDone = normalizedOnEventDone;
}
};
}
/**
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
* Notifies subscribers immediately after the final `onTurnDone` callback
* before ending VM event.
*
* This event is useful for validating application state (e.g. in a test).
*/
get onUnstable(): EventEmitter<any> { return this._onUnstable; }
get onEventDone() { return this._onEventDoneEvents; }
/**
* Notifies when there is no more microtasks enqueue in the current VM Turn.
* This is a hint for Angular to do change detection, which may enqueue more microtasks.
* For this reason this event can fire multiple times per VM Turn.
*/
get onMicrotaskEmpty(): EventEmitter<any> { return this._onMicrotaskEmpty; }
/**
* Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
* implies we are about to relinquish VM turn.
* This event gets called just once.
*/
get onStable(): EventEmitter<any> { return this._onStable; }
/**
* Notify that an error has been delivered.
*/
get onError(): EventEmitter<any> { return this._onErrorEvents; }
/** @internal */
_notifyOnEventDone(): void {
this.runOutsideAngular(() => { this._onEventDoneEvents.emit(null); });
}
/**
* Whether there are any outstanding microtasks.
*/
get hasPendingMicrotasks(): boolean { return this._hasPendingMicrotasks; }
get hasPendingMicrotasks(): boolean { return this._pendingMicrotasks > 0; }
/**
* Whether there are any outstanding microtasks.
* Whether there are any outstanding timers.
*/
get hasPendingMacrotasks(): boolean { return this._hasPendingMacrotasks; }
get hasPendingTimers(): boolean { return this._pendingTimeouts.length > 0; }
/**
* Whether there are any outstanding asynchronous tasks of any kind that are
* scheduled to run within Angular zone.
*
* Useful as a signal of UI stability. For example, when a test reaches a
* point when [hasPendingAsyncTasks] is `false` it might be a good time to run
* test expectations.
*/
get hasPendingAsyncTasks(): boolean { return this.hasPendingMicrotasks || this.hasPendingTimers; }
/**
* Sets the zone hook that is called when an error is thrown in the Angular zone.
*
* Setting the hook overrides any previously set hook.
*
* @deprecated this API will be removed in the future. Use `onError` instead.
*/
overrideOnErrorHandler(errorHandler: ErrorHandlingFn) {
this._onErrorHandler = normalizeBlank(errorHandler);
}
get onError() { return this._onErrorEvents; }
/**
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
@ -201,7 +308,18 @@ export class NgZone {
* Any future tasks or microtasks scheduled from within this function will continue executing from
* within the Angular zone.
*/
run(fn: () => any): any { return this._zoneImpl.runInner(fn); }
run(fn: () => any): any {
if (this._disabled) {
return fn();
} else {
var s = this._runScope();
try {
return this._innerZone.run(fn);
} finally {
wtfLeave(s);
}
}
}
/**
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
@ -215,5 +333,130 @@ export class NgZone {
*
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
*/
runOutsideAngular(fn: () => any): any { return this._zoneImpl.runOuter(fn); }
runOutsideAngular(fn: () => any): any {
if (this._disabled) {
return fn();
} else {
return this._mountZone.run(fn);
}
}
/** @internal */
_createInnerZone(zone, enableLongStackTrace) {
var microtaskScope = this._microtaskScope;
var ngZone = this;
var errorHandling;
if (enableLongStackTrace) {
errorHandling =
StringMapWrapper.merge(global.Zone.longStackTraceZone,
{onError: function(e) { ngZone._notifyOnError(this, e); }});
} else {
errorHandling = {onError: function(e) { ngZone._notifyOnError(this, e); }};
}
return zone.fork(errorHandling)
.fork({
'$run': function(parentRun) {
return function() {
try {
ngZone._nestedRun++;
if (!ngZone._hasExecutedCodeInInnerZone) {
ngZone._hasExecutedCodeInInnerZone = true;
ngZone._notifyOnTurnStart(parentRun);
if (ngZone._onTurnStart) {
parentRun.call(ngZone._innerZone, ngZone._onTurnStart);
}
}
return parentRun.apply(this, arguments);
} finally {
ngZone._nestedRun--;
// If there are no more pending microtasks, we are at the end of a VM turn (or in
// onTurnStart)
// _nestedRun will be 0 at the end of a macrotasks (it could be > 0 when there are
// nested calls
// to run()).
if (ngZone._pendingMicrotasks == 0 && ngZone._nestedRun == 0 &&
!this._inVmTurnDone) {
if (ngZone._hasExecutedCodeInInnerZone) {
try {
this._inVmTurnDone = true;
ngZone._notifyOnTurnDone(parentRun);
if (ngZone._onTurnDone) {
parentRun.call(ngZone._innerZone, ngZone._onTurnDone);
}
} finally {
this._inVmTurnDone = false;
ngZone._hasExecutedCodeInInnerZone = false;
}
}
if (ngZone._pendingMicrotasks === 0) {
ngZone._notifyOnEventDone();
if (isPresent(ngZone._onEventDone)) {
ngZone.runOutsideAngular(ngZone._onEventDone);
}
}
}
}
};
},
'$scheduleMicrotask': function(parentScheduleMicrotask) {
return function(fn) {
ngZone._pendingMicrotasks++;
var microtask = function() {
var s = microtaskScope();
try {
fn();
} finally {
ngZone._pendingMicrotasks--;
wtfLeave(s);
}
};
parentScheduleMicrotask.call(this, microtask);
};
},
'$setTimeout': function(parentSetTimeout) {
return function(fn: Function, delay: number, ...args) {
var id;
var cb = function() {
fn();
ListWrapper.remove(ngZone._pendingTimeouts, id);
};
id = parentSetTimeout.call(this, cb, delay, args);
ngZone._pendingTimeouts.push(id);
return id;
};
},
'$clearTimeout': function(parentClearTimeout) {
return function(id: number) {
parentClearTimeout.call(this, id);
ListWrapper.remove(ngZone._pendingTimeouts, id);
};
},
_innerZone: true
});
}
/** @internal */
_notifyOnError(zone, e): void {
if (isPresent(this._onErrorHandler) || ObservableWrapper.hasSubscribers(this._onErrorEvents)) {
var trace = [normalizeBlank(e.stack)];
while (zone && zone.constructedAtException) {
trace.push(zone.constructedAtException.get());
zone = zone.parent;
}
if (ObservableWrapper.hasSubscribers(this._onErrorEvents)) {
ObservableWrapper.callEmit(this._onErrorEvents, new NgZoneError(e, trace));
}
if (isPresent(this._onErrorHandler)) {
this._onErrorHandler(e, trace);
}
} else {
console.log('## _notifyOnError ##');
console.log(e.stack);
throw e;
}
}
}

View File

@ -1,222 +0,0 @@
library angular.zone;
import 'dart:async';
import 'package:stack_trace/stack_trace.dart' show Chain;
typedef void ZeroArgFunction();
typedef void ErrorHandlingFn(error, stackTrace);
/**
* A `Timer` wrapper that lets you specify additional functions to call when it
* is cancelled.
*/
class WrappedTimer implements Timer {
Timer _timer;
ZeroArgFunction _onCancelCb;
WrappedTimer(Timer timer) {
_timer = timer;
}
void addOnCancelCb(ZeroArgFunction onCancelCb) {
if (this._onCancelCb != null) {
throw "On cancel cb already registered";
}
this._onCancelCb = onCancelCb;
}
void cancel() {
if (this._onCancelCb != null) {
this._onCancelCb();
}
_timer.cancel();
}
bool get isActive => _timer.isActive;
}
/**
* Stores error information; delivered via [NgZone.onError] stream.
*/
class NgZoneError {
/// Error object thrown.
final error;
/// Either long or short chain of stack traces.
final List stackTrace;
NgZoneError(this.error, this.stackTrace);
}
/**
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
* before the next "VM turn", i.e. event loop iteration.
*
* This lets you freely schedule microtasks that prepare data, and set an {@link onMicrotaskEmpty} handler that
* will consume that data after it's ready but before the browser has a chance to re-render.
*
* A VM turn consist of a single macrotask followed 0 to many microtasks.
*
* The wrapper maintains an "inner" and "mount" `Zone`. The application code will executes
* in the "inner" zone unless `runOutsideAngular` is explicitely called.
*
* A typical application will create a singleton `NgZone`. The mount zone is the `Zone` where the singleton has been
* instantiated. The default `onMicrotaskEmpty` runs the Angular change detection.
*/
class NgZoneImpl {
static bool isInAngularZone() {
return Zone.current['isAngularZone'] == true;
}
// Number of microtasks pending from _innerZone (& descendants)
int _pendingMicrotasks = 0;
List<Timer> _pendingTimers = [];
Function onEnter;
Function onLeave;
Function setMicrotask;
Function setMacrotask;
Function onError;
Zone _outerZone;
Zone _innerZone;
/**
* Associates with this
*
* - a "mount" [Zone], which is a the one that instantiated this.
* - an "inner" [Zone], which is a child of the mount [Zone].
*
* @param {bool} trace whether to enable long stack trace. They should only be
* enabled in development mode as they significantly impact perf.
*/
NgZoneImpl({
bool trace,
Function this.onEnter,
Function this.onLeave,
Function this.setMicrotask,
Function this.setMacrotask,
Function this.onError
}) {
_outerZone = Zone.current;
if (trace) {
_innerZone = Chain.capture(
() => _createInnerZone(Zone.current),
onError: _onErrorWithLongStackTrace
);
} else {
_innerZone = _createInnerZone(
Zone.current,
handleUncaughtError: _onErrorWithoutLongStackTrace
);
}
}
Zone _createInnerZone(Zone zone, {handleUncaughtError}) {
return zone.fork(
specification: new ZoneSpecification(
scheduleMicrotask: _scheduleMicrotask,
run: _run,
runUnary: _runUnary,
runBinary: _runBinary,
handleUncaughtError: handleUncaughtError,
createTimer: _createTimer),
zoneValues: {'isAngularZone': true}
);
}
dynamic runInner(fn()) {
return _innerZone.runGuarded(fn);
}
/**
* Runs `fn` in the mount zone and returns whatever it returns.
*
* In a typical app where the inner zone is the Angular zone, this allows one to escape Angular's
* auto-digest mechanism.
*
* ```
* void myFunction(NgZone zone, Element element) {
* element.onClick.listen(() {
* // auto-digest will run after element click.
* });
* zone.runOutsideAngular(() {
* element.onMouseMove.listen(() {
* // auto-digest will NOT run after mouse move
* });
* });
* }
* ```
*/
dynamic runOuter(fn()) {
return _outerZone.run(fn);
}
dynamic _run(Zone self, ZoneDelegate parent, Zone zone, fn()) {
try {
onEnter();
return parent.run(zone, fn);
} finally {
onLeave();
}
}
dynamic _runUnary(Zone self, ZoneDelegate parent, Zone zone, fn(arg), arg) =>
_run(self, parent, zone, () => fn(arg));
dynamic _runBinary(Zone self, ZoneDelegate parent, Zone zone, fn(arg1, arg2),
arg1, arg2) =>
_run(self, parent, zone, () => fn(arg1, arg2));
void _scheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, fn) {
if (_pendingMicrotasks == 0) {
setMicrotask(true);
}
_pendingMicrotasks++;
var microtask = () {
try {
fn();
} finally {
_pendingMicrotasks--;
if (_pendingMicrotasks == 0) {
setMicrotask(false);
}
}
};
parent.scheduleMicrotask(zone, microtask);
}
// Called by Chain.capture() on errors when long stack traces are enabled
void _onErrorWithLongStackTrace(error, Chain chain) {
final traces = chain.terse.traces.map((t) => t.toString()).toList();
onError(new NgZoneError(error, traces));
}
// Outer zone handleUnchaughtError when long stack traces are not used
void _onErrorWithoutLongStackTrace(Zone self, ZoneDelegate parent, Zone zone,
error, StackTrace trace)
{
onError(new NgZoneError(error, [trace.toString()]));
}
Timer _createTimer(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, fn()) {
WrappedTimer wrappedTimer;
var cb = () {
try {
fn();
} finally {
_pendingTimers.remove(wrappedTimer);
setMacrotask(_pendingTimers.isNotEmpty);
}
};
Timer timer = parent.createTimer(zone, duration, cb);
wrappedTimer = new WrappedTimer(timer);
wrappedTimer.addOnCancelCb(() {
_pendingTimers.remove(wrappedTimer);
setMacrotask(_pendingTimers.isNotEmpty);
});
_pendingTimers.add(wrappedTimer);
setMacrotask(true);
return wrappedTimer;
}
}

View File

@ -1,99 +0,0 @@
import {global} from 'angular2/src/facade/lang';
/**
* Stores error information; delivered via [NgZone.onError] stream.
*/
export class NgZoneError {
constructor(public error: any, public stackTrace: any) {}
}
export class NgZoneImpl {
static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; }
/** @internal */
private outer: Zone;
/** @internal */
private inner: Zone;
private onEnter: () => void;
private onLeave: () => void;
private setMicrotask: (hasMicrotasks: boolean) => void;
private setMacrotask: (hasMacrotasks: boolean) => void;
private onError: (error: NgZoneError) => void;
constructor({trace, onEnter, onLeave, setMicrotask, setMacrotask, onError}: {
trace: boolean,
onEnter: () => void,
onLeave: () => void,
setMicrotask: (hasMicrotasks: boolean) => void,
setMacrotask: (hasMacrotasks: boolean) => void,
onError: (error: NgZoneError) => void
}) {
this.onEnter = onEnter;
this.onLeave = onLeave;
this.setMicrotask = setMicrotask;
this.setMacrotask = setMacrotask;
this.onError = onError;
if (Zone) {
this.outer = this.inner = Zone.current;
if (Zone['wtfZoneSpec']) {
this.inner = this.inner.fork(Zone['wtfZoneSpec']);
}
if (trace && Zone['longStackTraceZoneSpec']) {
this.inner = this.inner.fork(Zone['longStackTraceZoneSpec']);
}
this.inner = this.inner.fork({
name: 'angular',
properties:<any>{'isAngularZone': true},
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task,
applyThis: any, applyArgs: any): any => {
try {
this.onEnter();
return delegate.invokeTask(target, task, applyThis, applyArgs);
} finally {
this.onLeave();
}
},
onInvoke: (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function,
applyThis: any, applyArgs: any[], source: string): any => {
try {
this.onEnter();
return delegate.invoke(target, callback, applyThis, applyArgs, source);
} finally {
this.onLeave();
}
},
onHasTask:
(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) => {
delegate.hasTask(target, hasTaskState);
if (current == target) {
// We are only interested in hasTask events which originate from our zone
// (A child hasTask event is not interesting to us)
if (hasTaskState.change == 'microTask') {
this.setMicrotask(hasTaskState.microTask);
} else if (hasTaskState.change == 'macroTask') {
this.setMacrotask(hasTaskState.macroTask);
}
}
},
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any):
boolean => {
delegate.handleError(target, error);
this.onError(new NgZoneError(error, error.stack));
return false;
}
});
} else {
throw new Error('Angular2 needs to be run with Zone.js polyfill.');
}
}
runInner(fn: () => any): any { return this.inner.runGuarded(fn); };
runOuter(fn: () => any): any { return this.outer.run(fn); };
}

View File

@ -25,7 +25,7 @@ class TimerWrapper {
}
class ObservableWrapper {
static StreamSubscription subscribe/*<T>*/(Stream s, onNext(/*=T*/ value),
static StreamSubscription subscribe(Stream s, Function onNext,
[onError, onComplete]) {
return s.listen(onNext,
onError: onError, onDone: onComplete, cancelOnError: true);

View File

@ -1,8 +1,10 @@
import {global, noop} from 'angular2/src/facade/lang';
import {global, isPresent, noop} from 'angular2/src/facade/lang';
export {PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/promise';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
import {Operator} from 'rxjs/Operator';
import {PromiseObservable} from 'rxjs/observable/PromiseObservable';
import {toPromise} from 'rxjs/operator/toPromise';

View File

@ -33,15 +33,15 @@ class IterableMap extends IterableBase<List> {
}
class MapWrapper {
static Map/*<K,V>*/ clone/*<K,V>*/(Map/*<K,V>*/ m) => new Map.from(m);
static Map clone(Map m) => new Map.from(m);
// in opposite to JS, Dart does not create a new map
static Map/*<K,V>*/ createFromStringMap/*<K,V>*/(Map/*<K,V>*/ m) => m;
static Map createFromStringMap(Map m) => m;
// in opposite to JS, Dart does not create a new map
static Map/*<K,V>*/ toStringMap/*<K,V>*/(Map/*<K,V>*/ m) => m;
static Map toStringMap(Map m) => m;
static Map/*<K,V>*/ createFromPairs/*<K,V>*/(List pairs) => pairs.fold(/*<K,V>*/{}, (m, p) {
static Map createFromPairs(List pairs) => pairs.fold({}, (m, p) {
m[p[0]] = p[1];
return m;
});
@ -52,29 +52,29 @@ class MapWrapper {
}
}
static Iterable/*<List<dynamic>>*/ iterable/*<K,V>*/(Map/*<K,V>*/ m) => new IterableMap(m);
static List/*<K>*/ keys/*<K,V>*/(Map/*<K,V>*/ m) => m.keys.toList();
static List/*<V>*/ values/*<K,V>*/(Map/*<K,V>*/ m) => m.values.toList();
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/*<String,V>*/ create/*<V>*/() => {};
static bool contains/*<V>*/(Map/*<String,V>*/ map, String key) => map.containsKey(key);
static get/*<V>*/(Map/*<String,V>*/ map, String key) => map[key];
static void set/*<V>*/(Map/*<String,V>*/ map, String key, /*=V*/value) {
static Map create() => {};
static bool contains(Map map, key) => map.containsKey(key);
static get(Map map, key) => map[key];
static void set(Map map, key, value) {
map[key] = value;
}
static void delete/*<V>*/(Map/*<String,V>*/ m, String k) {
static void delete(Map m, k) {
m.remove(k);
}
static void forEach/*<V>*/(Map/*<String,V>*/ m, fn(/*=V*/ v, String k)) {
static void forEach(Map m, fn(v, k)) {
m.forEach((k, v) => fn(v, k));
}
static Map/*<String,V>*/ merge/*<V>*/(Map/*<String,V>*/ a, Map/*<String,V>*/ b) {
var m = new Map/*<String,V>*/.from(a);
static Map merge(Map a, Map b) {
var m = new Map.from(a);
if (b != null) {
b.forEach((k, v) => m[k] = v);
}
@ -85,12 +85,8 @@ class StringMapWrapper {
return a.keys.toList();
}
static List values(Map<String, dynamic> a) {
return a.values.toList();
}
static bool isEmpty(Map m) => m.isEmpty;
static bool equals/*<V>*/(Map/*<String,V>*/ m1, Map/*<String,V>*/ m2) {
static bool equals(Map m1, Map m2) {
if (m1.length != m2.length) {
return false;
}
@ -106,9 +102,9 @@ class StringMapWrapper {
typedef bool Predicate<T>(T item);
class ListWrapper {
static List/*<T>*/ clone/*<T>*/(Iterable/*<T>*/ l) => new List.from(l);
static List/*<T>*/ createFixedSize/*<T>*/(int size) => new List(size);
static List/*<T>*/ createGrowableSize/*<T>*/(int size) =>
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 UnmodifiableListView createImmutable(List input) {
return new UnmodifiableListView(input);
@ -120,43 +116,43 @@ class ListWrapper {
static int lastIndexOf(List list, value, [int startIndex = null]) =>
list.lastIndexOf(value, startIndex == null ? list.length : startIndex);
static void forEachWithIndex/*<T>*/(List/*<T>*/ list, fn(/*=T*/ item, int index)) {
static void forEachWithIndex(List list, fn(item, index)) {
for (var i = 0; i < list.length; ++i) {
fn(list[i], i);
}
}
static /*=T*/ first/*<T>*/(List/*<T>*/ list) => list.isEmpty ? null : list.first;
static /*=T*/ last/*<T>*/(List/*<T>*/ list) => list.isEmpty ? null : list.last;
static List/*<T>*/ reversed/*<T>*/(List/*<T>*/ list) => list.reversed.toList();
static List/*<T>*/ concat/*<T>*/(List/*<T>*/ a, List/*<T>*/ b) {
static first(List list) => list.isEmpty ? null : list.first;
static 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/*<T>*/(List/*<T>*/ l, int index, /*=T*/ value) {
static void insert(List l, int index, value) {
l.insert(index, value);
}
static removeAt(List l, int index) => l.removeAt(index);
static void removeAll/*<T>*/(List/*<T>*/ list, List/*<T>*/ items) {
static void removeAll(List list, List items) {
for (var i = 0; i < items.length; ++i) {
list.remove(items[i]);
}
}
static bool remove/*<T>*/(List/*<T>*/ list, /*=T*/ item) => list.remove(item);
static bool remove(List list, item) => list.remove(item);
static void clear(List l) {
l.clear();
}
static bool isEmpty(Iterable list) => list.isEmpty;
static void fill/*<T>*/(List/*<T>*/ l, /*=T*/ value, [int start = 0, int end]) {
static void fill(List l, value, [int start = 0, int end]) {
l.fillRange(_startOffset(l, start), _endOffset(l, end), value);
}
static bool equals/*<T>*/(List/*<T>*/ a, List/*<T>*/ b) {
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;
@ -164,7 +160,7 @@ class ListWrapper {
return true;
}
static List/*<T>*/ slice/*<T>*/(List/*<T>*/ l, [int from = 0, int to]) {
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
@ -174,7 +170,7 @@ class ListWrapper {
return l.sublist(from, to);
}
static List/*<T>*/ splice/*<T>*/(List/*<T>*/ l, int from, int length) {
static List splice(List l, int from, int length) {
from = _startOffset(l, from);
var to = from + length;
var sub = l.sublist(from, to);
@ -182,7 +178,7 @@ class ListWrapper {
return sub;
}
static void sort/*<T>*/(List/*<T>*/ l, [int compareFn(/*=T*/a, /*=T*/b) = null]) {
static void sort(List l, [compareFn(a, b) = null]) {
if (compareFn == null) {
l.sort();
} else {
@ -236,11 +232,7 @@ class ListWrapper {
bool isListLikeIterable(obj) => obj is Iterable;
bool areIterablesEqual/*<T>*/(
Iterable/*<T>*/ a,
Iterable/*<T>*/ b,
bool comparator(/*=T*/a, /*=T*/b))
{
bool areIterablesEqual(Iterable a, Iterable b, Function comparator) {
var iterator1 = a.iterator;
var iterator2 = b.iterator;
@ -249,11 +241,11 @@ bool areIterablesEqual/*<T>*/(
var done2 = !iterator2.moveNext();
if (done1 && done2) return true;
if (done1 || done2) return false;
if (!comparator(iterator1.current, iterator2.current)) return false;
if (!comparator(iterator2.current, iterator2.current)) return false;
}
}
void iterateListLike/*<T>*/(Iterable/*<T>*/ iter, fn(/*=T*/item)) {
void iterateListLike(iter, fn(item)) {
assert(iter is Iterable);
for (var item in iter) {
fn(item);
@ -261,9 +253,9 @@ void iterateListLike/*<T>*/(Iterable/*<T>*/ iter, fn(/*=T*/item)) {
}
class SetWrapper {
static Set/*<T>*/ createFromList/*<T>*/(List/*<T>*/ l) => new Set.from(l);
static bool has/*<T>*/(Set/*<T>*/ s, /*=T*/key) => s.contains(key);
static void delete/*<T>*/(Set/*<T>*/ m, /*=T*/k) {
static Set createFromList(List l) => new Set.from(l);
static bool has(Set s, key) => s.contains(key);
static void delete(Set m, k) {
m.remove(k);
}
}

View File

@ -116,12 +116,6 @@ export class StringMapWrapper {
}
static set<V>(map: {[key: string]: V}, key: string, value: V) { map[key] = value; }
static keys(map: {[key: string]: any}): string[] { return Object.keys(map); }
static values<T>(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;

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