angular-cn/modules/@angular/router/src/router_url_serializer.ts

200 lines
5.3 KiB
TypeScript

import {UrlSegment, Tree, TreeNode, rootNode, UrlTree} from './segments';
import {BaseException} from '@angular/core';
import {isBlank, isPresent, RegExpWrapper} from './facade/lang';
/**
* Defines a way to serialize/deserialize a url tree.
*/
export abstract class RouterUrlSerializer {
/**
* Parse a url into a {@Link UrlTree}
*/
abstract parse(url: string): UrlTree;
/**
* Converts a {@Link UrlTree} into a url
*/
abstract serialize(tree: UrlTree): string;
}
/**
* A default implementation of the serialization.
*/
export class DefaultRouterUrlSerializer extends RouterUrlSerializer {
parse(url: string): UrlTree {
let root = new _UrlParser().parse(url);
return new UrlTree(root);
}
serialize(tree: UrlTree): string { return _serializeUrlTreeNode(rootNode(tree)); }
}
function _serializeUrlTreeNode(node: TreeNode<UrlSegment>): string {
return `${node.value}${_serializeChildren(node)}`;
}
function _serializeUrlTreeNodes(nodes: TreeNode<UrlSegment>[]): string {
let main = nodes[0].value.toString();
let auxNodes = nodes.slice(1);
let aux = auxNodes.length > 0 ? `(${auxNodes.map(_serializeUrlTreeNode).join("//")})` : "";
let children = _serializeChildren(nodes[0]);
return `${main}${aux}${children}`;
}
function _serializeChildren(node: TreeNode<UrlSegment>): string {
if (node.children.length > 0) {
return `/${_serializeUrlTreeNodes(node.children)}`;
} else {
return "";
}
}
var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&#]+');
function matchUrlSegment(str: string): string {
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
return isPresent(match) ? match[0] : '';
}
var QUERY_PARAM_VALUE_RE = RegExpWrapper.create('^[^\\(\\)\\?;&#]+');
function matchUrlQueryParamValue(str: string): string {
var match = RegExpWrapper.firstMatch(QUERY_PARAM_VALUE_RE, str);
return isPresent(match) ? match[0] : '';
}
class _UrlParser {
private _remaining: string;
peekStartsWith(str: string): boolean { return this._remaining.startsWith(str); }
capture(str: string): void {
if (!this._remaining.startsWith(str)) {
throw new BaseException(`Expected "${str}".`);
}
this._remaining = this._remaining.substring(str.length);
}
parse(url: string): TreeNode<UrlSegment> {
this._remaining = url;
if (url == '' || url == '/') {
return new TreeNode<UrlSegment>(new UrlSegment('', {}, null), []);
} else {
return this.parseRoot();
}
}
parseRoot(): TreeNode<UrlSegment> {
let segments = this.parseSegments();
return new TreeNode<UrlSegment>(new UrlSegment('', {}, null), segments);
}
parseSegments(outletName: string = null): TreeNode<UrlSegment>[] {
if (this._remaining.length == 0) {
return [];
}
if (this.peekStartsWith('/')) {
this.capture('/');
}
var path = matchUrlSegment(this._remaining);
this.capture(path);
if (path.indexOf(":") > -1) {
let parts = path.split(":");
outletName = parts[0];
path = parts[1];
}
var matrixParams: {[key: string]: any} = {};
if (this.peekStartsWith(';')) {
matrixParams = this.parseMatrixParams();
}
var aux = [];
if (this.peekStartsWith('(')) {
aux = this.parseAuxiliaryRoutes();
}
var children: TreeNode<UrlSegment>[] = [];
if (this.peekStartsWith('/') && !this.peekStartsWith('//')) {
this.capture('/');
children = this.parseSegments();
}
let segment = new UrlSegment(path, matrixParams, outletName);
let node = new TreeNode<UrlSegment>(segment, children);
return [node].concat(aux);
}
parseQueryParams(): {[key: string]: any} {
var params: {[key: string]: any} = {};
this.capture('?');
this.parseQueryParam(params);
while (this._remaining.length > 0 && this.peekStartsWith('&')) {
this.capture('&');
this.parseQueryParam(params);
}
return params;
}
parseMatrixParams(): {[key: string]: any} {
var params: {[key: string]: any} = {};
while (this._remaining.length > 0 && this.peekStartsWith(';')) {
this.capture(';');
this.parseParam(params);
}
return params;
}
parseParam(params: {[key: string]: any}): void {
var key = matchUrlSegment(this._remaining);
if (isBlank(key)) {
return;
}
this.capture(key);
var value: any = "true";
if (this.peekStartsWith('=')) {
this.capture('=');
var valueMatch = matchUrlSegment(this._remaining);
if (isPresent(valueMatch)) {
value = valueMatch;
this.capture(value);
}
}
params[key] = value;
}
parseQueryParam(params: {[key: string]: any}): void {
var key = matchUrlSegment(this._remaining);
if (isBlank(key)) {
return;
}
this.capture(key);
var value: any = "true";
if (this.peekStartsWith('=')) {
this.capture('=');
var valueMatch = matchUrlQueryParamValue(this._remaining);
if (isPresent(valueMatch)) {
value = valueMatch;
this.capture(value);
}
}
params[key] = value;
}
parseAuxiliaryRoutes(): TreeNode<UrlSegment>[] {
var segments = [];
this.capture('(');
while (!this.peekStartsWith(')') && this._remaining.length > 0) {
segments = segments.concat(this.parseSegments("aux"));
if (this.peekStartsWith('//')) {
this.capture('//');
}
}
this.capture(')');
return segments;
}
}