You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.4 KiB
192 lines
5.4 KiB
(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(); |
|
}));
|
|
|