25
25
* @param name {String} (optional) Name of the current key, for handling children recursively.
28
QueryString.stringify = function (obj, sep, eq, munge, name) {
29
munge = typeof(munge) == "undefined" ? true : munge;
28
QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name) {
29
munge = typeof munge == "undefined" || munge;
32
if (isA(obj, null) || isA(obj, undefined) || typeof(obj) === 'function') {
33
return name ? encodeURIComponent(name) + eq : '';
32
var type = Object.prototype.toString.call(obj);
33
if (obj == null || type == "[object Function]" || type == "[object Number]" && !isFinite(obj)) {
34
return name ? QueryString.escape(name) + eq : "";
36
if (isBool(obj)) obj = +obj;
37
if (isNumber(obj) || isString(obj)) {
38
return encodeURIComponent(name) + eq + encodeURIComponent(obj);
42
name = name+(munge ? '[]' : '');
43
for (var i = 0, l = obj.length; i < l; i ++) {
44
s.push( QueryString.stringify(obj[i], sep, eq, munge, name) );
38
case '[object Boolean]':
39
obj = +obj; // fall through
40
case '[object Number]':
41
case '[object String]':
42
return QueryString.escape(name) + eq + QueryString.escape(obj);
43
case '[object Array]':
44
name = name + (munge ? "[]" : "");
45
return obj.map(function (item) {
46
return QueryString.stringify(item, sep, eq, munge, name);
48
49
// now we know it's an object.
58
var begin = name ? name + '[' : '';
59
var end = name ? ']' : '';
60
var keys = Object.keys(obj);
61
for (var i = 0, l = keys.length; i < l; i++) {
63
var n = begin + key + end;
64
s.push(QueryString.stringify(obj[key], sep, eq, munge, n));
58
var begin = name ? name + "[" : "",
59
end = name ? "]" : "",
60
keys = Object.keys(obj),
62
s = Object.keys(obj).map(function (key) {
63
n = begin + key + end;
64
return QueryString.stringify(obj[key], sep, eq, munge, n);
70
if (!s && name) return name + "=";
74
QueryString.parseQuery = QueryString.parse = function (qs, sep, eq) {
77
.map(pieceParser(eq||"="))
75
// matches .xxxxx or [xxxxx] or ['xxxxx'] or ["xxxxx"] with optional [] at the end
76
var chunks = /(?:(?:^|\.)([^\[\(\.]+)(?=\[|\.|$|\()|\[([^"'][^\]]*?)\]|\["([^\]"]*?)"\]|\['([^\]']*?)'\])(\[\])?/g;
81
77
// Parse a key=val string.
82
// These can get pretty hairy
84
// parse(foo[bar][][bla]=baz)
85
// return parse(foo[bar][][bla],"baz")
86
// return parse(foo[bar][], {bla : "baz"})
87
// return parse(foo[bar], [{bla:"baz"}])
88
// return parse(foo, {bar:[{bla:"baz"}]})
89
// return {foo:{bar:[{bla:"baz"}]}}
90
var trimmerPattern = /^\s+|\s+$/g,
91
slicerPattern = /(.*)\[([^\]]*)\]$/;
92
var pieceParser = function (eq) {
93
return function parsePiece (key, val) {
94
if (arguments.length !== 2) {
95
// key=val, called from the map/reduce
98
QueryString.unescape(key.shift(), true),
99
QueryString.unescape(key.join(eq), true)
102
key = key.replace(trimmerPattern, '');
104
val = val.replace(trimmerPattern, '');
105
// convert numerals to numbers
108
if (val === numVal.toString(10)) val = numVal;
78
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
80
if (qs === undefined) { return {} }
81
String(qs).split(sep || "&").map(function (keyValue) {
84
kv = keyValue.split(eq || "="),
85
key = QueryString.unescape(kv.shift(), true),
86
value = QueryString.unescape(kv.join(eq || "="), true);
87
key.replace(chunks, function (all, name, nameInBrackets, nameIn2Quotes, nameIn1Quotes, isArray, offset) {
88
var end = offset + all.length == key.length;
89
name = name || nameInBrackets || nameIn2Quotes || nameIn1Quotes;
90
next = end ? value : {};
91
if (Array.isArray(res[name])) {
97
res = (res[name] = [res[name], next])[1];
103
res = (res[name] = [next])[0];
105
res = res[name] = next;
111
var sliced = slicerPattern.exec(key);
114
if (key) ret[key] = val;
117
// ["foo[][bar][][baz]", "foo[][bar][]", "baz"]
118
var tail = sliced[2], head = sliced[1];
121
if (!tail) return parsePiece(head, [val]);
123
// obj: key[subkey]=val
126
return parsePiece(head, ret);
130
// the reducer function that merges each query piece together into one set of params
131
function mergeParams (params, addition) {
133
// if it's uncontested, then just return the addition.
135
// if the existing value is an array, then concat it.
136
: (isA(params, [])) ? params.concat(addition)
137
// if the existing value is not an array, and either are not objects, arrayify it.
138
: (!isA(params, {}) || !isA(addition, {})) ? [params].concat(addition)
139
// else merge them as objects, which is a little more complex
140
: mergeObjects(params, addition)
144
// Merge two *objects* together. If this is called, we've already ruled
145
// out the simple cases, and need to do a loop.
146
function mergeObjects (params, addition) {
147
var keys = Object.keys(addition);
148
for (var i = 0, l = keys.length; i < l; i++) {
151
params[key] = mergeParams(params[key], addition[key]);
158
function isA (thing, canon) {
160
// truthiness. you can feel it in your gut.
162
// typeof is usually "object"
163
&& typeof(thing) === typeof(canon)
164
// check the constructor
165
&& Object.prototype.toString.call(thing) === Object.prototype.toString.call(canon)
168
function isBool (thing) {
170
typeof(thing) === "boolean"
171
|| isA(thing, new Boolean(thing))
174
function isNumber (thing) {
176
typeof(thing) === "number"
177
|| isA(thing, new Number(thing))
178
) && isFinite(thing);
180
function isString (thing) {
182
typeof(thing) === "string"
183
|| isA(thing, new String(thing))