|
@@ -0,0 +1,260 @@
|
|
|
|
+(function(exports){
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Library version.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+exports.version = '0.4.2';
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Object#toString() ref for stringify().
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+var toString = Object.prototype.toString;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 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]) t[i] = parent[key][i];
|
|
|
|
+ parent[key] = t;
|
|
|
|
+ return t;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function parse(parts, parent, key, val) {
|
|
|
|
+ var part = parts.shift();
|
|
|
|
+ // end
|
|
|
|
+ if (!part) {
|
|
|
|
+ if (Array.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 (Array.isArray(obj)) {
|
|
|
|
+ if ('' != val) obj.push(val);
|
|
|
|
+ } else if ('object' == typeof obj) {
|
|
|
|
+ obj[Object.keys(obj).length] = val;
|
|
|
|
+ } else {
|
|
|
|
+ obj = parent[key] = [parent[key], val];
|
|
|
|
+ }
|
|
|
|
+ // prop
|
|
|
|
+ } else if (~part.indexOf(']')) {
|
|
|
|
+ part = part.substr(0, part.length - 1);
|
|
|
|
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
|
|
|
|
+ parse(parts, obj, part, val);
|
|
|
|
+ // key
|
|
|
|
+ } else {
|
|
|
|
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
|
|
|
|
+ parse(parts, obj, part, val);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Merge parent key/val pair.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+function merge(parent, key, val){
|
|
|
|
+ if (~key.indexOf(']')) {
|
|
|
|
+ var parts = key.split('[')
|
|
|
|
+ , len = parts.length
|
|
|
|
+ , last = len - 1;
|
|
|
|
+ parse(parts, parent, 'base', val);
|
|
|
|
+ // optimize
|
|
|
|
+ } else {
|
|
|
|
+ if (!isint.test(key) && Array.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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Parse the given obj.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+function parseObject(obj){
|
|
|
|
+ var ret = { base: {} };
|
|
|
|
+ Object.keys(obj).forEach(function(name){
|
|
|
|
+ merge(ret, name, obj[name]);
|
|
|
|
+ });
|
|
|
|
+ return ret.base;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Parse the given str.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+function parseString(str){
|
|
|
|
+ return String(str)
|
|
|
|
+ .split('&')
|
|
|
|
+ .reduce(function(ret, pair){
|
|
|
|
+ try{
|
|
|
|
+ pair = decodeURIComponent(pair.replace(/\+/g, ' '));
|
|
|
|
+ } catch(e) {
|
|
|
|
+ // ignore
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var eql = pair.indexOf('=')
|
|
|
|
+ , brace = lastBraceInKey(pair)
|
|
|
|
+ , key = pair.substr(0, brace || eql)
|
|
|
|
+ , val = pair.substr(brace || eql, pair.length)
|
|
|
|
+ , val = val.substr(val.indexOf('=') + 1, val.length);
|
|
|
|
+
|
|
|
|
+ // ?foo
|
|
|
|
+ if ('' == key) key = pair, val = '';
|
|
|
|
+
|
|
|
|
+ return merge(ret, key, val);
|
|
|
|
+ }, { base: {} }).base;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 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 (Array.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 + '=' + 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 = Object.keys(obj)
|
|
|
|
+ , key;
|
|
|
|
+
|
|
|
|
+ for (var i = 0, len = keys.length; i < len; ++i) {
|
|
|
|
+ key = keys[i];
|
|
|
|
+ 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 (undefined === v) {
|
|
|
|
+ obj[key] = val;
|
|
|
|
+ } else if (Array.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;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+})('undefined' == typeof exports ? qs = {} : exports);
|