|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 'use strict'; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "unstable" |
|
9 }; |
|
10 |
|
11 let unescape = decodeURIComponent; |
|
12 exports.unescape = unescape; |
|
13 |
|
14 // encodes a string safely for application/x-www-form-urlencoded |
|
15 // adheres to RFC 3986 |
|
16 // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent |
|
17 function escape(query) { |
|
18 return encodeURIComponent(query). |
|
19 replace(/%20/g, '+'). |
|
20 replace(/!/g, '%21'). |
|
21 replace(/'/g, '%27'). |
|
22 replace(/\(/g, '%28'). |
|
23 replace(/\)/g, '%29'). |
|
24 replace(/\*/g, '%2A'); |
|
25 } |
|
26 exports.escape = escape; |
|
27 |
|
28 // Converts an object of unordered key-vals to a string that can be passed |
|
29 // as part of a request |
|
30 function stringify(options, separator, assigner) { |
|
31 separator = separator || '&'; |
|
32 assigner = assigner || '='; |
|
33 // Explicitly return null if we have null, and empty string, or empty object. |
|
34 if (!options) |
|
35 return ''; |
|
36 |
|
37 // If content is already a string, just return it as is. |
|
38 if (typeof(options) == 'string') |
|
39 return options; |
|
40 |
|
41 // At this point we have a k:v object. Iterate over it and encode each value. |
|
42 // Arrays and nested objects will get encoded as needed. For example... |
|
43 // |
|
44 // { foo: [1, 2, { omg: 'bbq', 'all your base!': 'are belong to us' }], bar: 'baz' } |
|
45 // |
|
46 // will be encoded as |
|
47 // |
|
48 // foo[0]=1&foo[1]=2&foo[2][omg]=bbq&foo[2][all+your+base!]=are+belong+to+us&bar=baz |
|
49 // |
|
50 // Keys (including '[' and ']') and values will be encoded with |
|
51 // `escape` before returning. |
|
52 // |
|
53 // Execution was inspired by jQuery, but some details have changed and numeric |
|
54 // array keys are included (whereas they are not in jQuery). |
|
55 |
|
56 let encodedContent = []; |
|
57 function add(key, val) { |
|
58 encodedContent.push(escape(key) + assigner + escape(val)); |
|
59 } |
|
60 |
|
61 function make(key, value) { |
|
62 if (value && typeof(value) === 'object') |
|
63 Object.keys(value).forEach(function(name) { |
|
64 make(key + '[' + name + ']', value[name]); |
|
65 }); |
|
66 else |
|
67 add(key, value); |
|
68 } |
|
69 |
|
70 Object.keys(options).forEach(function(name) { make(name, options[name]); }); |
|
71 return encodedContent.join(separator); |
|
72 |
|
73 //XXXzpao In theory, we can just use a FormData object on 1.9.3, but I had |
|
74 // trouble getting that working. It would also be nice to stay |
|
75 // backwards-compat as long as possible. Keeping this in for now... |
|
76 // let formData = Cc['@mozilla.org/files/formdata;1']. |
|
77 // createInstance(Ci.nsIDOMFormData); |
|
78 // for ([k, v] in Iterator(content)) { |
|
79 // formData.append(k, v); |
|
80 // } |
|
81 // return formData; |
|
82 } |
|
83 exports.stringify = stringify; |
|
84 |
|
85 // Exporting aliases that nodejs implements just for the sake of |
|
86 // interoperability. |
|
87 exports.encode = stringify; |
|
88 exports.serialize = stringify; |
|
89 |
|
90 // Note: That `stringify` and `parse` aren't bijective as we use `stringify` |
|
91 // as it was implement in request module, but implement `parse` to match nodejs |
|
92 // behavior. |
|
93 // TODO: Make `stringify` implement API as in nodejs and figure out backwards |
|
94 // compatibility. |
|
95 function parse(query, separator, assigner) { |
|
96 separator = separator || '&'; |
|
97 assigner = assigner || '='; |
|
98 let result = {}; |
|
99 |
|
100 if (typeof query !== 'string' || query.length === 0) |
|
101 return result; |
|
102 |
|
103 query.split(separator).forEach(function(chunk) { |
|
104 let pair = chunk.split(assigner); |
|
105 let key = unescape(pair[0]); |
|
106 let value = unescape(pair.slice(1).join(assigner)); |
|
107 |
|
108 if (!(key in result)) |
|
109 result[key] = value; |
|
110 else if (Array.isArray(result[key])) |
|
111 result[key].push(value); |
|
112 else |
|
113 result[key] = [result[key], value]; |
|
114 }); |
|
115 |
|
116 return result; |
|
117 }; |
|
118 exports.parse = parse; |
|
119 // Exporting aliases that nodejs implements just for the sake of |
|
120 // interoperability. |
|
121 exports.decode = parse; |