2016-06-08 14:13:41 -04:00
|
|
|
import {PRIMARY_OUTLET} from './shared';
|
2016-06-14 17:55:59 -04:00
|
|
|
import {DefaultUrlSerializer, serializePath, serializePaths} from './url_serializer';
|
|
|
|
import {forEach, shallowEqual} from './utils/collection';
|
2016-05-26 19:50:59 -04:00
|
|
|
|
|
|
|
export function createEmptyUrlTree() {
|
2016-06-14 17:55:59 -04:00
|
|
|
return new UrlTree(new UrlSegment([], {}), {}, null);
|
2016-05-26 19:50:59 -04:00
|
|
|
}
|
2016-05-21 20:35:55 -04:00
|
|
|
|
2016-06-15 12:01:05 -04:00
|
|
|
export function containsTree(container: UrlTree, containee: UrlTree, exact: boolean): boolean {
|
|
|
|
if (exact) {
|
|
|
|
return equalSegments(container.root, containee.root);
|
|
|
|
} else {
|
|
|
|
return containsSegment(container.root, containee.root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function equalSegments(container: UrlSegment, containee: UrlSegment): boolean {
|
|
|
|
if (!equalPath(container.pathsWithParams, containee.pathsWithParams)) return false;
|
|
|
|
if (Object.keys(container.children).length !== Object.keys(containee.children).length)
|
|
|
|
return false;
|
|
|
|
for (let c in containee.children) {
|
|
|
|
if (!container.children[c]) return false;
|
|
|
|
if (!equalSegments(container.children[c], containee.children[c])) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function containsSegment(container: UrlSegment, containee: UrlSegment): boolean {
|
|
|
|
return containsSegmentHelper(container, containee, containee.pathsWithParams);
|
|
|
|
}
|
|
|
|
|
|
|
|
function containsSegmentHelper(
|
|
|
|
container: UrlSegment, containee: UrlSegment, containeePaths: UrlPathWithParams[]): boolean {
|
|
|
|
if (container.pathsWithParams.length > containeePaths.length) {
|
|
|
|
const current = container.pathsWithParams.slice(0, containeePaths.length);
|
|
|
|
if (!equalPath(current, containeePaths)) return false;
|
|
|
|
if (Object.keys(containee.children).length > 0) return false;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else if (container.pathsWithParams.length === containeePaths.length) {
|
|
|
|
if (!equalPath(container.pathsWithParams, containeePaths)) return false;
|
|
|
|
for (let c in containee.children) {
|
|
|
|
if (!container.children[c]) return false;
|
|
|
|
if (!containsSegment(container.children[c], containee.children[c])) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
const current = containeePaths.slice(0, container.pathsWithParams.length);
|
|
|
|
const next = containeePaths.slice(container.pathsWithParams.length);
|
|
|
|
if (!equalPath(container.pathsWithParams, current)) return false;
|
|
|
|
return containsSegmentHelper(container.children[PRIMARY_OUTLET], containee, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-24 16:41:37 -04:00
|
|
|
/**
|
|
|
|
* A URL in the tree form.
|
|
|
|
*/
|
2016-06-14 17:55:59 -04:00
|
|
|
export class UrlTree {
|
2016-06-02 18:18:34 -04:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2016-06-08 14:13:41 -04:00
|
|
|
constructor(
|
2016-06-14 17:55:59 -04:00
|
|
|
public root: UrlSegment, public queryParams: {[key: string]: string},
|
2016-06-15 12:14:41 -04:00
|
|
|
public fragment: string) {}
|
2016-06-09 17:11:54 -04:00
|
|
|
|
2016-06-09 17:33:09 -04:00
|
|
|
toString(): string { return new DefaultUrlSerializer().serialize(this); }
|
2016-05-21 20:35:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export class UrlSegment {
|
2016-06-15 12:14:41 -04:00
|
|
|
public parent: UrlSegment = null;
|
2016-06-08 14:13:41 -04:00
|
|
|
constructor(
|
2016-06-14 17:55:59 -04:00
|
|
|
public pathsWithParams: UrlPathWithParams[], public children: {[key: string]: UrlSegment}) {
|
|
|
|
forEach(children, (v, k) => v.parent = this);
|
|
|
|
}
|
2016-05-23 19:14:23 -04:00
|
|
|
|
2016-06-14 17:55:59 -04:00
|
|
|
toString(): string { return serializePaths(this); }
|
2016-05-23 19:14:23 -04:00
|
|
|
}
|
|
|
|
|
2016-06-14 17:55:59 -04:00
|
|
|
export class UrlPathWithParams {
|
|
|
|
constructor(public path: string, public parameters: {[key: string]: string}) {}
|
|
|
|
toString(): string { return serializePath(this); }
|
|
|
|
}
|
|
|
|
|
|
|
|
export function equalPathsWithParams(a: UrlPathWithParams[], b: UrlPathWithParams[]): boolean {
|
2016-05-23 19:14:23 -04:00
|
|
|
if (a.length !== b.length) return false;
|
|
|
|
for (let i = 0; i < a.length; ++i) {
|
|
|
|
if (a[i].path !== b[i].path) return false;
|
|
|
|
if (!shallowEqual(a[i].parameters, b[i].parameters)) return false;
|
2016-06-15 12:01:05 -04:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function equalPath(a: UrlPathWithParams[], b: UrlPathWithParams[]): boolean {
|
|
|
|
if (a.length !== b.length) return false;
|
|
|
|
for (let i = 0; i < a.length; ++i) {
|
|
|
|
if (a[i].path !== b[i].path) return false;
|
2016-05-23 19:14:23 -04:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-06-14 17:55:59 -04:00
|
|
|
|
|
|
|
export function mapChildren(segment: UrlSegment, fn: (v: UrlSegment, k: string) => UrlSegment):
|
|
|
|
{[name: string]: UrlSegment} {
|
2016-06-15 12:14:41 -04:00
|
|
|
const newChildren: {[name: string]: UrlSegment} = {};
|
2016-06-14 17:55:59 -04:00
|
|
|
forEach(segment.children, (child, childOutlet) => {
|
|
|
|
if (childOutlet === PRIMARY_OUTLET) {
|
|
|
|
newChildren[childOutlet] = fn(child, childOutlet);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
forEach(segment.children, (child, childOutlet) => {
|
|
|
|
if (childOutlet !== PRIMARY_OUTLET) {
|
|
|
|
newChildren[childOutlet] = fn(child, childOutlet);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return newChildren;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mapChildrenIntoArray<T>(
|
|
|
|
segment: UrlSegment, fn: (v: UrlSegment, k: string) => T[]): T[] {
|
|
|
|
let res = [];
|
|
|
|
forEach(segment.children, (child, childOutlet) => {
|
|
|
|
if (childOutlet === PRIMARY_OUTLET) {
|
|
|
|
res = res.concat(fn(child, childOutlet));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
forEach(segment.children, (child, childOutlet) => {
|
|
|
|
if (childOutlet !== PRIMARY_OUTLET) {
|
|
|
|
res = res.concat(fn(child, childOutlet));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return res;
|
|
|
|
}
|