source: frontend/node_modules/qs/lib/parse.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 15.2 KB
Line 
1'use strict';
2
3var utils = require('./utils');
4
5var has = Object.prototype.hasOwnProperty;
6var isArray = Array.isArray;
7
8var defaults = {
9 allowDots: false,
10 allowEmptyArrays: false,
11 allowPrototypes: false,
12 allowSparse: false,
13 arrayLimit: 20,
14 charset: 'utf-8',
15 charsetSentinel: false,
16 comma: false,
17 decodeDotInKeys: false,
18 decoder: utils.decode,
19 delimiter: '&',
20 depth: 5,
21 duplicates: 'combine',
22 ignoreQueryPrefix: false,
23 interpretNumericEntities: false,
24 parameterLimit: 1000,
25 parseArrays: true,
26 plainObjects: false,
27 strictDepth: false,
28 strictMerge: true,
29 strictNullHandling: false,
30 throwOnLimitExceeded: false
31};
32
33var interpretNumericEntities = function (str) {
34 return str.replace(/&#(\d+);/g, function ($0, numberStr) {
35 return String.fromCharCode(parseInt(numberStr, 10));
36 });
37};
38
39var parseArrayValue = function (val, options, currentArrayLength) {
40 if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
41 return val.split(',');
42 }
43
44 if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
45 throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
46 }
47
48 return val;
49};
50
51// This is what browsers will submit when the ✓ character occurs in an
52// application/x-www-form-urlencoded body and the encoding of the page containing
53// the form is iso-8859-1, or when the submitted form has an accept-charset
54// attribute of iso-8859-1. Presumably also with other charsets that do not contain
55// the ✓ character, such as us-ascii.
56var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('&#10003;')
57
58// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.
59var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
60
61var parseValues = function parseQueryStringValues(str, options) {
62 var obj = { __proto__: null };
63
64 var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
65 cleanStr = cleanStr.replace(/%5B/gi, '[').replace(/%5D/gi, ']');
66
67 var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
68 var parts = cleanStr.split(
69 options.delimiter,
70 options.throwOnLimitExceeded && typeof limit !== 'undefined' ? limit + 1 : limit
71 );
72
73 if (options.throwOnLimitExceeded && typeof limit !== 'undefined' && parts.length > limit) {
74 throw new RangeError('Parameter limit exceeded. Only ' + limit + ' parameter' + (limit === 1 ? '' : 's') + ' allowed.');
75 }
76
77 var skipIndex = -1; // Keep track of where the utf8 sentinel was found
78 var i;
79
80 var charset = options.charset;
81 if (options.charsetSentinel) {
82 for (i = 0; i < parts.length; ++i) {
83 if (parts[i].indexOf('utf8=') === 0) {
84 if (parts[i] === charsetSentinel) {
85 charset = 'utf-8';
86 } else if (parts[i] === isoSentinel) {
87 charset = 'iso-8859-1';
88 }
89 skipIndex = i;
90 i = parts.length; // The eslint settings do not allow break;
91 }
92 }
93 }
94
95 for (i = 0; i < parts.length; ++i) {
96 if (i === skipIndex) {
97 continue;
98 }
99 var part = parts[i];
100
101 var bracketEqualsPos = part.indexOf(']=');
102 var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;
103
104 var key;
105 var val;
106 if (pos === -1) {
107 key = options.decoder(part, defaults.decoder, charset, 'key');
108 val = options.strictNullHandling ? null : '';
109 } else {
110 key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
111
112 if (key !== null) {
113 val = utils.maybeMap(
114 parseArrayValue(
115 part.slice(pos + 1),
116 options,
117 isArray(obj[key]) ? obj[key].length : 0
118 ),
119 function (encodedVal) {
120 return options.decoder(encodedVal, defaults.decoder, charset, 'value');
121 }
122 );
123 }
124 }
125
126 if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
127 val = interpretNumericEntities(String(val));
128 }
129
130 if (part.indexOf('[]=') > -1) {
131 val = isArray(val) ? [val] : val;
132 }
133
134 if (options.comma && isArray(val) && val.length > options.arrayLimit) {
135 if (options.throwOnLimitExceeded) {
136 throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
137 }
138 val = utils.combine([], val, options.arrayLimit, options.plainObjects);
139 }
140
141 if (key !== null) {
142 var existing = has.call(obj, key);
143 if (existing && (options.duplicates === 'combine' || part.indexOf('[]=') > -1)) {
144 obj[key] = utils.combine(
145 obj[key],
146 val,
147 options.arrayLimit,
148 options.plainObjects
149 );
150 } else if (!existing || options.duplicates === 'last') {
151 obj[key] = val;
152 }
153 }
154 }
155
156 return obj;
157};
158
159var parseObject = function (chain, val, options, valuesParsed) {
160 var currentArrayLength = 0;
161 if (chain.length > 0 && chain[chain.length - 1] === '[]') {
162 var parentKey = chain.slice(0, -1).join('');
163 currentArrayLength = Array.isArray(val) && val[parentKey] ? val[parentKey].length : 0;
164 }
165
166 var leaf = valuesParsed ? val : parseArrayValue(val, options, currentArrayLength);
167
168 for (var i = chain.length - 1; i >= 0; --i) {
169 var obj;
170 var root = chain[i];
171
172 if (root === '[]' && options.parseArrays) {
173 if (utils.isOverflow(leaf)) {
174 // leaf is already an overflow object, preserve it
175 obj = leaf;
176 } else {
177 obj = options.allowEmptyArrays && (leaf === '' || (options.strictNullHandling && leaf === null))
178 ? []
179 : utils.combine(
180 [],
181 leaf,
182 options.arrayLimit,
183 options.plainObjects
184 );
185 }
186 } else {
187 obj = options.plainObjects ? { __proto__: null } : {};
188 var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
189 var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/g, '.') : cleanRoot;
190 var index = parseInt(decodedRoot, 10);
191 var isValidArrayIndex = !isNaN(index)
192 && root !== decodedRoot
193 && String(index) === decodedRoot
194 && index >= 0
195 && options.parseArrays;
196 if (!options.parseArrays && decodedRoot === '') {
197 obj = { 0: leaf };
198 } else if (isValidArrayIndex && index < options.arrayLimit) {
199 obj = [];
200 obj[index] = leaf;
201 } else if (isValidArrayIndex && options.throwOnLimitExceeded) {
202 throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
203 } else if (isValidArrayIndex) {
204 obj[index] = leaf;
205 utils.markOverflow(obj, index);
206 } else if (decodedRoot !== '__proto__') {
207 obj[decodedRoot] = leaf;
208 }
209 }
210
211 leaf = obj;
212 }
213
214 return leaf;
215};
216
217// Split a key like "a[b][c[]]" into ['a', '[b]', '[c[]]'] while preserving
218// qs parse semantics for depth/prototype guards.
219var splitKeyIntoSegments = function splitKeyIntoSegments(originalKey, options) {
220 var key = options.allowDots ? originalKey.replace(/\.([^.[]+)/g, '[$1]') : originalKey;
221
222 // depth <= 0 keeps the whole key as one segment
223 if (options.depth <= 0) {
224 if (!options.plainObjects && has.call(Object.prototype, key)) {
225 if (!options.allowPrototypes) {
226 return;
227 }
228 }
229
230 return [key];
231 }
232
233 var segments = [];
234
235 // parent before the first '[' (may be empty if key starts with '[')
236 var first = key.indexOf('[');
237 var parent = first >= 0 ? key.slice(0, first) : key;
238 if (parent) {
239 if (!options.plainObjects && has.call(Object.prototype, parent)) {
240 if (!options.allowPrototypes) {
241 return;
242 }
243 }
244
245 segments[segments.length] = parent;
246 }
247
248 var n = key.length;
249 var open = first;
250 var collected = 0;
251
252 while (open >= 0 && collected < options.depth) {
253 var level = 1;
254 var i = open + 1;
255 var close = -1;
256
257 // balance nested '[' and ']' inside this bracket group using a nesting level counter
258 while (i < n && close < 0) {
259 var cu = key.charCodeAt(i);
260 if (cu === 0x5B) { // '['
261 level += 1;
262 } else if (cu === 0x5D) { // ']'
263 level -= 1;
264 if (level === 0) {
265 close = i; // found matching close; loop will exit by condition
266 }
267 }
268 i += 1;
269 }
270
271 if (close < 0) {
272 // Unterminated group: wrap the raw remainder in one bracket pair so it stays
273 // a single literal segment (e.g. "[[]b" -> "[[]b]"); we do not infer missing ']'.
274 segments[segments.length] = '[' + key.slice(open) + ']';
275 return segments;
276 }
277
278 var seg = key.slice(open, close + 1);
279 // prototype guard for the content of this group
280 var content = seg.slice(1, -1);
281 if (!options.plainObjects && has.call(Object.prototype, content) && !options.allowPrototypes) {
282 return;
283 }
284
285 segments[segments.length] = seg;
286 collected += 1;
287
288 // find the next '[' after this balanced group
289 open = key.indexOf('[', close + 1);
290 }
291
292 if (open >= 0) {
293 if (options.strictDepth === true) {
294 throw new RangeError('Input depth exceeded depth option of ' + options.depth + ' and strictDepth is true');
295 }
296
297 segments[segments.length] = '[' + key.slice(open) + ']';
298 }
299
300 return segments;
301};
302
303var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
304 if (!givenKey) {
305 return;
306 }
307
308 var keys = splitKeyIntoSegments(givenKey, options);
309
310 if (!keys) {
311 return;
312 }
313
314 return parseObject(keys, val, options, valuesParsed);
315};
316
317var normalizeParseOptions = function normalizeParseOptions(opts) {
318 if (!opts) {
319 return defaults;
320 }
321
322 if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
323 throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
324 }
325
326 if (typeof opts.decodeDotInKeys !== 'undefined' && typeof opts.decodeDotInKeys !== 'boolean') {
327 throw new TypeError('`decodeDotInKeys` option can only be `true` or `false`, when provided');
328 }
329
330 if (opts.decoder !== null && typeof opts.decoder !== 'undefined' && typeof opts.decoder !== 'function') {
331 throw new TypeError('Decoder has to be a function.');
332 }
333
334 if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
335 throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
336 }
337
338 if (typeof opts.throwOnLimitExceeded !== 'undefined' && typeof opts.throwOnLimitExceeded !== 'boolean') {
339 throw new TypeError('`throwOnLimitExceeded` option must be a boolean');
340 }
341
342 var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;
343
344 var duplicates = typeof opts.duplicates === 'undefined' ? defaults.duplicates : opts.duplicates;
345
346 if (duplicates !== 'combine' && duplicates !== 'first' && duplicates !== 'last') {
347 throw new TypeError('The duplicates option must be either combine, first, or last');
348 }
349
350 var allowDots = typeof opts.allowDots === 'undefined' ? opts.decodeDotInKeys === true ? true : defaults.allowDots : !!opts.allowDots;
351
352 return {
353 allowDots: allowDots,
354 allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
355 allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
356 allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
357 arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
358 charset: charset,
359 charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
360 comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
361 decodeDotInKeys: typeof opts.decodeDotInKeys === 'boolean' ? opts.decodeDotInKeys : defaults.decodeDotInKeys,
362 decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
363 delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
364 // eslint-disable-next-line no-implicit-coercion, no-extra-parens
365 depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
366 duplicates: duplicates,
367 ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
368 interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
369 parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
370 parseArrays: opts.parseArrays !== false,
371 plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,
372 strictDepth: typeof opts.strictDepth === 'boolean' ? !!opts.strictDepth : defaults.strictDepth,
373 strictMerge: typeof opts.strictMerge === 'boolean' ? !!opts.strictMerge : defaults.strictMerge,
374 strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling,
375 throwOnLimitExceeded: typeof opts.throwOnLimitExceeded === 'boolean' ? opts.throwOnLimitExceeded : false
376 };
377};
378
379module.exports = function (str, opts) {
380 var options = normalizeParseOptions(opts);
381
382 if (str === '' || str === null || typeof str === 'undefined') {
383 return options.plainObjects ? { __proto__: null } : {};
384 }
385
386 var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
387 var obj = options.plainObjects ? { __proto__: null } : {};
388
389 // Iterate over the keys and setup the new object
390
391 var keys = Object.keys(tempObj);
392 for (var i = 0; i < keys.length; ++i) {
393 var key = keys[i];
394 var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
395 obj = utils.merge(obj, newObj, options);
396 }
397
398 if (options.allowSparse === true) {
399 return obj;
400 }
401
402 return utils.compact(obj);
403};
Note: See TracBrowser for help on using the repository browser.