165 lines
6.2 KiB
JavaScript
165 lines
6.2 KiB
JavaScript
|
// Based on https://github.com/bramstein/url-template, licensed under BSD
|
||
|
// TODO: create separate package.
|
||
|
//
|
||
|
// Copyright (c) 2012-2014, Bram Stein
|
||
|
// All rights reserved.
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions
|
||
|
// are met:
|
||
|
// 1. Redistributions of source code must retain the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer.
|
||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer in the
|
||
|
// documentation and/or other materials provided with the distribution.
|
||
|
// 3. The name of the author may not be used to endorse or promote products
|
||
|
// derived from this software without specific prior written permission.
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
|
||
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||
|
// EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
|
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
/* istanbul ignore file */
|
||
|
function encodeReserved(str) {
|
||
|
return str
|
||
|
.split(/(%[0-9A-Fa-f]{2})/g)
|
||
|
.map(function (part) {
|
||
|
if (!/%[0-9A-Fa-f]/.test(part)) {
|
||
|
part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]");
|
||
|
}
|
||
|
return part;
|
||
|
})
|
||
|
.join("");
|
||
|
}
|
||
|
function encodeUnreserved(str) {
|
||
|
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
|
||
|
return "%" + c.charCodeAt(0).toString(16).toUpperCase();
|
||
|
});
|
||
|
}
|
||
|
function encodeValue(operator, value, key) {
|
||
|
value =
|
||
|
operator === "+" || operator === "#"
|
||
|
? encodeReserved(value)
|
||
|
: encodeUnreserved(value);
|
||
|
if (key) {
|
||
|
return encodeUnreserved(key) + "=" + value;
|
||
|
}
|
||
|
else {
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
function isDefined(value) {
|
||
|
return value !== undefined && value !== null;
|
||
|
}
|
||
|
function isKeyOperator(operator) {
|
||
|
return operator === ";" || operator === "&" || operator === "?";
|
||
|
}
|
||
|
function getValues(context, operator, key, modifier) {
|
||
|
var value = context[key], result = [];
|
||
|
if (isDefined(value) && value !== "") {
|
||
|
if (typeof value === "string" ||
|
||
|
typeof value === "number" ||
|
||
|
typeof value === "boolean") {
|
||
|
value = value.toString();
|
||
|
if (modifier && modifier !== "*") {
|
||
|
value = value.substring(0, parseInt(modifier, 10));
|
||
|
}
|
||
|
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
|
||
|
}
|
||
|
else {
|
||
|
if (modifier === "*") {
|
||
|
if (Array.isArray(value)) {
|
||
|
value.filter(isDefined).forEach(function (value) {
|
||
|
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
Object.keys(value).forEach(function (k) {
|
||
|
if (isDefined(value[k])) {
|
||
|
result.push(encodeValue(operator, value[k], k));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
const tmp = [];
|
||
|
if (Array.isArray(value)) {
|
||
|
value.filter(isDefined).forEach(function (value) {
|
||
|
tmp.push(encodeValue(operator, value));
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
Object.keys(value).forEach(function (k) {
|
||
|
if (isDefined(value[k])) {
|
||
|
tmp.push(encodeUnreserved(k));
|
||
|
tmp.push(encodeValue(operator, value[k].toString()));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
if (isKeyOperator(operator)) {
|
||
|
result.push(encodeUnreserved(key) + "=" + tmp.join(","));
|
||
|
}
|
||
|
else if (tmp.length !== 0) {
|
||
|
result.push(tmp.join(","));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (operator === ";") {
|
||
|
if (isDefined(value)) {
|
||
|
result.push(encodeUnreserved(key));
|
||
|
}
|
||
|
}
|
||
|
else if (value === "" && (operator === "&" || operator === "?")) {
|
||
|
result.push(encodeUnreserved(key) + "=");
|
||
|
}
|
||
|
else if (value === "") {
|
||
|
result.push("");
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
export function parseUrl(template) {
|
||
|
return {
|
||
|
expand: expand.bind(null, template),
|
||
|
};
|
||
|
}
|
||
|
function expand(template, context) {
|
||
|
var operators = ["+", "#", ".", "/", ";", "?", "&"];
|
||
|
return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) {
|
||
|
if (expression) {
|
||
|
let operator = "";
|
||
|
const values = [];
|
||
|
if (operators.indexOf(expression.charAt(0)) !== -1) {
|
||
|
operator = expression.charAt(0);
|
||
|
expression = expression.substr(1);
|
||
|
}
|
||
|
expression.split(/,/g).forEach(function (variable) {
|
||
|
var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
|
||
|
values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
|
||
|
});
|
||
|
if (operator && operator !== "+") {
|
||
|
var separator = ",";
|
||
|
if (operator === "?") {
|
||
|
separator = "&";
|
||
|
}
|
||
|
else if (operator !== "#") {
|
||
|
separator = operator;
|
||
|
}
|
||
|
return (values.length !== 0 ? operator : "") + values.join(separator);
|
||
|
}
|
||
|
else {
|
||
|
return values.join(",");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
return encodeReserved(literal);
|
||
|
}
|
||
|
});
|
||
|
}
|