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

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