diff --git a/modules/angular2/src/router/browser_location.js b/modules/angular2/src/router/browser_location.js index b9b37dcf20..8d60fe3e1a 100644 --- a/modules/angular2/src/router/browser_location.js +++ b/modules/angular2/src/router/browser_location.js @@ -4,21 +4,22 @@ export class BrowserLocation { _location; _history; _baseHref:string; + constructor() { this._location = DOM.getLocation(); this._history = DOM.getHistory(); this._baseHref = DOM.getBaseHref(); } - onPopState(fn) { + onPopState(fn: Function): void { DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false); } - getBaseHref() { + getBaseHref(): string { return this._baseHref; } - path() { + path(): string { return this._location.pathname; } @@ -26,11 +27,11 @@ export class BrowserLocation { this._history.pushState(state, title, url); } - forward() { + forward(): void { this._history.forward(); } - back() { + back(): void { this._history.back(); } } diff --git a/modules/angular2/src/router/instruction.js b/modules/angular2/src/router/instruction.js index 0846c4ac21..a7d8a10c53 100644 --- a/modules/angular2/src/router/instruction.js +++ b/modules/angular2/src/router/instruction.js @@ -1,28 +1,29 @@ -import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; +import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; -import {isPresent} from 'angular2/src/facade/lang'; +import {isPresent, normalizeBlank} from 'angular2/src/facade/lang'; export class RouteParams { - params:Map; + params:StringMap; + constructor(params:StringMap) { this.params = params; } - get(param:string) { - return StringMapWrapper.get(this.params, param); + get(param:string): string { + return normalizeBlank(StringMapWrapper.get(this.params, param)); } } export class Instruction { component:any; - _children:Map; + _children:StringMap; router:any; matchedUrl:string; - params:Map; + params:StringMap; reuse:boolean; cost:number; - constructor({params, component, children, matchedUrl, parentCost}:{params:StringMap, component:any, children:Map, matchedUrl:string, cost:int} = {}) { + constructor({params, component, children, matchedUrl, parentCost}:{params:StringMap, component:any, children:StringMap, matchedUrl:string, cost:number} = {}) { this.reuse = false; this.matchedUrl = matchedUrl; this.cost = parentCost; @@ -43,11 +44,11 @@ export class Instruction { this.params = params; } - getChildInstruction(outletName:string) { + getChildInstruction(outletName:string): Instruction { return StringMapWrapper.get(this._children, outletName); } - forEachChild(fn:Function) { + forEachChild(fn:Function): void { StringMapWrapper.forEach(this._children, fn); } @@ -60,7 +61,7 @@ export class Instruction { * Takes a function with signature: * (parent:Instruction, child:Instruction) => {} */ - traverseSync(fn:Function) { + traverseSync(fn:Function): void { this.forEachChild((childInstruction, _) => fn(this, childInstruction)); this.forEachChild((childInstruction, _) => childInstruction.traverseSync(fn)); } @@ -70,7 +71,7 @@ export class Instruction { * Takes a function with signature: * (child:Instruction, parentOutletName:string) => {} */ - traverseAsync(fn:Function) { + traverseAsync(fn:Function):Promise { return this.mapChildrenAsync(fn) .then((_) => this.mapChildrenAsync((childInstruction, _) => childInstruction.traverseAsync(fn))); } @@ -79,7 +80,7 @@ export class Instruction { /** * Takes a currently active instruction and sets a reuse flag on this instruction */ - reuseComponentsFrom(oldInstruction:Instruction) { + reuseComponentsFrom(oldInstruction:Instruction): void { this.forEachChild((childInstruction, outletName) => { var oldInstructionChild = oldInstruction.getChildInstruction(outletName); if (shouldReuseComponent(childInstruction, oldInstructionChild)) { @@ -89,16 +90,16 @@ export class Instruction { } } -function shouldReuseComponent(instr1:Instruction, instr2:Instruction) { +function shouldReuseComponent(instr1:Instruction, instr2:Instruction): boolean { return instr1.component == instr2.component && StringMapWrapper.equals(instr1.params, instr2.params); } -function mapObjAsync(obj:StringMap, fn) { +function mapObjAsync(obj:StringMap, fn): Promise { return PromiseWrapper.all(mapObj(obj, fn)); } -function mapObj(obj:StringMap, fn):List { +function mapObj(obj:StringMap, fn: Function):List { var result = ListWrapper.create(); StringMapWrapper.forEach(obj, (value, key) => ListWrapper.push(result, fn(value, key))); return result; diff --git a/modules/angular2/src/router/location.js b/modules/angular2/src/router/location.js index 741fad9d98..a899da50b9 100644 --- a/modules/angular2/src/router/location.js +++ b/modules/angular2/src/router/location.js @@ -13,62 +13,62 @@ export class Location { this._browserLocation.onPopState((_) => this._onPopState(_)); } - _onPopState(_) { + _onPopState(_): void { ObservableWrapper.callNext(this._subject, { 'url': this.path() }); } - path() { + path(): string { return this.normalize(this._browserLocation.path()); } - normalize(url) { + normalize(url: string): string { return this._stripBaseHref(stripIndexHtml(url)); } - normalizeAbsolutely(url) { + normalizeAbsolutely(url: string): string { if (url[0] != '/') { url = '/' + url; } return this._addBaseHref(url); } - _stripBaseHref(url) { + _stripBaseHref(url: string): string { if (this._baseHref.length > 0 && StringWrapper.startsWith(url, this._baseHref)) { return StringWrapper.substring(url, this._baseHref.length); } return url; } - _addBaseHref(url) { + _addBaseHref(url: string): string { if (!StringWrapper.startsWith(url, this._baseHref)) { return this._baseHref + url; } return url; } - go(url:string) { + go(url:string): void { var finalUrl = this.normalizeAbsolutely(url); this._browserLocation.pushState(null, '', finalUrl); } - forward() { + forward(): void { this._browserLocation.forward(); } - back() { + back(): void { this._browserLocation.back(); } - subscribe(onNext, onThrow = null, onReturn = null) { + subscribe(onNext, onThrow = null, onReturn = null): void { ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn); } } -function stripIndexHtml(url) { +function stripIndexHtml(url: string): string { // '/index.html'.length == 11 if (url.length > 10 && StringWrapper.substring(url, url.length - 11) == '/index.html') { return StringWrapper.substring(url, 0, url.length - 11); diff --git a/modules/angular2/src/router/path_recognizer.js b/modules/angular2/src/router/path_recognizer.js index ac1fff5188..12a26c8f41 100644 --- a/modules/angular2/src/router/path_recognizer.js +++ b/modules/angular2/src/router/path_recognizer.js @@ -1,4 +1,4 @@ -import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; +import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent, isBlank, BaseException, normalizeBlank} from 'angular2/src/facade/lang'; import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; import {escapeRegex} from './url'; @@ -7,13 +7,14 @@ class StaticSegment { string:string; regex:string; name:string; + constructor(string:string) { this.string = string; this.name = ''; this.regex = escapeRegex(string); } - generate(params) { + generate(params): string { return this.string; } } @@ -26,11 +27,11 @@ class DynamicSegment { this.regex = "([^/]+)"; } - generate(params:StringMap) { + generate(params:StringMap): string { if (!StringMapWrapper.contains(params, this.name)) { throw new BaseException(`Route generator for '${this.name}' was not included in parameters passed.`) } - return StringMapWrapper.get(params, this.name); + return normalizeBlank(StringMapWrapper.get(params, this.name)); } } @@ -43,8 +44,8 @@ class StarSegment { this.regex = "(.+)"; } - generate(params:StringMap) { - return StringMapWrapper.get(params, this.name); + generate(params:StringMap): string { + return normalizeBlank(StringMapWrapper.get(params, this.name)); } } @@ -82,9 +83,8 @@ function parsePathString(route:string) { return {segments: results, cost}; } -var SLASH_RE = RegExpWrapper.create('/'); function splitBySlash (url:string):List { - return StringWrapper.split(url, SLASH_RE); + return url.split('/'); } @@ -97,7 +97,7 @@ export class PathRecognizer { constructor(path:string, handler:any) { this.handler = handler; - this.segments = ListWrapper.create(); + this.segments = []; // TODO: use destructuring assignment // see https://github.com/angular/ts2dart/issues/158 @@ -115,7 +115,7 @@ export class PathRecognizer { this.cost = cost; } - parseParams(url:string):StringMap { + parseParams(url:string):StringMap { var params = StringMapWrapper.create(); var urlPart = url; for(var i=0; i):string { return ListWrapper.join(ListWrapper.map(this.segments, (segment) => '/' + segment.generate(params)), ''); } diff --git a/modules/angular2/src/router/pipeline.js b/modules/angular2/src/router/pipeline.js index 0245ccd343..ab2363ecac 100644 --- a/modules/angular2/src/router/pipeline.js +++ b/modules/angular2/src/router/pipeline.js @@ -7,7 +7,8 @@ import {Instruction} from './instruction'; * "Steps" are conceptually similar to "middleware" */ export class Pipeline { - steps:List; + steps:List; + constructor() { this.steps = [ instruction => instruction.traverseSync((parentInstruction, childInstruction) => { diff --git a/modules/angular2/src/router/route_config_impl.js b/modules/angular2/src/router/route_config_impl.js index 505a9b912f..625727b5d2 100644 --- a/modules/angular2/src/router/route_config_impl.js +++ b/modules/angular2/src/router/route_config_impl.js @@ -11,6 +11,7 @@ import {List, Map} from 'angular2/src/facade/collection'; */ export class RouteConfig { configs:List; + @CONST() constructor(configs:List) { this.configs = configs; diff --git a/modules/angular2/src/router/route_recognizer.js b/modules/angular2/src/router/route_recognizer.js index 4598ddf9f6..a8990c52b0 100644 --- a/modules/angular2/src/router/route_recognizer.js +++ b/modules/angular2/src/router/route_recognizer.js @@ -14,11 +14,11 @@ export class RouteRecognizer { this.redirects = MapWrapper.create(); } - addRedirect(path:string, target:string) { + addRedirect(path:string, target:string): void { MapWrapper.set(this.redirects, path, target); } - addConfig(path:string, handler:any, alias:string = null) { + addConfig(path:string, handler:any, alias:string = null): void { var recognizer = new PathRecognizer(path, handler); MapWrapper.set(this.matchers, recognizer.regex, recognizer); if (isPresent(alias)) { @@ -59,12 +59,12 @@ export class RouteRecognizer { return solutions; } - hasRoute(name:string) { + hasRoute(name:string): boolean { return MapWrapper.contains(this.names, name); } - generate(name:string, params:any) { + generate(name:string, params:any): string { var pathRecognizer = MapWrapper.get(this.names, name); - return pathRecognizer.generate(params); + return isPresent(pathRecognizer) ? pathRecognizer.generate(params) : null; } } diff --git a/modules/angular2/src/router/route_registry.js b/modules/angular2/src/router/route_registry.js index 5ac6af9e3a..920fdd294f 100644 --- a/modules/angular2/src/router/route_registry.js +++ b/modules/angular2/src/router/route_registry.js @@ -12,7 +12,7 @@ export class RouteRegistry { this._rules = MapWrapper.create(); } - config(parentComponent, config) { + config(parentComponent, config: StringMap): void { if (!StringMapWrapper.contains(config, 'path')) { throw new BaseException('Route config does not contain "path"'); } @@ -23,10 +23,9 @@ export class RouteRegistry { throw new BaseException('Route config does not contain "component," "components," or "redirectTo"'); } - var recognizer:RouteRecognizer; - if (MapWrapper.contains(this._rules, parentComponent)) { - recognizer = MapWrapper.get(this._rules, parentComponent); - } else { + var recognizer:RouteRecognizer = MapWrapper.get(this._rules, parentComponent); + + if (isBlank(recognizer)) { recognizer = new RouteRecognizer(); MapWrapper.set(this._rules, parentComponent, recognizer); } @@ -46,7 +45,7 @@ export class RouteRegistry { recognizer.addConfig(config['path'], config, config['as']); } - configFromComponent(component) { + configFromComponent(component): void { if (!isType(component)) { return; } @@ -71,14 +70,14 @@ export class RouteRegistry { } - recognize(url:string, parentComponent) { + recognize(url:string, parentComponent): Instruction { var componentRecognizer = MapWrapper.get(this._rules, parentComponent); if (isBlank(componentRecognizer)) { return null; } var componentSolutions = componentRecognizer.recognize(url); - var fullSolutions = ListWrapper.create(); + var fullSolutions = []; for(var i = 0; i < componentSolutions.length; i++) { var candidate = componentSolutions[i]; @@ -120,16 +119,14 @@ export class RouteRegistry { return null; } - generate(name:string, params:any, hostComponent) { + generate(name:string, params:StringMap, hostComponent): string { //TODO: implement for hierarchical routes var componentRecognizer = MapWrapper.get(this._rules, hostComponent); - if (isPresent(componentRecognizer)) { - return componentRecognizer.generate(name, params); - } + return isPresent(componentRecognizer) ? componentRecognizer.generate(name, params) : null; } } -function handlerToLeafInstructions(context, parentComponent) { +function handlerToLeafInstructions(context, parentComponent): Instruction { var children = StringMapWrapper.create(); StringMapWrapper.forEach(context['handler']['components'], (component, outletName) => { children[outletName] = new Instruction({ @@ -150,7 +147,7 @@ function handlerToLeafInstructions(context, parentComponent) { // { component: Foo } // mutates the config to: // { components: { default: Foo } } -function normalizeConfig(config:StringMap) { +function normalizeConfig(config:StringMap): StringMap { if (StringMapWrapper.contains(config, 'component')) { var component = StringMapWrapper.get(config, 'component'); var components = StringMapWrapper.create(); diff --git a/modules/angular2/src/router/router.js b/modules/angular2/src/router/router.js index 0ec0da47e7..de614f98cb 100644 --- a/modules/angular2/src/router/router.js +++ b/modules/angular2/src/router/router.js @@ -51,18 +51,22 @@ export class Router { /** * Constructs a child router. You probably don't need to use this unless you're writing a reusable component. */ - childRouter(outletName = 'default') { - if (!MapWrapper.contains(this._children, outletName)) { - MapWrapper.set(this._children, outletName, new ChildRouter(this, outletName)); + childRouter(outletName = 'default'): Router { + var router = MapWrapper.get(this._children, outletName); + + if (isBlank(router)) { + router = new ChildRouter(this, outletName); + MapWrapper.set(this._children, outletName, router); } - return MapWrapper.get(this._children, outletName); + + return router; } /** * Register an object to notify of route changes. You probably don't need to use this unless you're writing a reusable component. */ - registerOutlet(outlet:RouterOutlet, name = 'default'):Promise { + registerOutlet(outlet:RouterOutlet, name: string = 'default'):Promise { MapWrapper.set(this._outlets, name, outlet); if (isPresent(this._currentInstruction)) { var childInstruction = this._currentInstruction.getChildInstruction(name); @@ -91,12 +95,12 @@ export class Router { * ``` * */ - config(config:any) { + config(config:any): Promise { if (config instanceof List) { config.forEach((configObject) => { // TODO: this is a hack this._registry.config(this.hostComponent, configObject); - }) + }); } else { this._registry.config(this.hostComponent, config); } @@ -140,18 +144,18 @@ export class Router { return result; } - _startNavigating() { + _startNavigating(): void { this.navigating = true; } - _finishNavigating() { + _finishNavigating(): void { this.navigating = false; } /** * Subscribe to URL updates from the router */ - subscribe(onNext) { + subscribe(onNext): void { ObservableWrapper.subscribe(this._subject, onNext); } @@ -182,7 +186,7 @@ export class Router { /** * Given a URL, returns an instruction representing the component graph */ - recognize(url:string) { + recognize(url:string): Instruction { return this._registry.recognize(url, this.hostComponent); } @@ -202,7 +206,7 @@ export class Router { /** * Generate a URL from a component name and optional map of parameters. The URL is relative to the app's base href. */ - generate(name:string, params:any) { + generate(name:string, params:StringMap): string { return this._registry.generate(name, params, this.hostComponent); } } @@ -223,7 +227,7 @@ class ChildRouter extends Router { } } -function mapObjAsync(obj:Map, fn) { +function mapObjAsync(obj:Map, fn): Promise { return PromiseWrapper.all(mapObj(obj, fn)); } diff --git a/modules/angular2/src/router/router_link.js b/modules/angular2/src/router/router_link.js index e49fcf9a63..c4ec644abc 100644 --- a/modules/angular2/src/router/router_link.js +++ b/modules/angular2/src/router/router_link.js @@ -40,7 +40,7 @@ import {Location} from './location'; export class RouterLink { _domEl; _route:string; - _params:any; + _params:StringMap; _router:Router; _location:Location; _href:string; @@ -56,15 +56,15 @@ export class RouterLink { }); } - set route(changes) { + set route(changes: string) { this._route = changes; } - set params(changes) { + set params(changes: StringMap) { this._params = changes; } - onAllChangesDone() { + onAllChangesDone(): void { if (isPresent(this._route) && isPresent(this._params)) { var newHref = this._router.generate(this._route, this._params); this._href = this._location.normalizeAbsolutely(newHref); diff --git a/modules/angular2/src/router/router_outlet.js b/modules/angular2/src/router/router_outlet.js index 448aa6f24e..a0f95c0b89 100644 --- a/modules/angular2/src/router/router_outlet.js +++ b/modules/angular2/src/router/router_outlet.js @@ -29,7 +29,7 @@ export class RouterOutlet { this._router.registerOutlet(this, nameAttr); } - activate(instruction:Instruction) { + activate(instruction:Instruction): Promise { return this._compiler.compileInHost(instruction.component).then((pv) => { var outletInjector = this._injector.resolveAndCreateChild([ bind(RouteParams).toValue(new RouteParams(instruction.params)), @@ -41,11 +41,11 @@ export class RouterOutlet { }); } - canActivate(instruction:any) { + canActivate(instruction:Instruction): Promise { return PromiseWrapper.resolve(true); } - canDeactivate(instruction:any) { + canDeactivate(instruction:Instruction): Promise { return PromiseWrapper.resolve(true); } } diff --git a/modules/angular2/src/router/url.js b/modules/angular2/src/router/url.js index 5b0085ee80..0df9751e4a 100644 --- a/modules/angular2/src/router/url.js +++ b/modules/angular2/src/router/url.js @@ -6,7 +6,7 @@ var specialCharacters = [ var escapeRe = RegExpWrapper.create('(\\' + specialCharacters.join('|\\') + ')', 'g'); -export function escapeRegex(string:string) { +export function escapeRegex(string:string): string { return StringWrapper.replaceAllMapped(string, escapeRe, (match) => { return "\\" + match; });