The LM Control website. Simple yet efficient.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

parse.js 8.9KB

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