blob: 1cfbc175b403e7aa5925c8ec5126b3cc69dd4a0a [file] [log] [blame]
/** @module common-sk/modules/query
* @descripiton Utilities for working with URL query strings.
*/
/** fromParamSet encodes an object of the form:
* <pre>
* {
* a:["2", "4"],
* b:["3"]
* }
* </pre>
*
* to a query string like:
*
* <pre>
* "a=2&a=4&b=3"
* </pre>
*
* This function handles URI encoding of both keys and values.
*
* @param {Object} o The object to encode.
* @returns {string}
*/
export function fromParamSet(o) {
if (!o) {
return "";
}
var ret = [];
var keys = Object.keys(o).sort();
keys.forEach(function(key) {
o[key].forEach(function(value) {
ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
});
});
return ret.join('&');
}
/** toParamSet parses a query string into an object with
* arrays of values for the values. I.e.
*
* <pre>
* "a=2&b=3&a=4"
* </pre>
*
* decodes to
*
* <pre>
* {
* a:["2", "4"],
* b:["3"],
* }
* </pre>
*
* This function handles URI decoding of both keys and values.
*
* @param {string} s The query string to decode.
* @returns {Object}
*/
export function toParamSet(s) {
s = s || '';
var ret = {};
var vars = s.split("&");
for (var i=0; i<vars.length; i++) {
var pair = vars[i].split("=", 2);
if (pair.length == 2) {
var key = decodeURIComponent(pair[0]);
var value = decodeURIComponent(pair[1]);
if (ret.hasOwnProperty(key)) {
ret[key].push(value);
} else {
ret[key] = [value];
}
}
}
return ret;
}
/** fromObject takes an object and encodes it into a query string.
*
* The reverse of this function is toObject.
*
* @param {Object} o The object to encode.
* @return {string}
*/
export function fromObject(o) {
var ret = [];
Object.keys(o).sort().forEach(function(key) {
if (Array.isArray(o[key])) {
o[key].forEach(function(value) {
ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
})
} else if (typeof(o[key]) == 'object') {
ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(fromObject(o[key])));
} else {
ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(o[key]));
}
});
return ret.join('&');
}
/** toObject decodes a query string into an object.
*
* Uses the 'target' as a source for hinting on the types of the values.
* For example:
*
* <pre>
* "a=2&b=true"
* </pre>
*
* decodes to:
*
* <pre>
* {
* a: 2,
* b: true,
* }
* </pre>
*
* When given a target of:
*
* <pre>
* {
* a: 1.0,
* b: false,
* }
* </pre>
*
* Note that a target of {} would decode
* the same query string into:
*
* <pre>
* {
* a: "2",
* b: "true",
* }
* </pre>
*
* Only Number, String, Boolean, Object, and Array of String hints are supported.
*
* @param {string} s The query string.
* @param {Object} target The object that contains the type hints.
* @returns {Object}
*/
export function toObject(s, target) {
var target = target || {};
var ret = {};
var vars = s.split("&");
for (var i=0; i<vars.length; i++) {
var pair = vars[i].split("=", 2);
if (pair.length == 2) {
var key = decodeURIComponent(pair[0]);
var value = decodeURIComponent(pair[1]);
if (target.hasOwnProperty(key)) {
switch (typeof(target[key])) {
case 'boolean':
ret[key] = value=="true";
break;
case 'number':
ret[key] = Number(value);
break;
case 'object': // Arrays report as 'object' to typeof.
if (Array.isArray(target[key])) {
var r = ret[key] || [];
r.push(value);
ret[key] = r;
} else {
ret[key] = toObject(value, target[key]);
}
break;
case 'string':
ret[key] = value;
break;
default:
ret[key] = value;
}
} else {
ret[key] = value;
}
}
}
return ret;
}
/** splitAmp returns the given query string as a newline
* separated list of key value pairs. If sepator is not
* provided newline will be used.
*
* @param {string} [queryStr=''] A query string.
* @param {string} [separator='\n'] The separator to use when joining.
* @returns {string}
*/
export function splitAmp(queryStr = '', separator = '\n') {
return queryStr.split('&').join(separator);
};