| // 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); |
| } |
| }); |
| } |