react-router
Version:
A complete routing library for React.js
167 lines (132 loc) • 4.09 kB
JavaScript
var invariant = require('react/lib/invariant');
var merge = require('qs/lib/utils').merge;
var qs = require('qs');
function encodeURL(url) {
return encodeURIComponent(url).replace(/%20/g, '+');
}
function decodeURL(url) {
return decodeURIComponent(url.replace(/\+/g, ' '));
}
function encodeURLPath(path) {
return String(path).split('/').map(encodeURL).join('/');
}
var paramMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
var queryMatcher = /\?(.+)/;
var _compiledPatterns = {};
function compilePattern(pattern) {
if (!(pattern in _compiledPatterns)) {
var paramNames = [];
var source = pattern.replace(paramMatcher, function (match, paramName) {
if (paramName) {
paramNames.push(paramName);
return '([^/?#]+)';
} else if (match === '*') {
paramNames.push('splat');
return '(.*?)';
} else {
return '\\' + match;
}
});
_compiledPatterns[pattern] = {
matcher: new RegExp('^' + source + '$', 'i'),
paramNames: paramNames
};
}
return _compiledPatterns[pattern];
}
var Path = {
/**
* Returns an array of the names of all parameters in the given pattern.
*/
extractParamNames: function (pattern) {
return compilePattern(pattern).paramNames;
},
/**
* Extracts the portions of the given URL path that match the given pattern
* and returns an object of param name => value pairs. Returns null if the
* pattern does not match the given path.
*/
extractParams: function (pattern, path) {
var object = compilePattern(pattern);
var match = decodeURL(path).match(object.matcher);
if (!match)
return null;
var params = {};
object.paramNames.forEach(function (paramName, index) {
params[paramName] = match[index + 1];
});
return params;
},
/**
* Returns a version of the given route path with params interpolated. Throws
* if there is a dynamic segment of the route path for which there is no param.
*/
injectParams: function (pattern, params) {
params = params || {};
var splatIndex = 0;
return pattern.replace(paramMatcher, function (match, paramName) {
paramName = paramName || 'splat';
invariant(
params[paramName] != null,
'Missing "' + paramName + '" parameter for path "' + pattern + '"'
);
var segment;
if (paramName === 'splat' && Array.isArray(params[paramName])) {
segment = params[paramName][splatIndex++];
invariant(
segment != null,
'Missing splat # ' + splatIndex + ' for path "' + pattern + '"'
);
} else {
segment = params[paramName];
}
return encodeURLPath(segment);
});
},
/**
* Returns an object that is the result of parsing any query string contained
* in the given path, null if the path contains no query string.
*/
extractQuery: function (path) {
var match = decodeURL(path).match(queryMatcher);
return match && qs.parse(match[1]);
},
/**
* Returns a version of the given path without the query string.
*/
withoutQuery: function (path) {
return path.replace(queryMatcher, '');
},
/**
* Returns a version of the given path with the parameters in the given
* query merged into the query string.
*/
withQuery: function (path, query) {
var existingQuery = Path.extractQuery(path);
if (existingQuery)
query = query ? merge(existingQuery, query) : existingQuery;
var queryString = query && qs.stringify(query);
if (queryString)
return Path.withoutQuery(path) + '?' + queryString;
return path;
},
/**
* Returns true if the given path is absolute.
*/
isAbsolute: function (path) {
return path.charAt(0) === '/';
},
/**
* Returns a normalized version of the given path.
*/
normalize: function (path, parentRoute) {
return path.replace(/^\/*/, '/');
},
/**
* Joins two URL paths together.
*/
join: function (a, b) {
return a.replace(/\/*$/, '/') + b;
}
};
module.exports = Path;