0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-29 23:16:30 +01:00
nodejs/lib/querystring.js

113 lines
3.6 KiB
JavaScript
Raw Normal View History

// Query String Utilities
var QueryString = exports;
var urlDecode = process.binding("http_parser").urlDecode;
// a safe fast alternative to decodeURIComponent
QueryString.unescape = urlDecode;
QueryString.escape = function (str) {
return encodeURIComponent(str);
};
var stack = [];
/**
* <p>Converts an arbitrary value to a Query String representation.</p>
*
* <p>Objects with cyclical references will trigger an exception.</p>
*
* @method stringify
* @param obj {Variant} any arbitrary value to convert to query string
* @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&"
* @param eq {String} (optional) Character that should join keys to their values. Default: "="
* @param munge {Boolean} (optional) Indicate whether array/object params should be munged, PHP/Rails-style. Default: true
* @param name {String} (optional) Name of the current key, for handling children recursively.
* @static
*/
QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name) {
munge = typeof munge == "undefined" || munge;
sep = sep || "&";
eq = eq || "=";
var type = Object.prototype.toString.call(obj);
if (obj == null || type == "[object Function]" || type == "[object Number]" && !isFinite(obj)) {
return name ? QueryString.escape(name) + eq : "";
}
2010-04-11 22:46:24 +02:00
switch (type) {
case '[object Boolean]':
obj = +obj; // fall through
case '[object Number]':
case '[object String]':
return QueryString.escape(name) + eq + QueryString.escape(obj);
case '[object Array]':
name = name + (munge ? "[]" : "");
return obj.map(function (item) {
return QueryString.stringify(item, sep, eq, munge, name);
}).join(sep);
}
// now we know it's an object.
2010-04-11 22:46:24 +02:00
// Check for cyclical references in nested objects
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] === obj) {
throw new Error("querystring.stringify. Cyclical reference");
}
2010-04-11 22:46:24 +02:00
stack.push(obj);
2010-04-11 22:46:24 +02:00
var begin = name ? name + "[" : "",
end = name ? "]" : "",
keys = Object.keys(obj),
n,
s = Object.keys(obj).map(function (key) {
n = begin + key + end;
return QueryString.stringify(obj[key], sep, eq, munge, n);
}).join(sep);
2010-04-11 22:46:24 +02:00
stack.pop();
2010-04-11 22:46:24 +02:00
if (!s && name) {
return name + "=";
}
return s;
};
// matches .xxxxx or [xxxxx] or ['xxxxx'] or ["xxxxx"] with optional [] at the end
var chunks = /(?:(?:^|\.)([^\[\(\.]+)(?=\[|\.|$|\()|\[([^"'][^\]]*?)\]|\["([^\]"]*?)"\]|\['([^\]']*?)'\])(\[\])?/g;
// Parse a key=val string.
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
var obj = {};
if (qs === undefined) { return {} }
String(qs).split(sep || "&").map(function (keyValue) {
var res = obj,
next,
kv = keyValue.split(eq || "="),
key = QueryString.unescape(kv.shift(), true),
value = QueryString.unescape(kv.join(eq || "="), true);
key.replace(chunks, function (all, name, nameInBrackets, nameIn2Quotes, nameIn1Quotes, isArray, offset) {
var end = offset + all.length == key.length;
name = name || nameInBrackets || nameIn2Quotes || nameIn1Quotes;
next = end ? value : {};
if (Array.isArray(res[name])) {
res[name].push(next);
res = next;
} else {
if (name in res) {
if (isArray || end) {
res = (res[name] = [res[name], next])[1];
} else {
res = res[name];
}
} else {
if (isArray) {
res = (res[name] = [next])[0];
} else {
res = res[name] = next;
}
}
}
});
});
return obj;
};