// Query String Utilities
var QueryString = exports;
-var urlDecode = process.binding('http_parser').urlDecode;
+var urlDecode = process.binding("http_parser").urlDecode;
// a safe fast alternative to decodeURIComponent
QueryString.unescape = urlDecode;
* @static
*/
QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name) {
- munge = typeof(munge) == "undefined" || munge;
+ munge = typeof munge == "undefined" || munge;
sep = sep || "&";
eq = eq || "=";
- if (obj == null || typeof(obj) === 'function') {
- return name ? QueryString.escape(name) + eq : '';
+ if (obj == null || typeof obj == "function") {
+ return name ? QueryString.escape(name) + eq : "";
}
- if (isBool(obj)) obj = +obj;
+ if (isBool(obj)) {
+ obj = +obj;
+ }
if (isNumber(obj) || isString(obj)) {
return QueryString.escape(name) + eq + QueryString.escape(obj);
}
if (isA(obj, [])) {
- var s = [];
- name = name+(munge ? '[]' : '');
- for (var i = 0, l = obj.length; i < l; i ++) {
- s.push( QueryString.stringify(obj[i], sep, eq, munge, name) );
- }
- return s.join(sep);
+ 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.
stack.push(obj);
- var s = [];
- var begin = name ? name + '[' : '';
- var end = name ? ']' : '';
- var keys = Object.keys(obj);
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i];
- var n = begin + key + end;
- s.push(QueryString.stringify(obj[key], sep, eq, munge, n));
- }
+ 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);
stack.pop();
- s = s.join(sep);
- if (!s && name) return name + "=";
+ if (!s && name) {
+ return name + "=";
+ }
return s;
};
-QueryString.parse = QueryString.decode = function (qs, sep, eq) {
- return (qs || '')
- .split(sep||"&")
- .map(pieceParser(eq||"="))
- .reduce(mergeParams);
-};
-
+// matches .xxxxx or [xxxxx] or ['xxxxx'] or ["xxxxx"] with optional [] at the end
+var chunks = /(?:(?:^|\.)([^\[\(\.]+)(?=\[|\.|$|\()|\[([^"'][^\]]*?)\]|\["([^\]"]*?)"\]|\['([^\]']*?)'\])(\[\])?/g;
// Parse a key=val string.
-// These can get pretty hairy
-// example flow:
-// parse(foo[bar][][bla]=baz)
-// return parse(foo[bar][][bla],"baz")
-// return parse(foo[bar][], {bla : "baz"})
-// return parse(foo[bar], [{bla:"baz"}])
-// return parse(foo, {bar:[{bla:"baz"}]})
-// return {foo:{bar:[{bla:"baz"}]}}
-var slicerPattern = /(.*)\[([^\]]*)\]$/;
-var pieceParser = function (eq) {
- return function parsePiece (key, val) {
- if (arguments.length !== 2) {
- // key=val, called from the map/reduce
- key = key.split(eq);
- return parsePiece(QueryString.unescape(key.shift(), true),
- QueryString.unescape(key.join(eq), true));
- }
- var sliced = slicerPattern.exec(key);
- if (!sliced) {
- var ret = {};
- if (key) ret[key] = val;
- return ret;
- }
- // ["foo[][bar][][baz]", "foo[][bar][]", "baz"]
- var tail = sliced[2], head = sliced[1];
-
- // array: key[]=val
- if (!tail) return parsePiece(head, [val]);
-
- // obj: key[subkey]=val
- var ret = {};
- ret[tail] = val;
- return parsePiece(head, ret);
- };
+QueryString.parse = QueryString.decode = function (qs, sep, eq) {
+ var obj = {};
+ 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 : {};
+ next = next && (+next == next ? +next : next);
+ 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;
};
-// the reducer function that merges each query piece together into one set of params
-function mergeParams (params, addition) {
- return (
- // if it's uncontested, then just return the addition.
- (!params) ? addition
- // if the existing value is an array, then concat it.
- : (isA(params, [])) ? params.concat(addition)
- // if the existing value is not an array, and either are not objects, arrayify it.
- : (!isA(params, {}) || !isA(addition, {})) ? [params].concat(addition)
- // else merge them as objects, which is a little more complex
- : mergeObjects(params, addition)
- );
-}
-
-// Merge two *objects* together. If this is called, we've already ruled
-// out the simple cases, and need to do a loop.
-function mergeObjects (params, addition) {
- var keys = Object.keys(addition);
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i];
- if (key) {
- params[key] = mergeParams(params[key], addition[key]);
- }
- }
- return params;
-}
-
function isA (thing, canon) {
- // special case for null and undefined
- if (thing == null || canon == null) {
- return thing === canon;
- }
- return Object.getPrototypeOf(Object(thing)) == Object.getPrototypeOf(Object(canon));
+ // special case for null and undefined
+ if (thing == null || canon == null) {
+ return thing === canon;
+ }
+ return Object.getPrototypeOf(Object(thing)) == Object.getPrototypeOf(Object(canon));
}
function isBool (thing) {
- return isA(thing, true);
+ return isA(thing, true);
}
function isNumber (thing) {
- return isA(thing, 0) && isFinite(thing);
+ return isA(thing, 0) && isFinite(thing);
}
function isString (thing) {
- return isA(thing, "");
-}
+ return isA(thing, "");
+}
\ No newline at end of file
["foo[bar][bla]=baz&foo[bar][bla]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
["foo[bar][][bla]=baz&foo[bar][][bla]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
["foo[bar][bla][]=baz&foo[bar][bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
+
+ ["foo.bar.bla=baz&foo.bar.bla=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
+ ["foo.bar[].bla=baz&foo[bar][][bla]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
+ ["foo[bar].bla[]=baz&foo.bar[bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
+
+ ["foo['bar']['bla']=baz&foo[\"bar\"][\"bla\"]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
+ ["foo['bar'][]['bla']=baz&foo['bar'][][\"bla\"]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
+ ["foo[bar][\"bla\"][]=baz&foo[\"bar\"][bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
+
[" foo = bar ", "%20foo%20=%20bar%20", {" foo ":" bar "}],
["foo=%zx", "foo=%25zx", {"foo":"%zx"}],
["foo=%EF%BF%BD", "foo=%EF%BF%BD", {"foo" : "\ufffd" }]