|
|
@ -1,7 +1,7 @@ |
|
|
|
// 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; |
|
|
@ -26,24 +26,24 @@ var stack = []; |
|
|
|
* @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.
|
|
|
|
|
|
|
@ -54,93 +54,61 @@ QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name |
|
|
|
|
|
|
|
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)); |
|
|
|
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]; |
|
|
|
} |
|
|
|
var sliced = slicerPattern.exec(key); |
|
|
|
if (!sliced) { |
|
|
|
var ret = {}; |
|
|
|
if (key) ret[key] = val; |
|
|
|
return ret; |
|
|
|
} else { |
|
|
|
if (isArray) { |
|
|
|
res = (res[name] = [next])[0]; |
|
|
|
} else { |
|
|
|
res = res[name] = next; |
|
|
|
} |
|
|
|
// ["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); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
// 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; |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
return obj; |
|
|
|
}; |
|
|
|
|
|
|
|
function isA (thing, canon) { |
|
|
|
// special case for null and undefined
|
|
|
|