@angular/router-deprecated
Version:
269 lines • 11.8 kB
JavaScript
"use strict";
var collection_1 = require('../../facade/collection');
var exceptions_1 = require('../../facade/exceptions');
var lang_1 = require('../../facade/lang');
var url_parser_1 = require('../../url_parser');
var utils_1 = require('../../utils');
var route_path_1 = require('./route_path');
/**
* Identified by a `...` URL segment. This indicates that the
* Route will continue to be matched by child `Router`s.
*/
var ContinuationPathSegment = (function () {
function ContinuationPathSegment() {
this.name = '';
this.specificity = '';
this.hash = '...';
}
ContinuationPathSegment.prototype.generate = function (params) { return ''; };
ContinuationPathSegment.prototype.match = function (path) { return true; };
return ContinuationPathSegment;
}());
/**
* Identified by a string not starting with a `:` or `*`.
* Only matches the URL segments that equal the segment path
*/
var StaticPathSegment = (function () {
function StaticPathSegment(path) {
this.path = path;
this.name = '';
this.specificity = '2';
this.hash = path;
}
StaticPathSegment.prototype.match = function (path) { return path == this.path; };
StaticPathSegment.prototype.generate = function (params) { return this.path; };
return StaticPathSegment;
}());
/**
* Identified by a string starting with `:`. Indicates a segment
* that can contain a value that will be extracted and provided to
* a matching `Instruction`.
*/
var DynamicPathSegment = (function () {
function DynamicPathSegment(name) {
this.name = name;
this.specificity = '1';
this.hash = ':';
}
DynamicPathSegment.prototype.match = function (path) { return path.length > 0; };
DynamicPathSegment.prototype.generate = function (params) {
if (!collection_1.StringMapWrapper.contains(params.map, this.name)) {
throw new exceptions_1.BaseException("Route generator for '" + this.name + "' was not included in parameters passed.");
}
return encodeDynamicSegment(utils_1.normalizeString(params.get(this.name)));
};
DynamicPathSegment.paramMatcher = /^:([^\/]+)$/g;
return DynamicPathSegment;
}());
/**
* Identified by a string starting with `*` Indicates that all the following
* segments match this route and that the value of these segments should
* be provided to a matching `Instruction`.
*/
var StarPathSegment = (function () {
function StarPathSegment(name) {
this.name = name;
this.specificity = '0';
this.hash = '*';
}
StarPathSegment.prototype.match = function (path) { return true; };
StarPathSegment.prototype.generate = function (params) { return utils_1.normalizeString(params.get(this.name)); };
StarPathSegment.wildcardMatcher = /^\*([^\/]+)$/g;
return StarPathSegment;
}());
/**
* Parses a URL string using a given matcher DSL, and generates URLs from param maps
*/
var ParamRoutePath = (function () {
/**
* Takes a string representing the matcher DSL
*/
function ParamRoutePath(routePath) {
this.routePath = routePath;
this.terminal = true;
this._assertValidPath(routePath);
this._parsePathString(routePath);
this.specificity = this._calculateSpecificity();
this.hash = this._calculateHash();
var lastSegment = this._segments[this._segments.length - 1];
this.terminal = !(lastSegment instanceof ContinuationPathSegment);
}
ParamRoutePath.prototype.matchUrl = function (url) {
var nextUrlSegment = url;
var currentUrlSegment;
var positionalParams = {};
var captured = [];
for (var i = 0; i < this._segments.length; i += 1) {
var pathSegment = this._segments[i];
if (pathSegment instanceof ContinuationPathSegment) {
break;
}
currentUrlSegment = nextUrlSegment;
if (lang_1.isPresent(currentUrlSegment)) {
// the star segment consumes all of the remaining URL, including matrix params
if (pathSegment instanceof StarPathSegment) {
positionalParams[pathSegment.name] =
currentUrlSegment.toString();
captured.push(currentUrlSegment.toString());
nextUrlSegment = null;
break;
}
captured.push(currentUrlSegment.path);
if (pathSegment instanceof DynamicPathSegment) {
positionalParams[pathSegment.name] =
decodeDynamicSegment(currentUrlSegment.path);
}
else if (!pathSegment.match(currentUrlSegment.path)) {
return null;
}
nextUrlSegment = currentUrlSegment.child;
}
else if (!pathSegment.match('')) {
return null;
}
}
if (this.terminal && lang_1.isPresent(nextUrlSegment)) {
return null;
}
var urlPath = captured.join('/');
var auxiliary = [];
var urlParams = [];
var allParams = positionalParams;
if (lang_1.isPresent(currentUrlSegment)) {
// If this is the root component, read query params. Otherwise, read matrix params.
var paramsSegment = url instanceof url_parser_1.RootUrl ? url : currentUrlSegment;
if (lang_1.isPresent(paramsSegment.params)) {
allParams = collection_1.StringMapWrapper.merge(paramsSegment.params, positionalParams);
urlParams = url_parser_1.convertUrlParamsToArray(paramsSegment.params);
}
else {
allParams = positionalParams;
}
auxiliary = currentUrlSegment.auxiliary;
}
return new route_path_1.MatchedUrl(urlPath, urlParams, allParams, auxiliary, nextUrlSegment);
};
ParamRoutePath.prototype.generateUrl = function (params) {
var paramTokens = new utils_1.TouchMap(params);
var path = [];
for (var i = 0; i < this._segments.length; i++) {
var segment = this._segments[i];
if (!(segment instanceof ContinuationPathSegment)) {
path.push(segment.generate(paramTokens));
}
}
var urlPath = path.join('/');
var nonPositionalParams = paramTokens.getUnused();
var urlParams = nonPositionalParams;
return new route_path_1.GeneratedUrl(urlPath, urlParams);
};
ParamRoutePath.prototype.toString = function () { return this.routePath; };
ParamRoutePath.prototype._parsePathString = function (routePath) {
// normalize route as not starting with a "/". Recognition will
// also normalize.
if (routePath.startsWith('/')) {
routePath = routePath.substring(1);
}
var segmentStrings = routePath.split('/');
this._segments = [];
var limit = segmentStrings.length - 1;
for (var i = 0; i <= limit; i++) {
var segment = segmentStrings[i], match;
if (lang_1.isPresent(match = lang_1.RegExpWrapper.firstMatch(DynamicPathSegment.paramMatcher, segment))) {
this._segments.push(new DynamicPathSegment(match[1]));
}
else if (lang_1.isPresent(match = lang_1.RegExpWrapper.firstMatch(StarPathSegment.wildcardMatcher, segment))) {
this._segments.push(new StarPathSegment(match[1]));
}
else if (segment == '...') {
if (i < limit) {
throw new exceptions_1.BaseException("Unexpected \"...\" before the end of the path for \"" + routePath + "\".");
}
this._segments.push(new ContinuationPathSegment());
}
else {
this._segments.push(new StaticPathSegment(segment));
}
}
};
ParamRoutePath.prototype._calculateSpecificity = function () {
// The "specificity" of a path is used to determine which route is used when multiple routes
// match
// a URL. Static segments (like "/foo") are the most specific, followed by dynamic segments
// (like
// "/:id"). Star segments add no specificity. Segments at the start of the path are more
// specific
// than proceeding ones.
//
// The code below uses place values to combine the different types of segments into a single
// string that we can sort later. Each static segment is marked as a specificity of "2," each
// dynamic segment is worth "1" specificity, and stars are worth "0" specificity.
var i /** TODO #9100 */, length = this._segments.length, specificity;
if (length == 0) {
// a single slash (or "empty segment" is as specific as a static segment
specificity += '2';
}
else {
specificity = '';
for (i = 0; i < length; i++) {
specificity += this._segments[i].specificity;
}
}
return specificity;
};
ParamRoutePath.prototype._calculateHash = function () {
// this function is used to determine whether a route config path like `/foo/:id` collides with
// `/foo/:name`
var i /** TODO #9100 */, length = this._segments.length;
var hashParts = [];
for (i = 0; i < length; i++) {
hashParts.push(this._segments[i].hash);
}
return hashParts.join('/');
};
ParamRoutePath.prototype._assertValidPath = function (path) {
if (lang_1.StringWrapper.contains(path, '#')) {
throw new exceptions_1.BaseException("Path \"" + path + "\" should not include \"#\". Use \"HashLocationStrategy\" instead.");
}
var illegalCharacter = lang_1.RegExpWrapper.firstMatch(ParamRoutePath.RESERVED_CHARS, path);
if (lang_1.isPresent(illegalCharacter)) {
throw new exceptions_1.BaseException("Path \"" + path + "\" contains \"" + illegalCharacter[0] + "\" which is not allowed in a route config.");
}
};
ParamRoutePath.RESERVED_CHARS = lang_1.RegExpWrapper.create('//|\\(|\\)|;|\\?|=');
return ParamRoutePath;
}());
exports.ParamRoutePath = ParamRoutePath;
var REGEXP_PERCENT = /%/g;
var REGEXP_SLASH = /\//g;
var REGEXP_OPEN_PARENT = /\(/g;
var REGEXP_CLOSE_PARENT = /\)/g;
var REGEXP_SEMICOLON = /;/g;
function encodeDynamicSegment(value) {
if (lang_1.isBlank(value)) {
return null;
}
value = lang_1.StringWrapper.replaceAll(value, REGEXP_PERCENT, '%25');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_SLASH, '%2F');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_OPEN_PARENT, '%28');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_CLOSE_PARENT, '%29');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_SEMICOLON, '%3B');
return value;
}
var REGEXP_ENC_SEMICOLON = /%3B/ig;
var REGEXP_ENC_CLOSE_PARENT = /%29/ig;
var REGEXP_ENC_OPEN_PARENT = /%28/ig;
var REGEXP_ENC_SLASH = /%2F/ig;
var REGEXP_ENC_PERCENT = /%25/ig;
function decodeDynamicSegment(value) {
if (lang_1.isBlank(value)) {
return null;
}
value = lang_1.StringWrapper.replaceAll(value, REGEXP_ENC_SEMICOLON, ';');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_ENC_CLOSE_PARENT, ')');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_ENC_OPEN_PARENT, '(');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_ENC_SLASH, '/');
value = lang_1.StringWrapper.replaceAll(value, REGEXP_ENC_PERCENT, '%');
return value;
}
//# sourceMappingURL=param_route_path.js.map