2015-05-29 14:58:41 -07:00
|
|
|
import {
|
|
|
|
RegExp,
|
|
|
|
RegExpWrapper,
|
|
|
|
StringWrapper,
|
2015-06-30 13:18:51 -07:00
|
|
|
isBlank,
|
2015-05-29 14:58:41 -07:00
|
|
|
isPresent,
|
2015-06-30 13:18:51 -07:00
|
|
|
isType,
|
|
|
|
isStringMap,
|
2015-05-29 14:58:41 -07:00
|
|
|
BaseException
|
|
|
|
} from 'angular2/src/facade/lang';
|
|
|
|
import {
|
|
|
|
Map,
|
|
|
|
MapWrapper,
|
|
|
|
List,
|
|
|
|
ListWrapper,
|
|
|
|
StringMap,
|
|
|
|
StringMapWrapper
|
|
|
|
} from 'angular2/src/facade/collection';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-06-30 13:18:51 -07:00
|
|
|
import {PathRecognizer} from './path_recognizer';
|
|
|
|
import {RouteHandler} from './route_handler';
|
2015-07-13 16:12:48 -07:00
|
|
|
import {Route, AsyncRoute, Redirect, RouteDefinition} from './route_config_impl';
|
2015-06-30 13:18:51 -07:00
|
|
|
import {AsyncRouteHandler} from './async_route_handler';
|
|
|
|
import {SyncRouteHandler} from './sync_route_handler';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-05-15 02:05:57 -07:00
|
|
|
/**
|
|
|
|
* `RouteRecognizer` is responsible for recognizing routes for a single component.
|
2015-05-29 14:58:41 -07:00
|
|
|
* It is consumed by `RouteRegistry`, which knows how to recognize an entire hierarchy of
|
|
|
|
* components.
|
2015-05-15 02:05:57 -07:00
|
|
|
*/
|
2015-04-17 09:59:56 -07:00
|
|
|
export class RouteRecognizer {
|
2015-06-29 10:37:55 +02:00
|
|
|
names: Map<string, PathRecognizer> = new Map();
|
|
|
|
redirects: Map<string, string> = new Map();
|
|
|
|
matchers: Map<RegExp, PathRecognizer> = new Map();
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-07-13 16:12:48 -07:00
|
|
|
config(config: RouteDefinition): boolean {
|
|
|
|
var handler;
|
|
|
|
if (config instanceof Redirect) {
|
|
|
|
let path = config.path == '/' ? '' : config.path;
|
|
|
|
this.redirects.set(path, config.redirectTo);
|
|
|
|
return true;
|
|
|
|
} else if (config instanceof Route) {
|
|
|
|
handler = new SyncRouteHandler(config.component);
|
|
|
|
} else if (config instanceof AsyncRoute) {
|
|
|
|
handler = new AsyncRouteHandler(config.loader);
|
2015-06-17 11:57:38 -07:00
|
|
|
}
|
2015-07-13 16:12:48 -07:00
|
|
|
var recognizer = new PathRecognizer(config.path, handler);
|
2015-05-15 02:05:57 -07:00
|
|
|
MapWrapper.forEach(this.matchers, (matcher, _) => {
|
|
|
|
if (recognizer.regex.toString() == matcher.regex.toString()) {
|
2015-05-29 14:58:41 -07:00
|
|
|
throw new BaseException(
|
2015-07-13 16:12:48 -07:00
|
|
|
`Configuration '${config.path}' conflicts with existing route '${matcher.path}'`);
|
2015-05-15 02:05:57 -07:00
|
|
|
}
|
|
|
|
});
|
2015-06-17 16:21:40 -07:00
|
|
|
this.matchers.set(recognizer.regex, recognizer);
|
2015-07-13 16:12:48 -07:00
|
|
|
if (isPresent(config.as)) {
|
|
|
|
this.names.set(config.as, recognizer);
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
2015-06-17 11:57:38 -07:00
|
|
|
return recognizer.terminal;
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
2015-05-15 02:05:57 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a URL, returns a list of `RouteMatch`es, which are partial recognitions for some route.
|
|
|
|
*
|
|
|
|
*/
|
2015-05-29 14:58:41 -07:00
|
|
|
recognize(url: string): List<RouteMatch> {
|
2015-06-17 11:17:21 -07:00
|
|
|
var solutions = [];
|
2015-06-17 11:57:38 -07:00
|
|
|
if (url.length > 0 && url[url.length - 1] == '/') {
|
|
|
|
url = url.substring(0, url.length - 1);
|
|
|
|
}
|
2015-05-15 02:05:57 -07:00
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
MapWrapper.forEach(this.redirects, (target, path) => {
|
2015-06-15 00:45:02 -07:00
|
|
|
// "/" redirect case
|
|
|
|
if (path == '/' || path == '') {
|
|
|
|
if (path == url) {
|
|
|
|
url = target;
|
|
|
|
}
|
2015-06-30 13:18:51 -07:00
|
|
|
} else if (url.startsWith(path)) {
|
|
|
|
url = target + url.substring(path.length);
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
MapWrapper.forEach(this.matchers, (pathRecognizer, regex) => {
|
|
|
|
var match;
|
|
|
|
if (isPresent(match = RegExpWrapper.firstMatch(regex, url))) {
|
2015-05-15 02:05:57 -07:00
|
|
|
var matchedUrl = '/';
|
|
|
|
var unmatchedUrl = '';
|
|
|
|
if (url != '/') {
|
|
|
|
matchedUrl = match[0];
|
2015-06-30 13:18:51 -07:00
|
|
|
unmatchedUrl = url.substring(match[0].length);
|
2015-04-29 15:47:12 -07:00
|
|
|
}
|
2015-06-30 13:18:51 -07:00
|
|
|
solutions.push(new RouteMatch(pathRecognizer, matchedUrl, unmatchedUrl));
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return solutions;
|
|
|
|
}
|
|
|
|
|
2015-06-17 21:42:56 -07:00
|
|
|
hasRoute(name: string): boolean { return this.names.has(name); }
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-06-30 13:18:51 -07:00
|
|
|
generate(name: string, params: any): StringMap<string, any> {
|
|
|
|
var pathRecognizer: PathRecognizer = this.names.get(name);
|
|
|
|
if (isBlank(pathRecognizer)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var url = pathRecognizer.generate(params);
|
|
|
|
return {url, 'nextComponent': pathRecognizer.handler.componentType};
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
}
|
2015-05-15 02:05:57 -07:00
|
|
|
|
|
|
|
export class RouteMatch {
|
2015-06-30 13:18:51 -07:00
|
|
|
constructor(public recognizer: PathRecognizer, public matchedUrl: string,
|
|
|
|
public unmatchedUrl: string) {}
|
|
|
|
|
|
|
|
params(): StringMap<string, string> { return this.recognizer.parseParams(this.matchedUrl); }
|
|
|
|
}
|
2015-06-29 10:37:55 +02:00
|
|
|
|
2015-06-30 13:18:51 -07:00
|
|
|
function configObjToHandler(config: any): RouteHandler {
|
|
|
|
if (isType(config)) {
|
|
|
|
return new SyncRouteHandler(config);
|
|
|
|
} else if (isStringMap(config)) {
|
|
|
|
if (isBlank(config['type'])) {
|
|
|
|
throw new BaseException(
|
|
|
|
`Component declaration when provided as a map should include a 'type' property`);
|
|
|
|
}
|
|
|
|
var componentType = config['type'];
|
|
|
|
if (componentType == 'constructor') {
|
|
|
|
return new SyncRouteHandler(config['constructor']);
|
|
|
|
} else if (componentType == 'loader') {
|
|
|
|
return new AsyncRouteHandler(config['loader']);
|
|
|
|
} else {
|
|
|
|
throw new BaseException(`oops`);
|
|
|
|
}
|
2015-05-15 02:05:57 -07:00
|
|
|
}
|
2015-06-30 13:18:51 -07:00
|
|
|
throw new BaseException(`Unexpected component "${config}".`);
|
2015-05-15 02:05:57 -07:00
|
|
|
}
|