querystring.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. module.metadata = {
  6. "stability": "unstable"
  7. };
  8. let unescape = decodeURIComponent;
  9. exports.unescape = unescape;
  10. // encodes a string safely for application/x-www-form-urlencoded
  11. // adheres to RFC 3986
  12. // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent
  13. function escape(query) {
  14. return encodeURIComponent(query).
  15. replace(/%20/g, '+').
  16. replace(/!/g, '%21').
  17. replace(/'/g, '%27').
  18. replace(/\(/g, '%28').
  19. replace(/\)/g, '%29').
  20. replace(/\*/g, '%2A');
  21. }
  22. exports.escape = escape;
  23. // Converts an object of unordered key-vals to a string that can be passed
  24. // as part of a request
  25. function stringify(options, separator, assigner) {
  26. separator = separator || '&';
  27. assigner = assigner || '=';
  28. // Explicitly return null if we have null, and empty string, or empty object.
  29. if (!options)
  30. return '';
  31. // If content is already a string, just return it as is.
  32. if (typeof(options) == 'string')
  33. return options;
  34. // At this point we have a k:v object. Iterate over it and encode each value.
  35. // Arrays and nested objects will get encoded as needed. For example...
  36. //
  37. // { foo: [1, 2, { omg: 'bbq', 'all your base!': 'are belong to us' }], bar: 'baz' }
  38. //
  39. // will be encoded as
  40. //
  41. // foo[0]=1&foo[1]=2&foo[2][omg]=bbq&foo[2][all+your+base!]=are+belong+to+us&bar=baz
  42. //
  43. // Keys (including '[' and ']') and values will be encoded with
  44. // `escape` before returning.
  45. //
  46. // Execution was inspired by jQuery, but some details have changed and numeric
  47. // array keys are included (whereas they are not in jQuery).
  48. let encodedContent = [];
  49. function add(key, val) {
  50. encodedContent.push(escape(key) + assigner + escape(val));
  51. }
  52. function make(key, value) {
  53. if (value && typeof(value) === 'object')
  54. Object.keys(value).forEach(function(name) {
  55. make(key + '[' + name + ']', value[name]);
  56. });
  57. else
  58. add(key, value);
  59. }
  60. Object.keys(options).forEach(function(name) { make(name, options[name]); });
  61. return encodedContent.join(separator);
  62. //XXXzpao In theory, we can just use a FormData object on 1.9.3, but I had
  63. // trouble getting that working. It would also be nice to stay
  64. // backwards-compat as long as possible. Keeping this in for now...
  65. // let formData = Cc['@mozilla.org/files/formdata;1'].
  66. // createInstance(Ci.nsIDOMFormData);
  67. // for ([k, v] in Iterator(content)) {
  68. // formData.append(k, v);
  69. // }
  70. // return formData;
  71. }
  72. exports.stringify = stringify;
  73. // Exporting aliases that nodejs implements just for the sake of
  74. // interoperability.
  75. exports.encode = stringify;
  76. exports.serialize = stringify;
  77. // Note: That `stringify` and `parse` aren't bijective as we use `stringify`
  78. // as it was implement in request module, but implement `parse` to match nodejs
  79. // behavior.
  80. // TODO: Make `stringify` implement API as in nodejs and figure out backwards
  81. // compatibility.
  82. function parse(query, separator, assigner) {
  83. separator = separator || '&';
  84. assigner = assigner || '=';
  85. let result = {};
  86. if (typeof query !== 'string' || query.length === 0)
  87. return result;
  88. query.split(separator).forEach(function(chunk) {
  89. let pair = chunk.split(assigner);
  90. let key = unescape(pair[0]);
  91. let value = unescape(pair.slice(1).join(assigner));
  92. if (!(key in result))
  93. result[key] = value;
  94. else if (Array.isArray(result[key]))
  95. result[key].push(value);
  96. else
  97. result[key] = [result[key], value];
  98. });
  99. return result;
  100. };
  101. exports.parse = parse;
  102. // Exporting aliases that nodejs implements just for the sake of
  103. // interoperability.
  104. exports.decode = parse;