qs 4.0.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.npmignore CHANGED
@@ -16,4 +16,3 @@ config.json
16
16
  coverage.*
17
17
  lib-cov
18
18
  complexity.md
19
- dist
package/.travis.yml CHANGED
@@ -2,5 +2,7 @@ language: node_js
2
2
 
3
3
  node_js:
4
4
  - 0.10
5
- - 0.12
6
- - iojs
5
+ - 4.0
6
+ - 4
7
+
8
+ sudo: false
package/CHANGELOG.md CHANGED
@@ -1,88 +1,104 @@
1
+ ## **5.2.1**
2
+ - [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
1
3
 
2
- ## [**3.1.0**](https://github.com/hapijs/qs/issues?milestone=24&state=open)
3
- - [**#89**](https://github.com/hapijs/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
4
-
5
- ## [**3.0.0**](https://github.com/hapijs/qs/issues?milestone=23&state=closed)
6
- - [**#77**](https://github.com/hapijs/qs/issues/77) Perf boost
7
- - [**#60**](https://github.com/hapijs/qs/issues/60) Add explicit option to disable array parsing
8
- - [**#80**](https://github.com/hapijs/qs/issues/80) qs.parse silently drops properties
9
- - [**#74**](https://github.com/hapijs/qs/issues/74) Bad parse when turning array into object
10
- - [**#81**](https://github.com/hapijs/qs/issues/81) Add a `filter` option
11
- - [**#68**](https://github.com/hapijs/qs/issues/68) Fixed issue with recursion and passing strings into objects.
12
- - [**#66**](https://github.com/hapijs/qs/issues/66) Add mixed array and object dot notation support Closes: #47
13
- - [**#76**](https://github.com/hapijs/qs/issues/76) RFC 3986
14
- - [**#85**](https://github.com/hapijs/qs/issues/85) No equal sign
15
- - [**#84**](https://github.com/hapijs/qs/issues/84) update license attribute
16
-
17
- ## [**2.4.1**](https://github.com/hapijs/qs/issues?milestone=20&state=closed)
18
- - [**#73**](https://github.com/hapijs/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
19
-
20
- ## [**2.4.0**](https://github.com/hapijs/qs/issues?milestone=19&state=closed)
21
- - [**#70**](https://github.com/hapijs/qs/issues/70) Add arrayFormat option
22
-
23
- ## [**2.3.3**](https://github.com/hapijs/qs/issues?milestone=18&state=closed)
24
- - [**#59**](https://github.com/hapijs/qs/issues/59) make sure array indexes are >= 0, closes #57
25
- - [**#58**](https://github.com/hapijs/qs/issues/58) make qs usable for browser loader
26
-
27
- ## [**2.3.2**](https://github.com/hapijs/qs/issues?milestone=17&state=closed)
28
- - [**#55**](https://github.com/hapijs/qs/issues/55) allow merging a string into an object
29
-
30
- ## [**2.3.1**](https://github.com/hapijs/qs/issues?milestone=16&state=closed)
31
- - [**#52**](https://github.com/hapijs/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
32
-
33
- ## [**2.3.0**](https://github.com/hapijs/qs/issues?milestone=15&state=closed)
34
- - [**#50**](https://github.com/hapijs/qs/issues/50) add option to omit array indices, closes #46
35
-
36
- ## [**2.2.5**](https://github.com/hapijs/qs/issues?milestone=14&state=closed)
37
- - [**#39**](https://github.com/hapijs/qs/issues/39) Is there an alternative to Buffer.isBuffer?
38
- - [**#49**](https://github.com/hapijs/qs/issues/49) refactor utils.merge, fixes #45
39
- - [**#41**](https://github.com/hapijs/qs/issues/41) avoid browserifying Buffer, for #39
40
-
41
- ## [**2.2.4**](https://github.com/hapijs/qs/issues?milestone=13&state=closed)
42
- - [**#38**](https://github.com/hapijs/qs/issues/38) how to handle object keys beginning with a number
43
-
44
- ## [**2.2.3**](https://github.com/hapijs/qs/issues?milestone=12&state=closed)
45
- - [**#37**](https://github.com/hapijs/qs/issues/37) parser discards first empty value in array
46
- - [**#36**](https://github.com/hapijs/qs/issues/36) Update to lab 4.x
47
-
48
- ## [**2.2.2**](https://github.com/hapijs/qs/issues?milestone=11&state=closed)
49
- - [**#33**](https://github.com/hapijs/qs/issues/33) Error when plain object in a value
50
- - [**#34**](https://github.com/hapijs/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
51
- - [**#24**](https://github.com/hapijs/qs/issues/24) Changelog? Semver?
52
-
53
- ## [**2.2.1**](https://github.com/hapijs/qs/issues?milestone=10&state=closed)
54
- - [**#32**](https://github.com/hapijs/qs/issues/32) account for circular references properly, closes #31
55
- - [**#31**](https://github.com/hapijs/qs/issues/31) qs.parse stackoverflow on circular objects
56
-
57
- ## [**2.2.0**](https://github.com/hapijs/qs/issues?milestone=9&state=closed)
58
- - [**#26**](https://github.com/hapijs/qs/issues/26) Don't use Buffer global if it's not present
59
- - [**#30**](https://github.com/hapijs/qs/issues/30) Bug when merging non-object values into arrays
60
- - [**#29**](https://github.com/hapijs/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
61
- - [**#23**](https://github.com/hapijs/qs/issues/23) Ability to not limit parameters?
62
-
63
- ## [**2.1.0**](https://github.com/hapijs/qs/issues?milestone=8&state=closed)
64
- - [**#22**](https://github.com/hapijs/qs/issues/22) Enable using a RegExp as delimiter
65
-
66
- ## [**2.0.0**](https://github.com/hapijs/qs/issues?milestone=7&state=closed)
67
- - [**#18**](https://github.com/hapijs/qs/issues/18) Why is there arrayLimit?
68
- - [**#20**](https://github.com/hapijs/qs/issues/20) Configurable parametersLimit
69
- - [**#21**](https://github.com/hapijs/qs/issues/21) make all limits optional, for #18, for #20
70
-
71
- ## [**1.2.2**](https://github.com/hapijs/qs/issues?milestone=6&state=closed)
72
- - [**#19**](https://github.com/hapijs/qs/issues/19) Don't overwrite null values
73
-
74
- ## [**1.2.1**](https://github.com/hapijs/qs/issues?milestone=5&state=closed)
75
- - [**#16**](https://github.com/hapijs/qs/issues/16) ignore non-string delimiters
76
- - [**#15**](https://github.com/hapijs/qs/issues/15) Close code block
77
-
78
- ## [**1.2.0**](https://github.com/hapijs/qs/issues?milestone=4&state=closed)
79
- - [**#12**](https://github.com/hapijs/qs/issues/12) Add optional delim argument
80
- - [**#13**](https://github.com/hapijs/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
81
-
82
- ## [**1.1.0**](https://github.com/hapijs/qs/issues?milestone=3&state=closed)
83
- - [**#7**](https://github.com/hapijs/qs/issues/7) Empty values of a POST array disappear after being submitted
84
- - [**#9**](https://github.com/hapijs/qs/issues/9) Should not omit equals signs (=) when value is null
85
- - [**#6**](https://github.com/hapijs/qs/issues/6) Minor grammar fix in README
86
-
87
- ## [**1.0.2**](https://github.com/hapijs/qs/issues?milestone=2&state=closed)
88
- - [**#5**](https://github.com/hapijs/qs/issues/5) array holes incorrectly copied into object on large index
4
+ ## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
5
+ - [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
6
+
7
+ ## [**5.1.0**](https://github.com/ljharb/qs/issues?milestone=29&state=closed)
8
+ - [**#117**](https://github.com/ljharb/qs/issues/117) make URI encoding stringified results optional
9
+ - [**#106**](https://github.com/ljharb/qs/issues/106) Add flag `skipNulls` to optionally skip null values in stringify
10
+
11
+ ## [**5.0.0**](https://github.com/ljharb/qs/issues?milestone=28&state=closed)
12
+ - [**#114**](https://github.com/ljharb/qs/issues/114) default allowDots to false
13
+ - [**#100**](https://github.com/ljharb/qs/issues/100) include dist to npm
14
+
15
+ ## [**4.0.0**](https://github.com/ljharb/qs/issues?milestone=26&state=closed)
16
+ - [**#98**](https://github.com/ljharb/qs/issues/98) make returning plain objects and allowing prototype overwriting properties optional
17
+
18
+ ## [**3.1.0**](https://github.com/ljharb/qs/issues?milestone=24&state=closed)
19
+ - [**#89**](https://github.com/ljharb/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
20
+
21
+ ## [**3.0.0**](https://github.com/ljharb/qs/issues?milestone=23&state=closed)
22
+ - [**#80**](https://github.com/ljharb/qs/issues/80) qs.parse silently drops properties
23
+ - [**#77**](https://github.com/ljharb/qs/issues/77) Perf boost
24
+ - [**#60**](https://github.com/ljharb/qs/issues/60) Add explicit option to disable array parsing
25
+ - [**#74**](https://github.com/ljharb/qs/issues/74) Bad parse when turning array into object
26
+ - [**#81**](https://github.com/ljharb/qs/issues/81) Add a `filter` option
27
+ - [**#68**](https://github.com/ljharb/qs/issues/68) Fixed issue with recursion and passing strings into objects.
28
+ - [**#66**](https://github.com/ljharb/qs/issues/66) Add mixed array and object dot notation support Closes: #47
29
+ - [**#76**](https://github.com/ljharb/qs/issues/76) RFC 3986
30
+ - [**#85**](https://github.com/ljharb/qs/issues/85) No equal sign
31
+ - [**#84**](https://github.com/ljharb/qs/issues/84) update license attribute
32
+
33
+ ## [**2.4.1**](https://github.com/ljharb/qs/issues?milestone=20&state=closed)
34
+ - [**#73**](https://github.com/ljharb/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
35
+
36
+ ## [**2.4.0**](https://github.com/ljharb/qs/issues?milestone=19&state=closed)
37
+ - [**#70**](https://github.com/ljharb/qs/issues/70) Add arrayFormat option
38
+
39
+ ## [**2.3.3**](https://github.com/ljharb/qs/issues?milestone=18&state=closed)
40
+ - [**#59**](https://github.com/ljharb/qs/issues/59) make sure array indexes are >= 0, closes #57
41
+ - [**#58**](https://github.com/ljharb/qs/issues/58) make qs usable for browser loader
42
+
43
+ ## [**2.3.2**](https://github.com/ljharb/qs/issues?milestone=17&state=closed)
44
+ - [**#55**](https://github.com/ljharb/qs/issues/55) allow merging a string into an object
45
+
46
+ ## [**2.3.1**](https://github.com/ljharb/qs/issues?milestone=16&state=closed)
47
+ - [**#52**](https://github.com/ljharb/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
48
+
49
+ ## [**2.3.0**](https://github.com/ljharb/qs/issues?milestone=15&state=closed)
50
+ - [**#50**](https://github.com/ljharb/qs/issues/50) add option to omit array indices, closes #46
51
+
52
+ ## [**2.2.5**](https://github.com/ljharb/qs/issues?milestone=14&state=closed)
53
+ - [**#39**](https://github.com/ljharb/qs/issues/39) Is there an alternative to Buffer.isBuffer?
54
+ - [**#49**](https://github.com/ljharb/qs/issues/49) refactor utils.merge, fixes #45
55
+ - [**#41**](https://github.com/ljharb/qs/issues/41) avoid browserifying Buffer, for #39
56
+
57
+ ## [**2.2.4**](https://github.com/ljharb/qs/issues?milestone=13&state=closed)
58
+ - [**#38**](https://github.com/ljharb/qs/issues/38) how to handle object keys beginning with a number
59
+
60
+ ## [**2.2.3**](https://github.com/ljharb/qs/issues?milestone=12&state=closed)
61
+ - [**#37**](https://github.com/ljharb/qs/issues/37) parser discards first empty value in array
62
+ - [**#36**](https://github.com/ljharb/qs/issues/36) Update to lab 4.x
63
+
64
+ ## [**2.2.2**](https://github.com/ljharb/qs/issues?milestone=11&state=closed)
65
+ - [**#33**](https://github.com/ljharb/qs/issues/33) Error when plain object in a value
66
+ - [**#34**](https://github.com/ljharb/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
67
+ - [**#24**](https://github.com/ljharb/qs/issues/24) Changelog? Semver?
68
+
69
+ ## [**2.2.1**](https://github.com/ljharb/qs/issues?milestone=10&state=closed)
70
+ - [**#32**](https://github.com/ljharb/qs/issues/32) account for circular references properly, closes #31
71
+ - [**#31**](https://github.com/ljharb/qs/issues/31) qs.parse stackoverflow on circular objects
72
+
73
+ ## [**2.2.0**](https://github.com/ljharb/qs/issues?milestone=9&state=closed)
74
+ - [**#26**](https://github.com/ljharb/qs/issues/26) Don't use Buffer global if it's not present
75
+ - [**#30**](https://github.com/ljharb/qs/issues/30) Bug when merging non-object values into arrays
76
+ - [**#29**](https://github.com/ljharb/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
77
+ - [**#23**](https://github.com/ljharb/qs/issues/23) Ability to not limit parameters?
78
+
79
+ ## [**2.1.0**](https://github.com/ljharb/qs/issues?milestone=8&state=closed)
80
+ - [**#22**](https://github.com/ljharb/qs/issues/22) Enable using a RegExp as delimiter
81
+
82
+ ## [**2.0.0**](https://github.com/ljharb/qs/issues?milestone=7&state=closed)
83
+ - [**#18**](https://github.com/ljharb/qs/issues/18) Why is there arrayLimit?
84
+ - [**#20**](https://github.com/ljharb/qs/issues/20) Configurable parametersLimit
85
+ - [**#21**](https://github.com/ljharb/qs/issues/21) make all limits optional, for #18, for #20
86
+
87
+ ## [**1.2.2**](https://github.com/ljharb/qs/issues?milestone=6&state=closed)
88
+ - [**#19**](https://github.com/ljharb/qs/issues/19) Don't overwrite null values
89
+
90
+ ## [**1.2.1**](https://github.com/ljharb/qs/issues?milestone=5&state=closed)
91
+ - [**#16**](https://github.com/ljharb/qs/issues/16) ignore non-string delimiters
92
+ - [**#15**](https://github.com/ljharb/qs/issues/15) Close code block
93
+
94
+ ## [**1.2.0**](https://github.com/ljharb/qs/issues?milestone=4&state=closed)
95
+ - [**#12**](https://github.com/ljharb/qs/issues/12) Add optional delim argument
96
+ - [**#13**](https://github.com/ljharb/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
97
+
98
+ ## [**1.1.0**](https://github.com/ljharb/qs/issues?milestone=3&state=closed)
99
+ - [**#7**](https://github.com/ljharb/qs/issues/7) Empty values of a POST array disappear after being submitted
100
+ - [**#9**](https://github.com/ljharb/qs/issues/9) Should not omit equals signs (=) when value is null
101
+ - [**#6**](https://github.com/ljharb/qs/issues/6) Minor grammar fix in README
102
+
103
+ ## [**1.0.2**](https://github.com/ljharb/qs/issues?milestone=2&state=closed)
104
+ - [**#5**](https://github.com/ljharb/qs/issues/5) array holes incorrectly copied into object on large index
package/README.md CHANGED
@@ -23,7 +23,7 @@ var str = Qs.stringify(obj); // 'a=c'
23
23
  Qs.parse(string, [options]);
24
24
  ```
25
25
 
26
- **qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`, or prefixing the sub-key with a dot `.`.
26
+ **qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
27
27
  For example, the string `'foo[bar]=baz'` converts to:
28
28
 
29
29
  ```javascript
@@ -118,11 +118,11 @@ Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
118
118
  // { a: 'b', c: 'd', e: 'f' }
119
119
  ```
120
120
 
121
- Option `allowDots` can be used to disable dot notation:
121
+ Option `allowDots` can be used to enable dot notation:
122
122
 
123
123
  ```javascript
124
- Qs.parse('a.b=c', { allowDots: false });
125
- // { 'a.b': 'c' } }
124
+ Qs.parse('a.b=c', { allowDots: true });
125
+ // { a: { b: 'c' } }
126
126
  ```
127
127
 
128
128
  ### Parsing Arrays
@@ -201,7 +201,7 @@ Qs.parse('a[][b]=c');
201
201
  Qs.stringify(object, [options]);
202
202
  ```
203
203
 
204
- When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect:
204
+ When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
205
205
 
206
206
  ```javascript
207
207
  Qs.stringify({ a: 'b' });
@@ -210,6 +210,13 @@ Qs.stringify({ a: { b: 'c' } });
210
210
  // 'a%5Bb%5D=c'
211
211
  ```
212
212
 
213
+ This encoding can be disabled by setting the `encode` option to `false`:
214
+
215
+ ```javascript
216
+ Qs.stringify({ a: { b: 'c' } }, { encode: false });
217
+ // 'a[b]=c'
218
+ ```
219
+
213
220
  Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
214
221
 
215
222
  When arrays are stringified, by default they are given explicit indices:
@@ -315,3 +322,10 @@ Qs.parse('a&b=', { strictNullHandling: true });
315
322
  // { a: null, b: '' }
316
323
 
317
324
  ```
325
+
326
+ To completely skip rendering keys with `null` values, use the `skipNulls` flag:
327
+
328
+ ```javascript
329
+ qs.stringify({ a: 'b', c: null}, { skipNulls: true })
330
+ // 'a=b'
331
+ ```
package/bower.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qs",
3
3
  "main": "dist/qs.js",
4
- "version": "3.0.0",
4
+ "version": "5.1.0",
5
5
  "homepage": "https://github.com/hapijs/qs",
6
6
  "authors": [
7
7
  "Nathan LaFreniere <quitlahok@gmail.com>"
package/component.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "qs",
3
+ "repository": "hapijs/qs",
4
+ "description": "query-string parser / stringifier with nesting support",
5
+ "version": "5.1.0",
6
+ "keywords": ["querystring", "query", "parser"],
7
+ "main": "lib/index.js",
8
+ "scripts": [
9
+ "lib/index.js",
10
+ "lib/parse.js",
11
+ "lib/stringify.js",
12
+ "lib/utils.js"
13
+ ],
14
+ "license": "BSD-3-Clause"
15
+ }
package/dist/qs.js ADDED
@@ -0,0 +1,551 @@
1
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ // Load modules
3
+
4
+ var Stringify = require('./stringify');
5
+ var Parse = require('./parse');
6
+
7
+
8
+ // Declare internals
9
+
10
+ var internals = {};
11
+
12
+
13
+ module.exports = {
14
+ stringify: Stringify,
15
+ parse: Parse
16
+ };
17
+
18
+ },{"./parse":2,"./stringify":3}],2:[function(require,module,exports){
19
+ // Load modules
20
+
21
+ var Utils = require('./utils');
22
+
23
+
24
+ // Declare internals
25
+
26
+ var internals = {
27
+ delimiter: '&',
28
+ depth: 5,
29
+ arrayLimit: 20,
30
+ parameterLimit: 1000,
31
+ strictNullHandling: false,
32
+ plainObjects: false,
33
+ allowPrototypes: false,
34
+ allowDots: false
35
+ };
36
+
37
+
38
+ internals.parseValues = function (str, options) {
39
+
40
+ var obj = {};
41
+ var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
42
+
43
+ for (var i = 0, il = parts.length; i < il; ++i) {
44
+ var part = parts[i];
45
+ var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
46
+
47
+ var key, val;
48
+ if (pos === -1) {
49
+ key = Utils.decode(part);
50
+ val = options.strictNullHandling ? null : '';
51
+ } else {
52
+ key = Utils.decode(part.slice(0, pos));
53
+ val = Utils.decode(part.slice(pos + 1));
54
+ }
55
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
56
+ obj[key] = [].concat(obj[key]).concat(val);
57
+ } else {
58
+ obj[key] = val;
59
+ }
60
+ }
61
+
62
+ return obj;
63
+ };
64
+
65
+
66
+ internals.parseObject = function (chain, val, options) {
67
+
68
+ if (!chain.length) {
69
+ return val;
70
+ }
71
+
72
+ var root = chain.shift();
73
+
74
+ var obj;
75
+ if (root === '[]') {
76
+ obj = [];
77
+ obj = obj.concat(internals.parseObject(chain, val, options));
78
+ }
79
+ else {
80
+ obj = options.plainObjects ? Object.create(null) : {};
81
+ var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
82
+ var index = parseInt(cleanRoot, 10);
83
+ var indexString = '' + index;
84
+ if (!isNaN(index) &&
85
+ root !== cleanRoot &&
86
+ indexString === cleanRoot &&
87
+ index >= 0 &&
88
+ (options.parseArrays &&
89
+ index <= options.arrayLimit)) {
90
+
91
+ obj = [];
92
+ obj[index] = internals.parseObject(chain, val, options);
93
+ }
94
+ else {
95
+ obj[cleanRoot] = internals.parseObject(chain, val, options);
96
+ }
97
+ }
98
+
99
+ return obj;
100
+ };
101
+
102
+
103
+ internals.parseKeys = function (key, val, options) {
104
+
105
+ if (!key) {
106
+ return;
107
+ }
108
+
109
+ // Transform dot notation to bracket notation
110
+
111
+ if (options.allowDots) {
112
+ key = key.replace(/\.([^\.\[]+)/g, '[$1]');
113
+ }
114
+
115
+ // The regex chunks
116
+
117
+ var parent = /^([^\[\]]*)/;
118
+ var child = /(\[[^\[\]]*\])/g;
119
+
120
+ // Get the parent
121
+
122
+ var segment = parent.exec(key);
123
+
124
+ // Stash the parent if it exists
125
+
126
+ var keys = [];
127
+ if (segment[1]) {
128
+ // If we aren't using plain objects, optionally prefix keys
129
+ // that would overwrite object prototype properties
130
+ if (!options.plainObjects &&
131
+ Object.prototype.hasOwnProperty(segment[1])) {
132
+
133
+ if (!options.allowPrototypes) {
134
+ return;
135
+ }
136
+ }
137
+
138
+ keys.push(segment[1]);
139
+ }
140
+
141
+ // Loop through children appending to the array until we hit depth
142
+
143
+ var i = 0;
144
+ while ((segment = child.exec(key)) !== null && i < options.depth) {
145
+
146
+ ++i;
147
+ if (!options.plainObjects &&
148
+ Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
149
+
150
+ if (!options.allowPrototypes) {
151
+ continue;
152
+ }
153
+ }
154
+ keys.push(segment[1]);
155
+ }
156
+
157
+ // If there's a remainder, just add whatever is left
158
+
159
+ if (segment) {
160
+ keys.push('[' + key.slice(segment.index) + ']');
161
+ }
162
+
163
+ return internals.parseObject(keys, val, options);
164
+ };
165
+
166
+
167
+ module.exports = function (str, options) {
168
+
169
+ options = options || {};
170
+ options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
171
+ options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
172
+ options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
173
+ options.parseArrays = options.parseArrays !== false;
174
+ options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
175
+ options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
176
+ options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
177
+ options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
178
+ options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
179
+
180
+ if (str === '' ||
181
+ str === null ||
182
+ typeof str === 'undefined') {
183
+
184
+ return options.plainObjects ? Object.create(null) : {};
185
+ }
186
+
187
+ var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
188
+ var obj = options.plainObjects ? Object.create(null) : {};
189
+
190
+ // Iterate over the keys and setup the new object
191
+
192
+ var keys = Object.keys(tempObj);
193
+ for (var i = 0, il = keys.length; i < il; ++i) {
194
+ var key = keys[i];
195
+ var newObj = internals.parseKeys(key, tempObj[key], options);
196
+ obj = Utils.merge(obj, newObj, options);
197
+ }
198
+
199
+ return Utils.compact(obj);
200
+ };
201
+
202
+ },{"./utils":4}],3:[function(require,module,exports){
203
+ // Load modules
204
+
205
+ var Utils = require('./utils');
206
+
207
+
208
+ // Declare internals
209
+
210
+ var internals = {
211
+ delimiter: '&',
212
+ arrayPrefixGenerators: {
213
+ brackets: function (prefix, key) {
214
+
215
+ return prefix + '[]';
216
+ },
217
+ indices: function (prefix, key) {
218
+
219
+ return prefix + '[' + key + ']';
220
+ },
221
+ repeat: function (prefix, key) {
222
+
223
+ return prefix;
224
+ }
225
+ },
226
+ strictNullHandling: false,
227
+ skipNulls: false,
228
+ encode: true
229
+ };
230
+
231
+
232
+ internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) {
233
+
234
+ if (typeof filter === 'function') {
235
+ obj = filter(prefix, obj);
236
+ }
237
+ else if (Utils.isBuffer(obj)) {
238
+ obj = obj.toString();
239
+ }
240
+ else if (obj instanceof Date) {
241
+ obj = obj.toISOString();
242
+ }
243
+ else if (obj === null) {
244
+ if (strictNullHandling) {
245
+ return encode ? Utils.encode(prefix) : prefix;
246
+ }
247
+
248
+ obj = '';
249
+ }
250
+
251
+ if (typeof obj === 'string' ||
252
+ typeof obj === 'number' ||
253
+ typeof obj === 'boolean') {
254
+
255
+ if (encode) {
256
+ return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
257
+ }
258
+ return [prefix + '=' + obj];
259
+ }
260
+
261
+ var values = [];
262
+
263
+ if (typeof obj === 'undefined') {
264
+ return values;
265
+ }
266
+
267
+ var objKeys;
268
+ if (Array.isArray(filter)) {
269
+ objKeys = filter;
270
+ } else {
271
+ var keys = Object.keys(obj);
272
+ objKeys = sort ? keys.sort(sort) : keys;
273
+ }
274
+
275
+ for (var i = 0, il = objKeys.length; i < il; ++i) {
276
+ var key = objKeys[i];
277
+
278
+ if (skipNulls &&
279
+ obj[key] === null) {
280
+
281
+ continue;
282
+ }
283
+
284
+ if (Array.isArray(obj)) {
285
+ values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
286
+ }
287
+ else {
288
+ values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
289
+ }
290
+ }
291
+
292
+ return values;
293
+ };
294
+
295
+
296
+ module.exports = function (obj, options) {
297
+
298
+ options = options || {};
299
+ var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
300
+ var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
301
+ var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
302
+ var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
303
+ var sort = typeof options.sort === 'function' ? options.sort : null;
304
+ var objKeys;
305
+ var filter;
306
+ if (typeof options.filter === 'function') {
307
+ filter = options.filter;
308
+ obj = filter('', obj);
309
+ }
310
+ else if (Array.isArray(options.filter)) {
311
+ objKeys = filter = options.filter;
312
+ }
313
+
314
+ var keys = [];
315
+
316
+ if (typeof obj !== 'object' ||
317
+ obj === null) {
318
+
319
+ return '';
320
+ }
321
+
322
+ var arrayFormat;
323
+ if (options.arrayFormat in internals.arrayPrefixGenerators) {
324
+ arrayFormat = options.arrayFormat;
325
+ }
326
+ else if ('indices' in options) {
327
+ arrayFormat = options.indices ? 'indices' : 'repeat';
328
+ }
329
+ else {
330
+ arrayFormat = 'indices';
331
+ }
332
+
333
+ var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
334
+
335
+ if (!objKeys) {
336
+ objKeys = Object.keys(obj);
337
+ }
338
+
339
+ if (sort) {
340
+ objKeys.sort(sort);
341
+ }
342
+
343
+ for (var i = 0, il = objKeys.length; i < il; ++i) {
344
+ var key = objKeys[i];
345
+
346
+ if (skipNulls &&
347
+ obj[key] === null) {
348
+
349
+ continue;
350
+ }
351
+
352
+ keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort));
353
+ }
354
+
355
+ return keys.join(delimiter);
356
+ };
357
+
358
+ },{"./utils":4}],4:[function(require,module,exports){
359
+ // Load modules
360
+
361
+
362
+ // Declare internals
363
+
364
+ var internals = {};
365
+ internals.hexTable = new Array(256);
366
+ for (var h = 0; h < 256; ++h) {
367
+ internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase();
368
+ }
369
+
370
+
371
+ exports.arrayToObject = function (source, options) {
372
+
373
+ var obj = options.plainObjects ? Object.create(null) : {};
374
+ for (var i = 0, il = source.length; i < il; ++i) {
375
+ if (typeof source[i] !== 'undefined') {
376
+
377
+ obj[i] = source[i];
378
+ }
379
+ }
380
+
381
+ return obj;
382
+ };
383
+
384
+
385
+ exports.merge = function (target, source, options) {
386
+
387
+ if (!source) {
388
+ return target;
389
+ }
390
+
391
+ if (typeof source !== 'object') {
392
+ if (Array.isArray(target)) {
393
+ target.push(source);
394
+ }
395
+ else if (typeof target === 'object') {
396
+ target[source] = true;
397
+ }
398
+ else {
399
+ target = [target, source];
400
+ }
401
+
402
+ return target;
403
+ }
404
+
405
+ if (typeof target !== 'object') {
406
+ target = [target].concat(source);
407
+ return target;
408
+ }
409
+
410
+ if (Array.isArray(target) &&
411
+ !Array.isArray(source)) {
412
+
413
+ target = exports.arrayToObject(target, options);
414
+ }
415
+
416
+ var keys = Object.keys(source);
417
+ for (var k = 0, kl = keys.length; k < kl; ++k) {
418
+ var key = keys[k];
419
+ var value = source[key];
420
+
421
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
422
+ target[key] = value;
423
+ }
424
+ else {
425
+ target[key] = exports.merge(target[key], value, options);
426
+ }
427
+ }
428
+
429
+ return target;
430
+ };
431
+
432
+
433
+ exports.decode = function (str) {
434
+
435
+ try {
436
+ return decodeURIComponent(str.replace(/\+/g, ' '));
437
+ } catch (e) {
438
+ return str;
439
+ }
440
+ };
441
+
442
+ exports.encode = function (str) {
443
+
444
+ // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
445
+ // It has been adapted here for stricter adherence to RFC 3986
446
+ if (str.length === 0) {
447
+ return str;
448
+ }
449
+
450
+ if (typeof str !== 'string') {
451
+ str = '' + str;
452
+ }
453
+
454
+ var out = '';
455
+ for (var i = 0, il = str.length; i < il; ++i) {
456
+ var c = str.charCodeAt(i);
457
+
458
+ if (c === 0x2D || // -
459
+ c === 0x2E || // .
460
+ c === 0x5F || // _
461
+ c === 0x7E || // ~
462
+ (c >= 0x30 && c <= 0x39) || // 0-9
463
+ (c >= 0x41 && c <= 0x5A) || // a-z
464
+ (c >= 0x61 && c <= 0x7A)) { // A-Z
465
+
466
+ out += str[i];
467
+ continue;
468
+ }
469
+
470
+ if (c < 0x80) {
471
+ out += internals.hexTable[c];
472
+ continue;
473
+ }
474
+
475
+ if (c < 0x800) {
476
+ out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)];
477
+ continue;
478
+ }
479
+
480
+ if (c < 0xD800 || c >= 0xE000) {
481
+ out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
482
+ continue;
483
+ }
484
+
485
+ ++i;
486
+ c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
487
+ out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
488
+ }
489
+
490
+ return out;
491
+ };
492
+
493
+ exports.compact = function (obj, refs) {
494
+
495
+ if (typeof obj !== 'object' ||
496
+ obj === null) {
497
+
498
+ return obj;
499
+ }
500
+
501
+ refs = refs || [];
502
+ var lookup = refs.indexOf(obj);
503
+ if (lookup !== -1) {
504
+ return refs[lookup];
505
+ }
506
+
507
+ refs.push(obj);
508
+
509
+ if (Array.isArray(obj)) {
510
+ var compacted = [];
511
+
512
+ for (var i = 0, il = obj.length; i < il; ++i) {
513
+ if (typeof obj[i] !== 'undefined') {
514
+ compacted.push(obj[i]);
515
+ }
516
+ }
517
+
518
+ return compacted;
519
+ }
520
+
521
+ var keys = Object.keys(obj);
522
+ for (i = 0, il = keys.length; i < il; ++i) {
523
+ var key = keys[i];
524
+ obj[key] = exports.compact(obj[key], refs);
525
+ }
526
+
527
+ return obj;
528
+ };
529
+
530
+
531
+ exports.isRegExp = function (obj) {
532
+
533
+ return Object.prototype.toString.call(obj) === '[object RegExp]';
534
+ };
535
+
536
+
537
+ exports.isBuffer = function (obj) {
538
+
539
+ if (obj === null ||
540
+ typeof obj === 'undefined') {
541
+
542
+ return false;
543
+ }
544
+
545
+ return !!(obj.constructor &&
546
+ obj.constructor.isBuffer &&
547
+ obj.constructor.isBuffer(obj));
548
+ };
549
+
550
+ },{}]},{},[1])(1)
551
+ });
package/lib/parse.js CHANGED
@@ -12,7 +12,8 @@ var internals = {
12
12
  parameterLimit: 1000,
13
13
  strictNullHandling: false,
14
14
  plainObjects: false,
15
- allowPrototypes: false
15
+ allowPrototypes: false,
16
+ allowDots: false
16
17
  };
17
18
 
18
19
 
@@ -25,23 +26,18 @@ internals.parseValues = function (str, options) {
25
26
  var part = parts[i];
26
27
  var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
27
28
 
29
+ var key, val;
28
30
  if (pos === -1) {
29
- obj[Utils.decode(part)] = '';
30
-
31
- if (options.strictNullHandling) {
32
- obj[Utils.decode(part)] = null;
33
- }
31
+ key = Utils.decode(part);
32
+ val = options.strictNullHandling ? null : '';
33
+ } else {
34
+ key = Utils.decode(part.slice(0, pos));
35
+ val = Utils.decode(part.slice(pos + 1));
34
36
  }
35
- else {
36
- var key = Utils.decode(part.slice(0, pos));
37
- var val = Utils.decode(part.slice(pos + 1));
38
-
39
- if (!Object.prototype.hasOwnProperty.call(obj, key)) {
40
- obj[key] = val;
41
- }
42
- else {
43
- obj[key] = [].concat(obj[key]).concat(val);
44
- }
37
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
38
+ obj[key] = [].concat(obj[key]).concat(val);
39
+ } else {
40
+ obj[key] = val;
45
41
  }
46
42
  }
47
43
 
@@ -157,7 +153,7 @@ module.exports = function (str, options) {
157
153
  options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
158
154
  options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
159
155
  options.parseArrays = options.parseArrays !== false;
160
- options.allowDots = options.allowDots !== false;
156
+ options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
161
157
  options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
162
158
  options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
163
159
  options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
package/lib/stringify.js CHANGED
@@ -21,11 +21,13 @@ var internals = {
21
21
  return prefix;
22
22
  }
23
23
  },
24
- strictNullHandling: false
24
+ strictNullHandling: false,
25
+ skipNulls: false,
26
+ encode: true
25
27
  };
26
28
 
27
29
 
28
- internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, filter) {
30
+ internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) {
29
31
 
30
32
  if (typeof filter === 'function') {
31
33
  obj = filter(prefix, obj);
@@ -38,7 +40,7 @@ internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHand
38
40
  }
39
41
  else if (obj === null) {
40
42
  if (strictNullHandling) {
41
- return Utils.encode(prefix);
43
+ return encode ? Utils.encode(prefix) : prefix;
42
44
  }
43
45
 
44
46
  obj = '';
@@ -48,7 +50,10 @@ internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHand
48
50
  typeof obj === 'number' ||
49
51
  typeof obj === 'boolean') {
50
52
 
51
- return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
53
+ if (encode) {
54
+ return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
55
+ }
56
+ return [prefix + '=' + obj];
52
57
  }
53
58
 
54
59
  var values = [];
@@ -57,15 +62,28 @@ internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHand
57
62
  return values;
58
63
  }
59
64
 
60
- var objKeys = Array.isArray(filter) ? filter : Object.keys(obj);
65
+ var objKeys;
66
+ if (Array.isArray(filter)) {
67
+ objKeys = filter;
68
+ } else {
69
+ var keys = Object.keys(obj);
70
+ objKeys = sort ? keys.sort(sort) : keys;
71
+ }
72
+
61
73
  for (var i = 0, il = objKeys.length; i < il; ++i) {
62
74
  var key = objKeys[i];
63
75
 
76
+ if (skipNulls &&
77
+ obj[key] === null) {
78
+
79
+ continue;
80
+ }
81
+
64
82
  if (Array.isArray(obj)) {
65
- values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, filter));
83
+ values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
66
84
  }
67
85
  else {
68
- values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, filter));
86
+ values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
69
87
  }
70
88
  }
71
89
 
@@ -78,6 +96,9 @@ module.exports = function (obj, options) {
78
96
  options = options || {};
79
97
  var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
80
98
  var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
99
+ var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
100
+ var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
101
+ var sort = typeof options.sort === 'function' ? options.sort : null;
81
102
  var objKeys;
82
103
  var filter;
83
104
  if (typeof options.filter === 'function') {
@@ -112,9 +133,21 @@ module.exports = function (obj, options) {
112
133
  if (!objKeys) {
113
134
  objKeys = Object.keys(obj);
114
135
  }
136
+
137
+ if (sort) {
138
+ objKeys.sort(sort);
139
+ }
140
+
115
141
  for (var i = 0, il = objKeys.length; i < il; ++i) {
116
142
  var key = objKeys[i];
117
- keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, filter));
143
+
144
+ if (skipNulls &&
145
+ obj[key] === null) {
146
+
147
+ continue;
148
+ }
149
+
150
+ keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort));
118
151
  }
119
152
 
120
153
  return keys.join(delimiter);
package/package.json CHANGED
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "name": "qs",
3
- "version": "4.0.0",
4
3
  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
5
4
  "homepage": "https://github.com/hapijs/qs",
5
+ "version": "5.2.1",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/hapijs/qs.git"
9
+ },
6
10
  "main": "lib/index.js",
11
+ "keywords": [
12
+ "querystring",
13
+ "qs"
14
+ ],
15
+ "engines": ">=0.10.40",
7
16
  "dependencies": {},
8
17
  "devDependencies": {
9
18
  "browserify": "^10.2.1",
@@ -12,16 +21,9 @@
12
21
  },
13
22
  "scripts": {
14
23
  "test": "lab -a code -t 100 -L",
24
+ "test-tap": "lab -a code -r tap -o tests.tap",
15
25
  "test-cov-html": "lab -a code -r html -o coverage.html",
16
26
  "dist": "browserify --standalone Qs lib/index.js > dist/qs.js"
17
27
  },
18
- "repository": {
19
- "type": "git",
20
- "url": "https://github.com/hapijs/qs.git"
21
- },
22
- "keywords": [
23
- "querystring",
24
- "qs"
25
- ],
26
28
  "license": "BSD-3-Clause"
27
29
  }
package/test/parse.js CHANGED
@@ -47,10 +47,10 @@ describe('parse()', function () {
47
47
  done();
48
48
  });
49
49
 
50
- it('allows disabling dot notation', function (done) {
50
+ it('allows enabling dot notation', function (done) {
51
51
 
52
- expect(Qs.parse('a.b=c')).to.deep.equal({ a: { b: 'c' } });
53
- expect(Qs.parse('a.b=c', { allowDots: false })).to.deep.equal({ 'a.b': 'c' });
52
+ expect(Qs.parse('a.b=c')).to.deep.equal({ 'a.b': 'c' });
53
+ expect(Qs.parse('a.b=c', { allowDots: true })).to.deep.equal({ a: { b: 'c' } });
54
54
  done();
55
55
  });
56
56
 
@@ -175,16 +175,16 @@ describe('parse()', function () {
175
175
 
176
176
  it('transforms arrays to objects (dot notation)', function (done) {
177
177
 
178
- expect(Qs.parse('foo[0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
179
- expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
180
- expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
181
- expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] });
182
- expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] });
183
- expect(Qs.parse('foo.bad=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
184
- expect(Qs.parse('foo.bad=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
185
- expect(Qs.parse('foo[]=bar&foo.bad=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
186
- expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
187
- expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
178
+ expect(Qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
179
+ expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
180
+ expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
181
+ expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] });
182
+ expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] });
183
+ expect(Qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
184
+ expect(Qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
185
+ expect(Qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true })).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
186
+ expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
187
+ expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true })).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
188
188
  done();
189
189
  });
190
190
 
@@ -232,8 +232,13 @@ describe('parse()', function () {
232
232
  it('allows for empty strings in arrays', function (done) {
233
233
 
234
234
  expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] });
235
- expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true })).to.deep.equal({ a: ['b', null, 'c', ''] });
236
- expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true })).to.deep.equal({ a: ['b', '', 'c', null] });
235
+
236
+ expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', null, 'c', ''] });
237
+ expect(Qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', null, 'c', ''] });
238
+
239
+ expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', '', 'c', null] });
240
+ expect(Qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', '', 'c', null] });
241
+
237
242
  expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] });
238
243
  done();
239
244
  });
@@ -372,7 +377,7 @@ describe('parse()', function () {
372
377
  }
373
378
  };
374
379
 
375
- var result = Qs.parse(input);
380
+ var result = Qs.parse(input, { allowDots: true });
376
381
 
377
382
  expect(result).to.deep.equal(expected);
378
383
  done();
package/test/stringify.js CHANGED
@@ -47,6 +47,19 @@ describe('stringify()', function () {
47
47
  done();
48
48
  });
49
49
 
50
+ it('omits nulls when asked', function (done) {
51
+
52
+ expect(Qs.stringify({ a: 'b', c: null }, { skipNulls: true })).to.equal('a=b');
53
+ done();
54
+ });
55
+
56
+
57
+ it('omits nested nulls when asked', function (done) {
58
+
59
+ expect(Qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true })).to.equal('a%5Bb%5D=c');
60
+ done();
61
+ });
62
+
50
63
  it('omits array indices when asked', function (done) {
51
64
 
52
65
  expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d');
@@ -216,8 +229,9 @@ describe('stringify()', function () {
216
229
 
217
230
  var tempBuffer = global.Buffer;
218
231
  delete global.Buffer;
219
- expect(Qs.stringify({ a: 'b', c: 'd' })).to.equal('a=b&c=d');
232
+ var result = Qs.stringify({ a: 'b', c: 'd' });
220
233
  global.Buffer = tempBuffer;
234
+ expect(result).to.equal('a=b&c=d');
221
235
  done();
222
236
  });
223
237
 
@@ -256,4 +270,24 @@ describe('stringify()', function () {
256
270
  done();
257
271
 
258
272
  });
273
+
274
+ it('can disable uri encoding', function (done) {
275
+
276
+ expect(Qs.stringify({ a: 'b' }, { encode: false })).to.equal('a=b');
277
+ expect(Qs.stringify({ a: { b: 'c' } }, { encode: false })).to.equal('a[b]=c');
278
+ expect(Qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false })).to.equal('a=b&c');
279
+ done();
280
+ });
281
+
282
+ it('can sort the keys', function (done) {
283
+
284
+ var sort = function alphabeticalSort (a, b) {
285
+
286
+ return a.localeCompare(b);
287
+ };
288
+
289
+ expect(Qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z=y');
290
+ expect(Qs.stringify({ a: 'c', z: { j: 'a', i:'b' }, b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a');
291
+ done();
292
+ });
259
293
  });