; | |
var has = Object.prototype.hasOwnProperty | |
, undef; | |
/** | |
* Decode a URI encoded string. | |
* | |
* @param {String} input The URI encoded string. | |
* @returns {String|Null} The decoded string. | |
* @api private | |
*/ | |
function decode(input) { | |
try { | |
return decodeURIComponent(input.replace(/\+/g, ' ')); | |
} catch (e) { | |
return null; | |
} | |
} | |
/** | |
* Attempts to encode a given input. | |
* | |
* @param {String} input The string that needs to be encoded. | |
* @returns {String|Null} The encoded string. | |
* @api private | |
*/ | |
function encode(input) { | |
try { | |
return encodeURIComponent(input); | |
} catch (e) { | |
return null; | |
} | |
} | |
/** | |
* Simple query string parser. | |
* | |
* @param {String} query The query string that needs to be parsed. | |
* @returns {Object} | |
* @api public | |
*/ | |
function querystring(query) { | |
var parser = /([^=?#&]+)=?([^&]*)/g | |
, result = {} | |
, part; | |
while (part = parser.exec(query)) { | |
var key = decode(part[1]) | |
, value = decode(part[2]); | |
// | |
// Prevent overriding of existing properties. This ensures that build-in | |
// methods like `toString` or __proto__ are not overriden by malicious | |
// querystrings. | |
// | |
// In the case if failed decoding, we want to omit the key/value pairs | |
// from the result. | |
// | |
if (key === null || value === null || key in result) continue; | |
result[key] = value; | |
} | |
return result; | |
} | |
/** | |
* Transform a query string to an object. | |
* | |
* @param {Object} obj Object that should be transformed. | |
* @param {String} prefix Optional prefix. | |
* @returns {String} | |
* @api public | |
*/ | |
function querystringify(obj, prefix) { | |
prefix = prefix || ''; | |
var pairs = [] | |
, value | |
, key; | |
// | |
// Optionally prefix with a '?' if needed | |
// | |
if ('string' !== typeof prefix) prefix = '?'; | |
for (key in obj) { | |
if (has.call(obj, key)) { | |
value = obj[key]; | |
// | |
// Edge cases where we actually want to encode the value to an empty | |
// string instead of the stringified value. | |
// | |
if (!value && (value === null || value === undef || isNaN(value))) { | |
value = ''; | |
} | |
key = encode(key); | |
value = encode(value); | |
// | |
// If we failed to encode the strings, we should bail out as we don't | |
// want to add invalid strings to the query. | |
// | |
if (key === null || value === null) continue; | |
pairs.push(key +'='+ value); | |
} | |
} | |
return pairs.length ? prefix + pairs.join('&') : ''; | |
} | |
// | |
// Expose the module. | |
// | |
exports.stringify = querystringify; | |
exports.parse = querystring; | |