123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- /**
- * Object#toString() ref for stringify().
- */
- var toString = Object.prototype.toString;
- /**
- * Object#hasOwnProperty ref
- */
- var hasOwnProperty = Object.prototype.hasOwnProperty;
- /**
- * Array#indexOf shim.
- */
- var indexOf = typeof Array.prototype.indexOf === 'function'
- ? function(arr, el) { return arr.indexOf(el); }
- : function(arr, el) {
- for (var i = 0; i < arr.length; i++) {
- if (arr[i] === el) return i;
- }
- return -1;
- };
- /**
- * Array.isArray shim.
- */
- var isArray = Array.isArray || function(arr) {
- return toString.call(arr) == '[object Array]';
- };
- /**
- * Object.keys shim.
- */
- var objectKeys = Object.keys || function(obj) {
- var ret = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- ret.push(key);
- }
- }
- return ret;
- };
- /**
- * Array#forEach shim.
- */
- var forEach = typeof Array.prototype.forEach === 'function'
- ? function(arr, fn) { return arr.forEach(fn); }
- : function(arr, fn) {
- for (var i = 0; i < arr.length; i++) fn(arr[i]);
- };
- /**
- * Array#reduce shim.
- */
- var reduce = function(arr, fn, initial) {
- if (typeof arr.reduce === 'function') return arr.reduce(fn, initial);
- var res = initial;
- for (var i = 0; i < arr.length; i++) res = fn(res, arr[i]);
- return res;
- };
- /**
- * Cache non-integer test regexp.
- */
- var isint = /^[0-9]+$/;
- function promote(parent, key) {
- if (parent[key].length == 0) return parent[key] = {}
- var t = {};
- for (var i in parent[key]) {
- if (hasOwnProperty.call(parent[key], i)) {
- t[i] = parent[key][i];
- }
- }
- parent[key] = t;
- return t;
- }
- function parse(parts, parent, key, val) {
- var part = parts.shift();
-
- // illegal
- if (Object.getOwnPropertyDescriptor(Object.prototype, key)) return;
-
- // end
- if (!part) {
- if (isArray(parent[key])) {
- parent[key].push(val);
- } else if ('object' == typeof parent[key]) {
- parent[key] = val;
- } else if ('undefined' == typeof parent[key]) {
- parent[key] = val;
- } else {
- parent[key] = [parent[key], val];
- }
- // array
- } else {
- var obj = parent[key] = parent[key] || [];
- if (']' == part) {
- if (isArray(obj)) {
- if ('' != val) obj.push(val);
- } else if ('object' == typeof obj) {
- obj[objectKeys(obj).length] = val;
- } else {
- obj = parent[key] = [parent[key], val];
- }
- // prop
- } else if (~indexOf(part, ']')) {
- part = part.substr(0, part.length - 1);
- if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
- parse(parts, obj, part, val);
- // key
- } else {
- if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
- parse(parts, obj, part, val);
- }
- }
- }
- /**
- * Merge parent key/val pair.
- */
- function merge(parent, key, val){
- if (~indexOf(key, ']')) {
- var parts = key.split('[')
- , len = parts.length
- , last = len - 1;
- parse(parts, parent, 'base', val);
- // optimize
- } else {
- if (!isint.test(key) && isArray(parent.base)) {
- var t = {};
- for (var k in parent.base) t[k] = parent.base[k];
- parent.base = t;
- }
- set(parent.base, key, val);
- }
- return parent;
- }
- /**
- * Compact sparse arrays.
- */
- function compact(obj) {
- if ('object' != typeof obj) return obj;
- if (isArray(obj)) {
- var ret = [];
- for (var i in obj) {
- if (hasOwnProperty.call(obj, i)) {
- ret.push(obj[i]);
- }
- }
- return ret;
- }
- for (var key in obj) {
- obj[key] = compact(obj[key]);
- }
- return obj;
- }
- /**
- * Parse the given obj.
- */
- function parseObject(obj){
- var ret = { base: {} };
- forEach(objectKeys(obj), function(name){
- merge(ret, name, obj[name]);
- });
- return compact(ret.base);
- }
- /**
- * Parse the given str.
- */
- function parseString(str){
- var ret = reduce(String(str).split('&'), function(ret, pair){
- var eql = indexOf(pair, '=')
- , brace = lastBraceInKey(pair)
- , key = pair.substr(0, brace || eql)
- , val = pair.substr(brace || eql, pair.length)
- , val = val.substr(indexOf(val, '=') + 1, val.length);
- // ?foo
- if ('' == key) key = pair, val = '';
- if ('' == key) return ret;
- return merge(ret, decode(key), decode(val));
- }, { base: {} }).base;
- return compact(ret);
- }
- /**
- * Parse the given query `str` or `obj`, returning an object.
- *
- * @param {String} str | {Object} obj
- * @return {Object}
- * @api public
- */
- exports.parse = function(str){
- if (null == str || '' == str) return {};
- return 'object' == typeof str
- ? parseObject(str)
- : parseString(str);
- };
- /**
- * Turn the given `obj` into a query string
- *
- * @param {Object} obj
- * @return {String}
- * @api public
- */
- var stringify = exports.stringify = function(obj, prefix) {
- if (isArray(obj)) {
- return stringifyArray(obj, prefix);
- } else if ('[object Object]' == toString.call(obj)) {
- return stringifyObject(obj, prefix);
- } else if ('string' == typeof obj) {
- return stringifyString(obj, prefix);
- } else {
- return prefix + '=' + encodeURIComponent(String(obj));
- }
- };
- /**
- * Stringify the given `str`.
- *
- * @param {String} str
- * @param {String} prefix
- * @return {String}
- * @api private
- */
- function stringifyString(str, prefix) {
- if (!prefix) throw new TypeError('stringify expects an object');
- return prefix + '=' + encodeURIComponent(str);
- }
- /**
- * Stringify the given `arr`.
- *
- * @param {Array} arr
- * @param {String} prefix
- * @return {String}
- * @api private
- */
- function stringifyArray(arr, prefix) {
- var ret = [];
- if (!prefix) throw new TypeError('stringify expects an object');
- for (var i = 0; i < arr.length; i++) {
- ret.push(stringify(arr[i], prefix + '[' + i + ']'));
- }
- return ret.join('&');
- }
- /**
- * Stringify the given `obj`.
- *
- * @param {Object} obj
- * @param {String} prefix
- * @return {String}
- * @api private
- */
- function stringifyObject(obj, prefix) {
- var ret = []
- , keys = objectKeys(obj)
- , key;
- for (var i = 0, len = keys.length; i < len; ++i) {
- key = keys[i];
- if ('' == key) continue;
- if (null == obj[key]) {
- ret.push(encodeURIComponent(key) + '=');
- } else {
- ret.push(stringify(obj[key], prefix
- ? prefix + '[' + encodeURIComponent(key) + ']'
- : encodeURIComponent(key)));
- }
- }
- return ret.join('&');
- }
- /**
- * Set `obj`'s `key` to `val` respecting
- * the weird and wonderful syntax of a qs,
- * where "foo=bar&foo=baz" becomes an array.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {String} val
- * @api private
- */
- function set(obj, key, val) {
- var v = obj[key];
- if (Object.getOwnPropertyDescriptor(Object.prototype, key)) return;
- if (undefined === v) {
- obj[key] = val;
- } else if (isArray(v)) {
- v.push(val);
- } else {
- obj[key] = [v, val];
- }
- }
- /**
- * Locate last brace in `str` within the key.
- *
- * @param {String} str
- * @return {Number}
- * @api private
- */
- function lastBraceInKey(str) {
- var len = str.length
- , brace
- , c;
- for (var i = 0; i < len; ++i) {
- c = str[i];
- if (']' == c) brace = false;
- if ('[' == c) brace = true;
- if ('=' == c && !brace) return i;
- }
- }
- /**
- * Decode `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
- function decode(str) {
- try {
- return decodeURIComponent(str.replace(/\+/g, ' '));
- } catch (err) {
- return str;
- }
- }
|