| // Copyright Joyent, Inc. and other Node contributors. | 
 | // | 
 | // Permission is hereby granted, free of charge, to any person obtaining a | 
 | // copy of this software and associated documentation files (the | 
 | // "Software"), to deal in the Software without restriction, including | 
 | // without limitation the rights to use, copy, modify, merge, publish, | 
 | // distribute, sublicense, and/or sell copies of the Software, and to permit | 
 | // persons to whom the Software is furnished to do so, subject to the | 
 | // following conditions: | 
 | // | 
 | // The above copyright notice and this permission notice shall be included | 
 | // in all copies or substantial portions of the Software. | 
 | // | 
 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 
 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | 
 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | 
 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | 
 | // USE OR OTHER DEALINGS IN THE SOFTWARE. | 
 |  | 
 | // Query String Utilities | 
 |  | 
 | define("third_party/js/querystring", [ | 
 |   "third_party/js/util", | 
 | ], function(util) { | 
 |  | 
 | var QueryString = {}; | 
 |  | 
 | // If obj.hasOwnProperty has been overridden, then calling | 
 | // obj.hasOwnProperty(prop) will break. | 
 | // See: https://github.com/joyent/node/issues/1707 | 
 | function hasOwnProperty(obj, prop) { | 
 |   return Object.prototype.hasOwnProperty.call(obj, prop); | 
 | } | 
 |  | 
 |  | 
 | function charCode(c) { | 
 |   return c.charCodeAt(0); | 
 | } | 
 |  | 
 |  | 
 | // a safe fast alternative to decodeURIComponent | 
 | QueryString.unescapeBuffer = function(s, decodeSpaces) { | 
 |   var out = new Buffer(s.length); | 
 |   var state = 'CHAR'; // states: CHAR, HEX0, HEX1 | 
 |   var n, m, hexchar; | 
 |  | 
 |   for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) { | 
 |     var c = s.charCodeAt(inIndex); | 
 |     switch (state) { | 
 |       case 'CHAR': | 
 |         switch (c) { | 
 |           case charCode('%'): | 
 |             n = 0; | 
 |             m = 0; | 
 |             state = 'HEX0'; | 
 |             break; | 
 |           case charCode('+'): | 
 |             if (decodeSpaces) c = charCode(' '); | 
 |             // pass thru | 
 |           default: | 
 |             out[outIndex++] = c; | 
 |             break; | 
 |         } | 
 |         break; | 
 |  | 
 |       case 'HEX0': | 
 |         state = 'HEX1'; | 
 |         hexchar = c; | 
 |         if (charCode('0') <= c && c <= charCode('9')) { | 
 |           n = c - charCode('0'); | 
 |         } else if (charCode('a') <= c && c <= charCode('f')) { | 
 |           n = c - charCode('a') + 10; | 
 |         } else if (charCode('A') <= c && c <= charCode('F')) { | 
 |           n = c - charCode('A') + 10; | 
 |         } else { | 
 |           out[outIndex++] = charCode('%'); | 
 |           out[outIndex++] = c; | 
 |           state = 'CHAR'; | 
 |           break; | 
 |         } | 
 |         break; | 
 |  | 
 |       case 'HEX1': | 
 |         state = 'CHAR'; | 
 |         if (charCode('0') <= c && c <= charCode('9')) { | 
 |           m = c - charCode('0'); | 
 |         } else if (charCode('a') <= c && c <= charCode('f')) { | 
 |           m = c - charCode('a') + 10; | 
 |         } else if (charCode('A') <= c && c <= charCode('F')) { | 
 |           m = c - charCode('A') + 10; | 
 |         } else { | 
 |           out[outIndex++] = charCode('%'); | 
 |           out[outIndex++] = hexchar; | 
 |           out[outIndex++] = c; | 
 |           break; | 
 |         } | 
 |         out[outIndex++] = 16 * n + m; | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   // TODO support returning arbitrary buffers. | 
 |  | 
 |   return out.slice(0, outIndex - 1); | 
 | }; | 
 |  | 
 |  | 
 | QueryString.unescape = function(s, decodeSpaces) { | 
 |   try { | 
 |     return decodeURIComponent(s); | 
 |   } catch (e) { | 
 |     return QueryString.unescapeBuffer(s, decodeSpaces).toString(); | 
 |   } | 
 | }; | 
 |  | 
 |  | 
 | QueryString.escape = function(str) { | 
 |   return encodeURIComponent(str); | 
 | }; | 
 |  | 
 | var stringifyPrimitive = function(v) { | 
 |   if (util.isString(v)) | 
 |     return v; | 
 |   if (util.isBoolean(v)) | 
 |     return v ? 'true' : 'false'; | 
 |   if (util.isNumber(v)) | 
 |     return isFinite(v) ? v : ''; | 
 |   return ''; | 
 | }; | 
 |  | 
 |  | 
 | QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) { | 
 |   sep = sep || '&'; | 
 |   eq = eq || '='; | 
 |  | 
 |   var encode = QueryString.escape; | 
 |   if (options && typeof options.encodeURIComponent === 'function') { | 
 |     encode = options.encodeURIComponent; | 
 |   } | 
 |  | 
 |   if (util.isObject(obj)) { | 
 |     var keys = Object.keys(obj); | 
 |     var fields = []; | 
 |  | 
 |     for (var i = 0; i < keys.length; i++) { | 
 |       var k = keys[i]; | 
 |       var v = obj[k]; | 
 |       var ks = encode(stringifyPrimitive(k)) + eq; | 
 |  | 
 |       if (util.isArray(v)) { | 
 |         for (var j = 0; j < v.length; j++) | 
 |           fields.push(ks + encode(stringifyPrimitive(v[j]))); | 
 |       } else { | 
 |         fields.push(ks + encode(stringifyPrimitive(v))); | 
 |       } | 
 |     } | 
 |     return fields.join(sep); | 
 |   } | 
 |   return ''; | 
 | }; | 
 |  | 
 | // Parse a key=val string. | 
 | QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | 
 |   sep = sep || '&'; | 
 |   eq = eq || '='; | 
 |   var obj = {}; | 
 |  | 
 |   if (!util.isString(qs) || qs.length === 0) { | 
 |     return obj; | 
 |   } | 
 |  | 
 |   var regexp = /\+/g; | 
 |   qs = qs.split(sep); | 
 |  | 
 |   var maxKeys = 1000; | 
 |   if (options && util.isNumber(options.maxKeys)) { | 
 |     maxKeys = options.maxKeys; | 
 |   } | 
 |  | 
 |   var len = qs.length; | 
 |   // maxKeys <= 0 means that we should not limit keys count | 
 |   if (maxKeys > 0 && len > maxKeys) { | 
 |     len = maxKeys; | 
 |   } | 
 |  | 
 |   var decode = QueryString.unescape; | 
 |   if (options && typeof options.decodeURIComponent === 'function') { | 
 |     decode = options.decodeURIComponent; | 
 |   } | 
 |  | 
 |   for (var i = 0; i < len; ++i) { | 
 |     var x = qs[i].replace(regexp, '%20'), | 
 |         idx = x.indexOf(eq), | 
 |         kstr, vstr, k, v; | 
 |  | 
 |     if (idx >= 0) { | 
 |       kstr = x.substr(0, idx); | 
 |       vstr = x.substr(idx + 1); | 
 |     } else { | 
 |       kstr = x; | 
 |       vstr = ''; | 
 |     } | 
 |  | 
 |     try { | 
 |       k = decode(kstr); | 
 |       v = decode(vstr); | 
 |     } catch (e) { | 
 |       k = QueryString.unescape(kstr, true); | 
 |       v = QueryString.unescape(vstr, true); | 
 |     } | 
 |  | 
 |     if (!hasOwnProperty(obj, k)) { | 
 |       obj[k] = v; | 
 |     } else if (util.isArray(obj[k])) { | 
 |       obj[k].push(v); | 
 |     } else { | 
 |       obj[k] = [obj[k], v]; | 
 |     } | 
 |   } | 
 |  | 
 |   return obj; | 
 | }; | 
 |  | 
 | return QueryString; | 
 | }); |