(function (root, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define([], factory); } else { root.urltemplate = factory(); } }(this, function () { /** * @constructor */ function UrlTemplate() { } /** * @private * @param {string} str * @return {string} */ UrlTemplate.prototype.encodeReserved = function (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(''); }; /** * @private * @param {string} str * @return {string} */ UrlTemplate.prototype.encodeUnreserved = function (str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase(); }); } /** * @private * @param {string} operator * @param {string} value * @param {string} key * @return {string} */ UrlTemplate.prototype.encodeValue = function (operator, value, key) { value = (operator === '+' || operator === '#') ? this.encodeReserved(value) : this.encodeUnreserved(value); if (key) { return this.encodeUnreserved(key) + '=' + value; } else { return value; } }; /** * @private * @param {*} value * @return {boolean} */ UrlTemplate.prototype.isDefined = function (value) { return value !== undefined && value !== null; }; /** * @private * @param {string} * @return {boolean} */ UrlTemplate.prototype.isKeyOperator = function (operator) { return operator === ';' || operator === '&' || operator === '?'; }; /** * @private * @param {Object} context * @param {string} operator * @param {string} key * @param {string} modifier */ UrlTemplate.prototype.getValues = function (context, operator, key, modifier) { var value = context[key], result = []; if (this.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(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); } else { if (modifier === '*') { if (Array.isArray(value)) { value.filter(this.isDefined).forEach(function (value) { result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); }, this); } else { Object.keys(value).forEach(function (k) { if (this.isDefined(value[k])) { result.push(this.encodeValue(operator, value[k], k)); } }, this); } } else { var tmp = []; if (Array.isArray(value)) { value.filter(this.isDefined).forEach(function (value) { tmp.push(this.encodeValue(operator, value)); }, this); } else { Object.keys(value).forEach(function (k) { if (this.isDefined(value[k])) { tmp.push(this.encodeUnreserved(k)); tmp.push(this.encodeValue(operator, value[k].toString())); } }, this); } if (this.isKeyOperator(operator)) { result.push(this.encodeUnreserved(key) + '=' + tmp.join(',')); } else if (tmp.length !== 0) { result.push(tmp.join(',')); } } } } else { if (operator === ';') { if (this.isDefined(value)) { result.push(this.encodeUnreserved(key)); } } else if (value === '' && (operator === '&' || operator === '?')) { result.push(this.encodeUnreserved(key) + '='); } else if (value === '') { result.push(''); } } return result; }; /** * @param {string} template * @return {function(Object):string} */ UrlTemplate.prototype.parse = function (template) { var that = this; var operators = ['+', '#', '.', '/', ';', '?', '&']; return { expand: function (context) { return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { if (expression) { var operator = null, 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.apply(values, that.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 that.encodeReserved(literal); } }); } }; }; return new UrlTemplate(); }));