angular-cn/modules/angular2/src/router/route_registry.js
2015-05-18 19:32:33 +02:00

180 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {RouteRecognizer} from './route_recognizer';
import {Instruction, noopInstruction} from './instruction';
import {List, ListWrapper, Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank, isType, StringWrapper, BaseException} from 'angular2/src/facade/lang';
import {RouteConfig} from './route_config_impl';
import {reflector} from 'angular2/src/reflection/reflection';
export class RouteRegistry {
_rules:Map<any, RouteRecognizer>;
constructor() {
this._rules = MapWrapper.create();
}
config(parentComponent, config: StringMap<string, any>): void {
if (!StringMapWrapper.contains(config, 'path')) {
throw new BaseException('Route config does not contain "path"');
}
if (!StringMapWrapper.contains(config, 'component') &&
!StringMapWrapper.contains(config, 'components') &&
!StringMapWrapper.contains(config, 'redirectTo')) {
throw new BaseException('Route config does not contain "component," "components," or "redirectTo"');
}
var recognizer:RouteRecognizer = MapWrapper.get(this._rules, parentComponent);
if (isBlank(recognizer)) {
recognizer = new RouteRecognizer();
MapWrapper.set(this._rules, parentComponent, recognizer);
}
config = normalizeConfig(config);
if (StringMapWrapper.contains(config, 'redirectTo')) {
recognizer.addRedirect(StringMapWrapper.get(config, 'path'), StringMapWrapper.get(config, 'redirectTo'));
return;
}
var components = StringMapWrapper.get(config, 'components');
StringMapWrapper.forEach(components, (component, _) => {
this.configFromComponent(component);
});
recognizer.addConfig(config['path'], config, config['as']);
}
configFromComponent(component): void {
if (!isType(component)) {
return;
}
// Don't read the annotations from a type more than once
// this prevents an infinite loop if a component routes recursively.
if (MapWrapper.contains(this._rules, component)) {
return;
}
var annotations = reflector.annotations(component);
if (isPresent(annotations)) {
for (var i=0; i<annotations.length; i++) {
var annotation = annotations[i];
if (annotation instanceof RouteConfig) {
ListWrapper.forEach(annotation.configs, (config) => {
this.config(component, config);
})
}
}
}
}
recognize(url:string, parentComponent): Instruction {
var componentRecognizer = MapWrapper.get(this._rules, parentComponent);
if (isBlank(componentRecognizer)) {
return null;
}
var componentSolutions = componentRecognizer.recognize(url);
var fullSolutions = [];
for(var i = 0; i < componentSolutions.length; i++) {
var candidate = componentSolutions[i];
if (candidate['unmatchedUrl'].length == 0) {
ListWrapper.push(fullSolutions, handlerToLeafInstructions(candidate, parentComponent));
} else {
var children = StringMapWrapper.create(),
allMapped = true;
var components = candidate['handler']['components'];
var componentNames = StringMapWrapper.keys(components);
for (var cmpIndex = 0; cmpIndex < componentNames.length; cmpIndex++) {
var name = componentNames[cmpIndex];
var component = components[name];
var childInstruction = this.recognize(candidate['unmatchedUrl'], component);
if (isPresent(childInstruction)) {
childInstruction.params = candidate['params'];
children[name] = childInstruction;
} else {
allMapped = false;
break;
}
}
if (allMapped) {
ListWrapper.push(fullSolutions, new Instruction({
component: parentComponent,
children: children,
matchedUrl: candidate['matchedUrl'],
parentCost: candidate['cost']
}));
}
}
}
if (fullSolutions.length > 0) {
var lowCost = fullSolutions[0].cost;
var lowIndex = 0;
for (var solIdx = 1; solIdx < fullSolutions.length; solIdx++) {
if (fullSolutions[solIdx].cost < lowCost) {
lowCost = fullSolutions[solIdx].cost;
lowIndex = solIdx;
}
}
return fullSolutions[lowIndex];
}
return null;
}
generate(name:string, params:StringMap<string, string>, hostComponent): string {
//TODO: implement for hierarchical routes
var componentRecognizer = MapWrapper.get(this._rules, hostComponent);
return isPresent(componentRecognizer) ? componentRecognizer.generate(name, params) : null;
}
}
function handlerToLeafInstructions(context, parentComponent): Instruction {
var children = StringMapWrapper.create();
StringMapWrapper.forEach(context['handler']['components'], (component, outletName) => {
children[outletName] = new Instruction({
component: component,
params: context['params'],
parentCost: 0
});
});
return new Instruction({
component: parentComponent,
children: children,
matchedUrl: context['matchedUrl'],
parentCost: context['cost']
});
}
// given:
// { component: Foo }
// mutates the config to:
// { components: { default: Foo } }
function normalizeConfig(config:StringMap<string, any>): StringMap<string, any> {
if (StringMapWrapper.contains(config, 'component')) {
var component = StringMapWrapper.get(config, 'component');
var components = StringMapWrapper.create();
StringMapWrapper.set(components, 'default', component);
var newConfig = StringMapWrapper.create();
StringMapWrapper.set(newConfig, 'components', components);
StringMapWrapper.forEach(config, (value, key) => {
if (!StringWrapper.equals(key, 'component') && !StringWrapper.equals(key, 'components')) {
StringMapWrapper.set(newConfig, key, value);
}
});
return newConfig;
}
return config;
}