123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- // Query String Utilities
- // Taken from Jack
- var DEFAULT_SEP = "&";
- var DEFAULT_EQ = "=";
- exports.unescape = function (str, decodeSpaces) {
- return decodeURIComponent(decodeSpaces ? str.replace(/\+/g, " ") : str);
- };
- exports.escape = function (str) {
- return encodeURIComponent(str);
- };
- exports.toQueryString = function (obj, sep, eq, name) {
- sep = sep || DEFAULT_SEP;
- eq = eq || DEFAULT_EQ;
- if (isA(obj, null) || isA(obj, undefined)) {
- return name ? encodeURIComponent(name) + eq : '';
- }
- if (isNumber(obj) || isString(obj)) {
- return encodeURIComponent(name) + eq + encodeURIComponent(obj);
- }
- if (isA(obj, [])) {
- var s = [];
- name = name+'[]';
- for (var i = 0, l = obj.length; i < l; i ++) {
- s.push( exports.toQueryString(obj[i], sep, eq, name) );
- }
- return s.join(sep);
- }
- // now we know it's an object.
- var s = [];
- var begin = name ? name + '[' : '';
- var end = name ? ']' : '';
- for (var i in obj) if (obj.hasOwnProperty(i)) {
- var n = begin + i + end;
- s.push(exports.toQueryString(obj[i], sep, eq, n));
- }
- return s.join(sep);
- };
- exports.parseQuery = function(qs, sep, eq) {
- return qs
- .split(sep||DEFAULT_SEP)
- .map(pieceParser(eq||DEFAULT_EQ))
- .reduce(mergeParams);
- };
- // 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 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(
- exports.unescape(key.shift(), true), exports.unescape(key.join(eq), true)
- );
- }
- key = key.replace(/^\s+|\s+$/g, '');
- if (isString(val)) val = val.replace(/^\s+|\s+$/g, '');
- var sliced = /(.*)\[([^\]]*)\]$/.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);
- };
- };
- // 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, 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 the for-in business.
- function mergeObjects (params, addition) {
- for (var i in addition) if (i && addition.hasOwnProperty(i)) {
- params[i] = mergeParams(params[i], addition[i]);
- }
- return params;
- };
- // duck typing
- function isA (thing, canon) {
- return (
- // truthiness. you can feel it in your gut.
- (!thing === !canon)
- // typeof is usually "object"
- && typeof(thing) === typeof(canon)
- // check the constructor
- && Object.prototype.toString.call(thing) === Object.prototype.toString.call(canon)
- );
- };
- function isNumber (thing) {
- return typeof(thing) === "number" && isFinite(thing);
- };
- function isString (thing) {
- return typeof(thing) === "string";
- };
|