项目原始demo,不改动
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. 'use strict';
  2. var utils = require('./utils');
  3. var has = Object.prototype.hasOwnProperty;
  4. var defaults = {
  5. allowDots: false,
  6. allowPrototypes: false,
  7. arrayLimit: 20,
  8. charset: 'utf-8',
  9. charsetSentinel: false,
  10. decoder: utils.decode,
  11. delimiter: '&',
  12. depth: 5,
  13. ignoreQueryPrefix: false,
  14. interpretNumericEntities: false,
  15. parameterLimit: 1000,
  16. parseArrays: true,
  17. plainObjects: false,
  18. strictNullHandling: false
  19. };
  20. var interpretNumericEntities = function (str) {
  21. return str.replace(/&#(\d+);/g, function ($0, numberStr) {
  22. return String.fromCharCode(parseInt(numberStr, 10));
  23. });
  24. };
  25. // This is what browsers will submit when the ✓ character occurs in an
  26. // application/x-www-form-urlencoded body and the encoding of the page containing
  27. // the form is iso-8859-1, or when the submitted form has an accept-charset
  28. // attribute of iso-8859-1. Presumably also with other charsets that do not contain
  29. // the ✓ character, such as us-ascii.
  30. var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓')
  31. // These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.
  32. var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
  33. var parseValues = function parseQueryStringValues(str, options) {
  34. var obj = {};
  35. var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
  36. var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
  37. var parts = cleanStr.split(options.delimiter, limit);
  38. var skipIndex = -1; // Keep track of where the utf8 sentinel was found
  39. var i;
  40. var charset = options.charset;
  41. if (options.charsetSentinel) {
  42. for (i = 0; i < parts.length; ++i) {
  43. if (parts[i].indexOf('utf8=') === 0) {
  44. if (parts[i] === charsetSentinel) {
  45. charset = 'utf-8';
  46. } else if (parts[i] === isoSentinel) {
  47. charset = 'iso-8859-1';
  48. }
  49. skipIndex = i;
  50. i = parts.length; // The eslint settings do not allow break;
  51. }
  52. }
  53. }
  54. for (i = 0; i < parts.length; ++i) {
  55. if (i === skipIndex) {
  56. continue;
  57. }
  58. var part = parts[i];
  59. var bracketEqualsPos = part.indexOf(']=');
  60. var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;
  61. var key, val;
  62. if (pos === -1) {
  63. key = options.decoder(part, defaults.decoder, charset);
  64. val = options.strictNullHandling ? null : '';
  65. } else {
  66. key = options.decoder(part.slice(0, pos), defaults.decoder, charset);
  67. val = options.decoder(part.slice(pos + 1), defaults.decoder, charset);
  68. }
  69. if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
  70. val = interpretNumericEntities(val);
  71. }
  72. if (has.call(obj, key)) {
  73. obj[key] = utils.combine(obj[key], val);
  74. } else {
  75. obj[key] = val;
  76. }
  77. }
  78. return obj;
  79. };
  80. var parseObject = function (chain, val, options) {
  81. var leaf = val;
  82. for (var i = chain.length - 1; i >= 0; --i) {
  83. var obj;
  84. var root = chain[i];
  85. if (root === '[]' && options.parseArrays) {
  86. obj = [].concat(leaf);
  87. } else {
  88. obj = options.plainObjects ? Object.create(null) : {};
  89. var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
  90. var index = parseInt(cleanRoot, 10);
  91. if (!options.parseArrays && cleanRoot === '') {
  92. obj = { 0: leaf };
  93. } else if (
  94. !isNaN(index)
  95. && root !== cleanRoot
  96. && String(index) === cleanRoot
  97. && index >= 0
  98. && (options.parseArrays && index <= options.arrayLimit)
  99. ) {
  100. obj = [];
  101. obj[index] = leaf;
  102. } else {
  103. obj[cleanRoot] = leaf;
  104. }
  105. }
  106. leaf = obj;
  107. }
  108. return leaf;
  109. };
  110. var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
  111. if (!givenKey) {
  112. return;
  113. }
  114. // Transform dot notation to bracket notation
  115. var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
  116. // The regex chunks
  117. var brackets = /(\[[^[\]]*])/;
  118. var child = /(\[[^[\]]*])/g;
  119. // Get the parent
  120. var segment = brackets.exec(key);
  121. var parent = segment ? key.slice(0, segment.index) : key;
  122. // Stash the parent if it exists
  123. var keys = [];
  124. if (parent) {
  125. // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties
  126. if (!options.plainObjects && has.call(Object.prototype, parent)) {
  127. if (!options.allowPrototypes) {
  128. return;
  129. }
  130. }
  131. keys.push(parent);
  132. }
  133. // Loop through children appending to the array until we hit depth
  134. var i = 0;
  135. while ((segment = child.exec(key)) !== null && i < options.depth) {
  136. i += 1;
  137. if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {
  138. if (!options.allowPrototypes) {
  139. return;
  140. }
  141. }
  142. keys.push(segment[1]);
  143. }
  144. // If there's a remainder, just add whatever is left
  145. if (segment) {
  146. keys.push('[' + key.slice(segment.index) + ']');
  147. }
  148. return parseObject(keys, val, options);
  149. };
  150. module.exports = function (str, opts) {
  151. var options = opts ? utils.assign({}, opts) : {};
  152. if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
  153. throw new TypeError('Decoder has to be a function.');
  154. }
  155. options.ignoreQueryPrefix = options.ignoreQueryPrefix === true;
  156. options.delimiter = typeof options.delimiter === 'string' || utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
  157. options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
  158. options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
  159. options.parseArrays = options.parseArrays !== false;
  160. options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
  161. options.allowDots = typeof options.allowDots === 'undefined' ? defaults.allowDots : !!options.allowDots;
  162. options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
  163. options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
  164. options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
  165. options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
  166. if (typeof options.charset !== 'undefined' && options.charset !== 'utf-8' && options.charset !== 'iso-8859-1') {
  167. throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined');
  168. }
  169. if (typeof options.charset === 'undefined') {
  170. options.charset = defaults.charset;
  171. }
  172. if (str === '' || str === null || typeof str === 'undefined') {
  173. return options.plainObjects ? Object.create(null) : {};
  174. }
  175. var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
  176. var obj = options.plainObjects ? Object.create(null) : {};
  177. // Iterate over the keys and setup the new object
  178. var keys = Object.keys(tempObj);
  179. for (var i = 0; i < keys.length; ++i) {
  180. var key = keys[i];
  181. var newObj = parseKeys(key, tempObj[key], options);
  182. obj = utils.merge(obj, newObj, options);
  183. }
  184. return utils.compact(obj);
  185. };