2016-05-27 18:36:17 +00:00
|
|
|
import { flatten, first, merge } from './utils/collection';
|
2016-06-02 22:30:49 +00:00
|
|
|
import { TreeNode } from './utils/tree';
|
|
|
|
|
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
|
2016-05-27 18:36:17 +00:00
|
|
|
import { PRIMARY_OUTLET } from './shared';
|
|
|
|
|
import { Observable } from 'rxjs/Observable';
|
2016-06-02 22:30:49 +00:00
|
|
|
export function recognize(rootComponentType, config, url) {
|
2016-05-27 18:36:17 +00:00
|
|
|
try {
|
2016-06-02 22:30:49 +00:00
|
|
|
const match = new MatchResult(rootComponentType, config, [url.root], {}, url._root.children, [], PRIMARY_OUTLET, null, url.root);
|
|
|
|
|
const roots = constructActivatedRoute(match);
|
|
|
|
|
const res = new RouterStateSnapshot(roots[0], url.queryParameters, url.fragment);
|
2016-05-27 18:36:17 +00:00
|
|
|
return new Observable(obs => {
|
|
|
|
|
obs.next(res);
|
|
|
|
|
obs.complete();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
return new Observable(obs => obs.error(e));
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
function constructActivatedRoute(match) {
|
|
|
|
|
const activatedRoute = createActivatedRouteSnapshot(match);
|
|
|
|
|
const children = match.leftOverUrl.length > 0 ?
|
|
|
|
|
recognizeMany(match.children, match.leftOverUrl) : recognizeLeftOvers(match.children, match.lastUrlSegment);
|
|
|
|
|
checkOutletNameUniqueness(children);
|
|
|
|
|
children.sort((a, b) => {
|
|
|
|
|
if (a.value.outlet === PRIMARY_OUTLET)
|
|
|
|
|
return -1;
|
|
|
|
|
if (b.value.outlet === PRIMARY_OUTLET)
|
|
|
|
|
return 1;
|
|
|
|
|
return a.value.outlet.localeCompare(b.value.outlet);
|
|
|
|
|
});
|
|
|
|
|
return [new TreeNode(activatedRoute, children)];
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
function recognizeLeftOvers(config, lastUrlSegment) {
|
|
|
|
|
if (!config)
|
|
|
|
|
return [];
|
|
|
|
|
const mIndex = matchIndex(config, [], lastUrlSegment);
|
|
|
|
|
return mIndex ? constructActivatedRoute(mIndex) : [];
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
function recognizeMany(config, urls) {
|
|
|
|
|
return flatten(urls.map(url => recognizeOne(config, url)));
|
|
|
|
|
}
|
|
|
|
|
function createActivatedRouteSnapshot(match) {
|
|
|
|
|
return new ActivatedRouteSnapshot(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route, match.lastUrlSegment);
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
function recognizeOne(config, url) {
|
|
|
|
|
const m = match(config, url);
|
|
|
|
|
const primary = constructActivatedRoute(m);
|
|
|
|
|
const secondary = recognizeMany(config, m.secondary);
|
2016-05-27 18:36:17 +00:00
|
|
|
const res = primary.concat(secondary);
|
|
|
|
|
checkOutletNameUniqueness(res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
function checkOutletNameUniqueness(nodes) {
|
|
|
|
|
let names = {};
|
|
|
|
|
nodes.forEach(n => {
|
|
|
|
|
let routeWithSameOutletName = names[n.value.outlet];
|
|
|
|
|
if (routeWithSameOutletName) {
|
2016-06-02 22:30:49 +00:00
|
|
|
const p = routeWithSameOutletName.urlSegments.map(s => s.toString()).join("/");
|
|
|
|
|
const c = n.value.urlSegments.map(s => s.toString()).join("/");
|
2016-05-27 18:36:17 +00:00
|
|
|
throw new Error(`Two segments cannot have the same outlet name: '${p}' and '${c}'.`);
|
|
|
|
|
}
|
|
|
|
|
names[n.value.outlet] = n.value;
|
|
|
|
|
});
|
|
|
|
|
return nodes;
|
|
|
|
|
}
|
|
|
|
|
function match(config, url) {
|
|
|
|
|
const m = matchNonIndex(config, url);
|
|
|
|
|
if (m)
|
|
|
|
|
return m;
|
2016-06-02 22:30:49 +00:00
|
|
|
const mIndex = matchIndex(config, [url], url.value);
|
2016-05-27 18:36:17 +00:00
|
|
|
if (mIndex)
|
|
|
|
|
return mIndex;
|
|
|
|
|
const availableRoutes = config.map(r => {
|
|
|
|
|
const outlet = !r.outlet ? '' : `${r.outlet}:`;
|
|
|
|
|
return `'${outlet}${r.path}'`;
|
|
|
|
|
}).join(", ");
|
|
|
|
|
throw new Error(`Cannot match any routes. Current segment: '${url.value}'. Available routes: [${availableRoutes}].`);
|
|
|
|
|
}
|
|
|
|
|
function matchNonIndex(config, url) {
|
|
|
|
|
for (let r of config) {
|
|
|
|
|
let m = matchWithParts(r, url);
|
|
|
|
|
if (m)
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
function matchIndex(config, leftOverUrls, lastUrlSegment) {
|
2016-05-27 18:36:17 +00:00
|
|
|
for (let r of config) {
|
|
|
|
|
if (r.index) {
|
|
|
|
|
const outlet = r.outlet ? r.outlet : PRIMARY_OUTLET;
|
|
|
|
|
const children = r.children ? r.children : [];
|
2016-06-02 22:30:49 +00:00
|
|
|
return new MatchResult(r.component, children, [], lastUrlSegment.parameters, leftOverUrls, [], outlet, r, lastUrlSegment);
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
function matchWithParts(route, url) {
|
|
|
|
|
if (!route.path)
|
|
|
|
|
return null;
|
|
|
|
|
if ((route.outlet ? route.outlet : PRIMARY_OUTLET) !== url.value.outlet)
|
|
|
|
|
return null;
|
|
|
|
|
const path = route.path.startsWith("/") ? route.path.substring(1) : route.path;
|
|
|
|
|
if (path === "**") {
|
|
|
|
|
const consumedUrl = [];
|
|
|
|
|
let u = url;
|
|
|
|
|
while (u) {
|
|
|
|
|
consumedUrl.push(u.value);
|
|
|
|
|
u = first(u.children);
|
|
|
|
|
}
|
|
|
|
|
const last = consumedUrl[consumedUrl.length - 1];
|
2016-06-02 22:30:49 +00:00
|
|
|
return new MatchResult(route.component, [], consumedUrl, last.parameters, [], [], PRIMARY_OUTLET, route, last);
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
|
|
|
|
const parts = path.split("/");
|
|
|
|
|
const positionalParams = {};
|
|
|
|
|
const consumedUrlSegments = [];
|
|
|
|
|
let lastParent = null;
|
|
|
|
|
let lastSegment = null;
|
|
|
|
|
let current = url;
|
|
|
|
|
for (let i = 0; i < parts.length; ++i) {
|
|
|
|
|
if (!current)
|
|
|
|
|
return null;
|
|
|
|
|
const p = parts[i];
|
|
|
|
|
const isLastSegment = i === parts.length - 1;
|
|
|
|
|
const isLastParent = i === parts.length - 2;
|
|
|
|
|
const isPosParam = p.startsWith(":");
|
|
|
|
|
if (!isPosParam && p != current.value.path)
|
|
|
|
|
return null;
|
|
|
|
|
if (isLastSegment) {
|
|
|
|
|
lastSegment = current;
|
|
|
|
|
}
|
|
|
|
|
if (isLastParent) {
|
|
|
|
|
lastParent = current;
|
|
|
|
|
}
|
|
|
|
|
if (isPosParam) {
|
|
|
|
|
positionalParams[p.substring(1)] = current.value.path;
|
|
|
|
|
}
|
|
|
|
|
consumedUrlSegments.push(current.value);
|
|
|
|
|
current = first(current.children);
|
|
|
|
|
}
|
|
|
|
|
if (!lastSegment)
|
|
|
|
|
throw "Cannot be reached";
|
|
|
|
|
const p = lastSegment.value.parameters;
|
|
|
|
|
const parameters = merge(p, positionalParams);
|
|
|
|
|
const secondarySubtrees = lastParent ? lastParent.children.slice(1) : [];
|
|
|
|
|
const children = route.children ? route.children : [];
|
|
|
|
|
const outlet = route.outlet ? route.outlet : PRIMARY_OUTLET;
|
2016-06-02 22:30:49 +00:00
|
|
|
return new MatchResult(route.component, children, consumedUrlSegments, parameters, lastSegment.children, secondarySubtrees, outlet, route, lastSegment.value);
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
|
|
|
|
class MatchResult {
|
2016-06-02 22:30:49 +00:00
|
|
|
constructor(component, children, consumedUrlSegments, parameters, leftOverUrl, secondary, outlet, route, lastUrlSegment) {
|
2016-05-27 18:36:17 +00:00
|
|
|
this.component = component;
|
|
|
|
|
this.children = children;
|
|
|
|
|
this.consumedUrlSegments = consumedUrlSegments;
|
|
|
|
|
this.parameters = parameters;
|
|
|
|
|
this.leftOverUrl = leftOverUrl;
|
|
|
|
|
this.secondary = secondary;
|
|
|
|
|
this.outlet = outlet;
|
2016-06-02 22:30:49 +00:00
|
|
|
this.route = route;
|
|
|
|
|
this.lastUrlSegment = lastUrlSegment;
|
2016-05-27 18:36:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-02 22:30:49 +00:00
|
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb2duaXplLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3JlY29nbml6ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiT0FDTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sb0JBQW9CO09BQ25ELEVBQUUsUUFBUSxFQUFFLE1BQU0sY0FBYztPQUNoQyxFQUFFLG1CQUFtQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sZ0JBQWdCO09BQ3JFLEVBQVUsY0FBYyxFQUFFLE1BQU0sVUFBVTtPQUcxQyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlCQUFpQjtBQUU1QywwQkFBMEIsaUJBQXVCLEVBQUUsTUFBb0IsRUFBRSxHQUFZO0lBQ25GLElBQUksQ0FBQztRQUNILE1BQU0sS0FBSyxHQUFHLElBQUksV0FBVyxDQUFDLGlCQUFpQixFQUFFLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsR0FBRyxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsRUFBRSxFQUFFLGNBQWMsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2pJLE1BQU0sS0FBSyxHQUFHLHVCQUF1QixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzdDLE1BQU0sR0FBRyxHQUFHLElBQUksbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxlQUFlLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2pGLE1BQU0sQ0FBQyxJQUFJLFVBQVUsQ0FBc0IsR0FBRztZQUM1QyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ2QsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2pCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBRTtJQUFBLEtBQUssQ0FBQSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDVixNQUFNLENBQUMsSUFB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
|