qs 0.4.0 → 0.5.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.
File without changes
@@ -0,0 +1,351 @@
1
+
2
+ /**
3
+ * Require the given path.
4
+ *
5
+ * @param {String} path
6
+ * @return {Object} exports
7
+ * @api public
8
+ */
9
+
10
+ function require(p, parent){
11
+ var path = require.resolve(p)
12
+ , mod = require.modules[path];
13
+ if (!mod) throw new Error('failed to require "' + p + '" in ' + parent);
14
+ if (!mod.exports) {
15
+ mod.exports = {};
16
+ mod.client = true;
17
+ mod.call(mod.exports, mod, mod.exports, require.relative(path));
18
+ }
19
+ return mod.exports;
20
+ }
21
+
22
+ /**
23
+ * Registered modules.
24
+ */
25
+
26
+ require.modules = {};
27
+
28
+ /**
29
+ * Resolve `path`.
30
+ *
31
+ * @param {String} path
32
+ * @return {Object} module
33
+ * @api public
34
+ */
35
+
36
+ require.resolve = function(path){
37
+ var orig = path
38
+ , reg = path + '.js'
39
+ , index = path + '/index.js';
40
+ return require.modules[reg] && reg
41
+ || require.modules[index] && index
42
+ || orig;
43
+ };
44
+
45
+ /**
46
+ * Register module at `path` with callback `fn`.
47
+ *
48
+ * @param {String} path
49
+ * @param {Function} fn
50
+ * @api public
51
+ */
52
+
53
+ require.register = function(path, fn){
54
+ require.modules[path] = fn;
55
+ };
56
+
57
+ /**
58
+ * Defines and executes anonymous module immediately, while preserving relative
59
+ * paths.
60
+ *
61
+ * @param {String} path
62
+ * @param {Function} require ref
63
+ * @api public
64
+ */
65
+
66
+ require.exec = function (path, fn) {
67
+ fn.call(window, require.relative(path));
68
+ };
69
+
70
+ /**
71
+ * Return a require function relative to the `parent` path.
72
+ *
73
+ * @param {String} parent
74
+ * @return {Function}
75
+ * @api private
76
+ */
77
+
78
+ require.relative = function(parent) {
79
+ return function(p){
80
+ if ('.' != p[0]) return require(p);
81
+
82
+ var path = parent.split('/')
83
+ , segs = p.split('/');
84
+ path.pop();
85
+
86
+ for (var i = 0; i < segs.length; i++) {
87
+ var seg = segs[i];
88
+ if ('..' == seg) path.pop();
89
+ else if ('.' != seg) path.push(seg);
90
+ }
91
+
92
+ return require(path.join('/'), parent);
93
+ };
94
+ };
95
+ // component qs: querystring
96
+ require.register("querystring", function(module, exports, require){
97
+ ;(function(){
98
+
99
+ /**
100
+ * Object#toString() ref for stringify().
101
+ */
102
+
103
+ var toString = Object.prototype.toString;
104
+
105
+ /**
106
+ * Cache non-integer test regexp.
107
+ */
108
+
109
+ var isint = /^[0-9]+$/;
110
+
111
+ function promote(parent, key) {
112
+ if (parent[key].length == 0) return parent[key] = {};
113
+ var t = {};
114
+ for (var i in parent[key]) t[i] = parent[key][i];
115
+ parent[key] = t;
116
+ return t;
117
+ }
118
+
119
+ function parse(parts, parent, key, val) {
120
+ var part = parts.shift();
121
+ // end
122
+ if (!part) {
123
+ if (Array.isArray(parent[key])) {
124
+ parent[key].push(val);
125
+ } else if ('object' == typeof parent[key]) {
126
+ parent[key] = val;
127
+ } else if ('undefined' == typeof parent[key]) {
128
+ parent[key] = val;
129
+ } else {
130
+ parent[key] = [parent[key], val];
131
+ }
132
+ // array
133
+ } else {
134
+ var obj = parent[key] = parent[key] || [];
135
+ if (']' == part) {
136
+ if (Array.isArray(obj)) {
137
+ if ('' != val) obj.push(val);
138
+ } else if ('object' == typeof obj) {
139
+ obj[Object.keys(obj).length] = val;
140
+ } else {
141
+ obj = parent[key] = [parent[key], val];
142
+ }
143
+ // prop
144
+ } else if (~part.indexOf(']')) {
145
+ part = part.substr(0, part.length - 1);
146
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
147
+ parse(parts, obj, part, val);
148
+ // key
149
+ } else {
150
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
151
+ parse(parts, obj, part, val);
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Merge parent key/val pair.
158
+ */
159
+
160
+ function merge(parent, key, val){
161
+ if (~key.indexOf(']')) {
162
+ var parts = key.split('[')
163
+ , len = parts.length
164
+ , last = len - 1;
165
+ parse(parts, parent, 'base', val);
166
+ // optimize
167
+ } else {
168
+ if (!isint.test(key) && Array.isArray(parent.base)) {
169
+ var t = {};
170
+ for (var k in parent.base) t[k] = parent.base[k];
171
+ parent.base = t;
172
+ }
173
+ set(parent.base, key, val);
174
+ }
175
+
176
+ return parent;
177
+ }
178
+
179
+ /**
180
+ * Parse the given obj.
181
+ */
182
+
183
+ function parseObject(obj){
184
+ var ret = { base: {} };
185
+ Object.keys(obj).forEach(function(name){
186
+ merge(ret, name, obj[name]);
187
+ });
188
+ return ret.base;
189
+ }
190
+
191
+ /**
192
+ * Parse the given str.
193
+ */
194
+
195
+ function parseString(str){
196
+ return String(str)
197
+ .split('&')
198
+ .reduce(function(ret, pair){
199
+ try{
200
+ pair = decodeURIComponent(pair.replace(/\+/g, ' '));
201
+ } catch(e) {
202
+ // ignore
203
+ }
204
+
205
+ var eql = pair.indexOf('=')
206
+ , brace = lastBraceInKey(pair)
207
+ , key = pair.substr(0, brace || eql)
208
+ , val = pair.substr(brace || eql, pair.length)
209
+ , val = val.substr(val.indexOf('=') + 1, val.length);
210
+
211
+ // ?foo
212
+ if ('' == key) key = pair, val = '';
213
+
214
+ return merge(ret, key, val);
215
+ }, { base: {} }).base;
216
+ }
217
+
218
+ /**
219
+ * Parse the given query `str` or `obj`, returning an object.
220
+ *
221
+ * @param {String} str | {Object} obj
222
+ * @return {Object}
223
+ * @api public
224
+ */
225
+
226
+ exports.parse = function(str){
227
+ if (null == str || '' == str) return {};
228
+ return 'object' == typeof str
229
+ ? parseObject(str)
230
+ : parseString(str);
231
+ };
232
+
233
+ /**
234
+ * Turn the given `obj` into a query string
235
+ *
236
+ * @param {Object} obj
237
+ * @return {String}
238
+ * @api public
239
+ */
240
+
241
+ var stringify = exports.stringify = function(obj, prefix) {
242
+ if (Array.isArray(obj)) {
243
+ return stringifyArray(obj, prefix);
244
+ } else if ('[object Object]' == toString.call(obj)) {
245
+ return stringifyObject(obj, prefix);
246
+ } else if ('string' == typeof obj) {
247
+ return stringifyString(obj, prefix);
248
+ } else {
249
+ return prefix + '=' + obj;
250
+ }
251
+ };
252
+
253
+ /**
254
+ * Stringify the given `str`.
255
+ *
256
+ * @param {String} str
257
+ * @param {String} prefix
258
+ * @return {String}
259
+ * @api private
260
+ */
261
+
262
+ function stringifyString(str, prefix) {
263
+ if (!prefix) throw new TypeError('stringify expects an object');
264
+ return prefix + '=' + encodeURIComponent(str);
265
+ }
266
+
267
+ /**
268
+ * Stringify the given `arr`.
269
+ *
270
+ * @param {Array} arr
271
+ * @param {String} prefix
272
+ * @return {String}
273
+ * @api private
274
+ */
275
+
276
+ function stringifyArray(arr, prefix) {
277
+ var ret = [];
278
+ if (!prefix) throw new TypeError('stringify expects an object');
279
+ for (var i = 0; i < arr.length; i++) {
280
+ ret.push(stringify(arr[i], prefix + '['+i+']'));
281
+ }
282
+ return ret.join('&');
283
+ }
284
+
285
+ /**
286
+ * Stringify the given `obj`.
287
+ *
288
+ * @param {Object} obj
289
+ * @param {String} prefix
290
+ * @return {String}
291
+ * @api private
292
+ */
293
+
294
+ function stringifyObject(obj, prefix) {
295
+ var ret = []
296
+ , keys = Object.keys(obj)
297
+ , key;
298
+
299
+ for (var i = 0, len = keys.length; i < len; ++i) {
300
+ key = keys[i];
301
+ ret.push(stringify(obj[key], prefix
302
+ ? prefix + '[' + encodeURIComponent(key) + ']'
303
+ : encodeURIComponent(key)));
304
+ }
305
+
306
+ return ret.join('&');
307
+ }
308
+
309
+ /**
310
+ * Set `obj`'s `key` to `val` respecting
311
+ * the weird and wonderful syntax of a qs,
312
+ * where "foo=bar&foo=baz" becomes an array.
313
+ *
314
+ * @param {Object} obj
315
+ * @param {String} key
316
+ * @param {String} val
317
+ * @api private
318
+ */
319
+
320
+ function set(obj, key, val) {
321
+ var v = obj[key];
322
+ if (undefined === v) {
323
+ obj[key] = val;
324
+ } else if (Array.isArray(v)) {
325
+ v.push(val);
326
+ } else {
327
+ obj[key] = [v, val];
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Locate last brace in `str` within the key.
333
+ *
334
+ * @param {String} str
335
+ * @return {Number}
336
+ * @api private
337
+ */
338
+
339
+ function lastBraceInKey(str) {
340
+ var len = str.length
341
+ , brace
342
+ , c;
343
+ for (var i = 0; i < len; ++i) {
344
+ c = str[i];
345
+ if (']' == c) brace = false;
346
+ if ('[' == c) brace = true;
347
+ if ('=' == c && !brace) return i;
348
+ }
349
+ }
350
+ })();
351
+ });
package/test/parse.js CHANGED
@@ -1,155 +1,147 @@
1
1
 
2
- /**
3
- * Module dependencies.
4
- */
2
+ if (require.register) {
3
+ var qs = require('querystring');
4
+ } else {
5
+ var qs = require('../')
6
+ , expect = require('expect.js');
7
+ }
5
8
 
6
- var qs = require('../');
9
+ describe('qs.parse()', function(){
10
+ it('should support the basics', function(){
11
+ expect(qs.parse('0=foo')).to.eql({ '0': 'foo' });
7
12
 
8
- module.exports = {
9
- 'test basics': function(){
10
- qs.parse('0=foo').should.eql({ '0': 'foo' });
13
+ expect(qs.parse('foo=c++'))
14
+ .to.eql({ foo: 'c ' });
11
15
 
12
- qs.parse('foo=c++')
13
- .should.eql({ foo: 'c ' });
16
+ expect(qs.parse('a[>=]=23'))
17
+ .to.eql({ a: { '>=': '23' }});
14
18
 
15
- qs.parse('a[>=]=23')
16
- .should.eql({ a: { '>=': '23' }});
19
+ expect(qs.parse('a[<=>]==23'))
20
+ .to.eql({ a: { '<=>': '=23' }});
17
21
 
18
- qs.parse('a[<=>]==23')
19
- .should.eql({ a: { '<=>': '=23' }});
22
+ expect(qs.parse('a[==]=23'))
23
+ .to.eql({ a: { '==': '23' }});
20
24
 
21
- qs.parse('a[==]=23')
22
- .should.eql({ a: { '==': '23' }});
25
+ expect(qs.parse('foo'))
26
+ .to.eql({ foo: '' });
23
27
 
24
- qs.parse('foo')
25
- .should.eql({ foo: '' });
28
+ expect(qs.parse('foo=bar'))
29
+ .to.eql({ foo: 'bar' });
26
30
 
27
- qs.parse('foo=bar')
28
- .should.eql({ foo: 'bar' });
31
+ expect(qs.parse(' foo = bar = baz '))
32
+ .to.eql({ ' foo ': ' bar = baz ' });
29
33
 
30
- qs.parse('foo%3Dbar=baz')
31
- .should.eql({ foo: 'bar=baz' });
34
+ expect(qs.parse('foo=bar=baz'))
35
+ .to.eql({ foo: 'bar=baz' });
32
36
 
33
- qs.parse(' foo = bar = baz ')
34
- .should.eql({ ' foo ': ' bar = baz ' });
37
+ expect(qs.parse('foo=bar&bar=baz'))
38
+ .to.eql({ foo: 'bar', bar: 'baz' });
35
39
 
36
- qs.parse('foo=bar=baz')
37
- .should.eql({ foo: 'bar=baz' });
40
+ expect(qs.parse('foo=bar&baz'))
41
+ .to.eql({ foo: 'bar', baz: '' });
38
42
 
39
- qs.parse('foo=bar&bar=baz')
40
- .should.eql({ foo: 'bar', bar: 'baz' });
41
-
42
- qs.parse('foo=bar&baz')
43
- .should.eql({ foo: 'bar', baz: '' });
44
-
45
- qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')
46
- .should.eql({
43
+ expect(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'))
44
+ .to.eql({
47
45
  cht: 'p3'
48
46
  , chd: 't:60,40'
49
47
  , chs: '250x100'
50
48
  , chl: 'Hello|World'
51
49
  });
52
- },
53
-
54
- 'test nesting': function(){
55
- qs.parse('ops[>=]=25')
56
- .should.eql({ ops: { '>=': '25' }});
57
-
58
- qs.parse('user[name]=tj')
59
- .should.eql({ user: { name: 'tj' }});
60
-
61
- qs.parse('user[name][first]=tj&user[name][last]=holowaychuk')
62
- .should.eql({ user: { name: { first: 'tj', last: 'holowaychuk' }}});
63
- },
64
-
65
- 'test escaping': function(){
66
- qs.parse('foo=foo%20bar')
67
- .should.eql({ foo: 'foo bar' });
68
- },
69
-
70
- 'test arrays': function(){
71
- qs.parse('images[]')
72
- .should.eql({ images: [] });
73
-
74
- qs.parse('user[]=tj')
75
- .should.eql({ user: ['tj'] });
76
-
77
- qs.parse('user[]=tj&user[]=tobi&user[]=jane')
78
- .should.eql({ user: ['tj', 'tobi', 'jane'] });
79
-
80
- qs.parse('user[names][]=tj&user[names][]=tyler')
81
- .should.eql({ user: { names: ['tj', 'tyler'] }});
82
-
83
- qs.parse('user[names][]=tj&user[names][]=tyler&user[email]=tj@vision-media.ca')
84
- .should.eql({ user: { names: ['tj', 'tyler'], email: 'tj@vision-media.ca' }});
85
-
86
- qs.parse('items=a&items=b')
87
- .should.eql({ items: ['a', 'b'] });
88
-
89
- qs.parse('user[names]=tj&user[names]=holowaychuk&user[names]=TJ')
90
- .should.eql({ user: { names: ['tj', 'holowaychuk', 'TJ'] }});
91
-
92
- qs.parse('user[name][first]=tj&user[name][first]=TJ')
93
- .should.eql({ user: { name: { first: ['tj', 'TJ'] }}});
94
- },
95
-
96
- 'test right-hand brackets': function(){
97
- qs.parse('pets=["tobi"]')
98
- .should.eql({ pets: '["tobi"]' });
99
-
100
- qs.parse('operators=[">=", "<="]')
101
- .should.eql({ operators: '[">=", "<="]' });
102
-
103
- qs.parse('op[>=]=[1,2,3]')
104
- .should.eql({ op: { '>=': '[1,2,3]' }});
105
-
106
- qs.parse('op[>=]=[1,2,3]&op[=]=[[[[1]]]]')
107
- .should.eql({ op: { '>=': '[1,2,3]', '=': '[[[[1]]]]' }});
108
- },
109
-
110
- 'test duplicates': function(){
111
- qs.parse('items=bar&items=baz&items=raz')
112
- .should.eql({ items: ['bar', 'baz', 'raz'] });
113
- },
114
-
115
- 'test empty': function(){
116
- qs.parse('').should.eql({});
117
- qs.parse(undefined).should.eql({});
118
- qs.parse(null).should.eql({});
119
- },
120
-
121
- 'test arrays with indexes': function(){
122
- qs.parse('foo[0]=bar&foo[1]=baz').should.eql({ foo: ['bar', 'baz'] });
123
- qs.parse('foo[1]=bar&foo[0]=baz').should.eql({ foo: ['baz', 'bar'] });
124
- qs.parse('foo[base64]=RAWR').should.eql({ foo: { base64: 'RAWR' }});
125
- qs.parse('foo[64base]=RAWR').should.eql({ foo: { '64base': 'RAWR' }});
126
- },
127
-
128
- 'test arrays becoming objects': function(){
129
- qs.parse('foo[0]=bar&foo[bad]=baz').should.eql({ foo: { 0: "bar", bad: "baz" }});
130
- qs.parse('foo[bad]=baz&foo[0]=bar').should.eql({ foo: { 0: "bar", bad: "baz" }});
131
- },
132
-
133
- 'test bleed-through of Array native properties/methods': function(){
134
- Array.prototype.protoProperty = true;
135
- Array.prototype.protoFunction = function () {};
136
- qs.parse('foo=bar').should.eql({ foo: 'bar' });
137
- },
138
-
139
- 'test malformed uri': function(){
140
- qs.parse('{%:%}').should.eql({ '{%:%}': '' });
141
- qs.parse('foo=%:%}').should.eql({ 'foo': '%:%}' });
142
- }
143
-
144
- // 'test complex': function(){
145
- // qs.parse('users[][name][first]=tj&users[foo]=bar')
146
- // .should.eql({
147
- // users: [ { name: 'tj' }, { name: 'tobi' }, { foo: 'bar' }]
148
- // });
149
- //
150
- // qs.parse('users[][name][first]=tj&users[][name][first]=tobi')
151
- // .should.eql({
152
- // users: [ { name: 'tj' }, { name: 'tobi' }]
153
- // });
154
- // }
155
- };
50
+ })
51
+
52
+ it('should support encoded = signs', function(){
53
+ expect(qs.parse('he%3Dllo=th%3Dere'))
54
+ .to.eql({ 'he=llo': 'th=ere' });
55
+ })
56
+
57
+ it('should support nesting', function(){
58
+ expect(qs.parse('ops[>=]=25'))
59
+ .to.eql({ ops: { '>=': '25' }});
60
+
61
+ expect(qs.parse('user[name]=tj'))
62
+ .to.eql({ user: { name: 'tj' }});
63
+
64
+ expect(qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'))
65
+ .to.eql({ user: { name: { first: 'tj', last: 'holowaychuk' }}});
66
+ })
67
+
68
+ it('should support array notation', function(){
69
+ expect(qs.parse('images[]'))
70
+ .to.eql({ images: [] });
71
+
72
+ expect(qs.parse('user[]=tj'))
73
+ .to.eql({ user: ['tj'] });
74
+
75
+ expect(qs.parse('user[]=tj&user[]=tobi&user[]=jane'))
76
+ .to.eql({ user: ['tj', 'tobi', 'jane'] });
77
+
78
+ expect(qs.parse('user[names][]=tj&user[names][]=tyler'))
79
+ .to.eql({ user: { names: ['tj', 'tyler'] }});
80
+
81
+ expect(qs.parse('user[names][]=tj&user[names][]=tyler&user[email]=tj@vision-media.ca'))
82
+ .to.eql({ user: { names: ['tj', 'tyler'], email: 'tj@vision-media.ca' }});
83
+
84
+ expect(qs.parse('items=a&items=b'))
85
+ .to.eql({ items: ['a', 'b'] });
86
+
87
+ expect(qs.parse('user[names]=tj&user[names]=holowaychuk&user[names]=TJ'))
88
+ .to.eql({ user: { names: ['tj', 'holowaychuk', 'TJ'] }});
89
+
90
+ expect(qs.parse('user[name][first]=tj&user[name][first]=TJ'))
91
+ .to.eql({ user: { name: { first: ['tj', 'TJ'] }}});
92
+
93
+ var o = qs.parse('existing[fcbaebfecc][name][last]=tj')
94
+ expect(o).to.eql({ existing: { 'fcbaebfecc': { name: { last: 'tj' }}}})
95
+ expect(Array.isArray(o.existing)).to.equal(false);
96
+ })
97
+
98
+ it('should support arrays with indexes', function(){
99
+ expect(qs.parse('foo[0]=bar&foo[1]=baz')).to.eql({ foo: ['bar', 'baz'] });
100
+ expect(qs.parse('foo[1]=bar&foo[0]=baz')).to.eql({ foo: ['baz', 'bar'] });
101
+ expect(qs.parse('foo[base64]=RAWR')).to.eql({ foo: { base64: 'RAWR' }});
102
+ expect(qs.parse('foo[64base]=RAWR')).to.eql({ foo: { '64base': 'RAWR' }});
103
+ })
104
+
105
+ it('should expand to an array when dupliate keys are present', function(){
106
+ expect(qs.parse('items=bar&items=baz&items=raz'))
107
+ .to.eql({ items: ['bar', 'baz', 'raz'] });
108
+ })
109
+
110
+ it('should support right-hand side brackets', function(){
111
+ expect(qs.parse('pets=["tobi"]'))
112
+ .to.eql({ pets: '["tobi"]' });
113
+
114
+ expect(qs.parse('operators=[">=", "<="]'))
115
+ .to.eql({ operators: '[">=", "<="]' });
116
+
117
+ expect(qs.parse('op[>=]=[1,2,3]'))
118
+ .to.eql({ op: { '>=': '[1,2,3]' }});
119
+
120
+ expect(qs.parse('op[>=]=[1,2,3]&op[=]=[[[[1]]]]'))
121
+ .to.eql({ op: { '>=': '[1,2,3]', '=': '[[[[1]]]]' }});
122
+ })
123
+
124
+ it('should support empty values', function(){
125
+ expect(qs.parse('')).to.eql({});
126
+ expect(qs.parse(undefined)).to.eql({});
127
+ expect(qs.parse(null)).to.eql({});
128
+ })
129
+
130
+ it('should transform arrays to objects', function(){
131
+ expect(qs.parse('foo[0]=bar&foo[bad]=baz')).to.eql({ foo: { 0: "bar", bad: "baz" }});
132
+ expect(qs.parse('foo[bad]=baz&foo[0]=bar')).to.eql({ foo: { 0: "bar", bad: "baz" }});
133
+ })
134
+
135
+ it('should support malformed uri chars', function(){
136
+ expect(qs.parse('{%:%}')).to.eql({ '{%:%}': '' });
137
+ expect(qs.parse('foo=%:%}')).to.eql({ 'foo': '%:%}' });
138
+ })
139
+
140
+ it('should support semi-parsed strings', function(){
141
+ expect(qs.parse({ 'user[name]': 'tobi' }))
142
+ .to.eql({ user: { name: 'tobi' }});
143
+
144
+ expect(qs.parse({ 'user[name]': 'tobi', 'user[email][main]': 'tobi@lb.com' }))
145
+ .to.eql({ user: { name: 'tobi', email: { main: 'tobi@lb.com' } }});
146
+ })
147
+ })