blob: a671b935c63c5146c225948284bde8fe1dcdd4b8 [file] [log] [blame]
Hans Mullerb821f932015-01-13 12:43:35 -08001// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22// Query String Utilities
23
24define("third_party/js/querystring", [
25 "third_party/js/util",
26], function(util) {
27
28var QueryString = {};
29
30// If obj.hasOwnProperty has been overridden, then calling
31// obj.hasOwnProperty(prop) will break.
32// See: https://github.com/joyent/node/issues/1707
33function hasOwnProperty(obj, prop) {
34 return Object.prototype.hasOwnProperty.call(obj, prop);
35}
36
37
38function charCode(c) {
39 return c.charCodeAt(0);
40}
41
42
43// a safe fast alternative to decodeURIComponent
44QueryString.unescapeBuffer = function(s, decodeSpaces) {
45 var out = new Buffer(s.length);
46 var state = 'CHAR'; // states: CHAR, HEX0, HEX1
47 var n, m, hexchar;
48
49 for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
50 var c = s.charCodeAt(inIndex);
51 switch (state) {
52 case 'CHAR':
53 switch (c) {
54 case charCode('%'):
55 n = 0;
56 m = 0;
57 state = 'HEX0';
58 break;
59 case charCode('+'):
60 if (decodeSpaces) c = charCode(' ');
61 // pass thru
62 default:
63 out[outIndex++] = c;
64 break;
65 }
66 break;
67
68 case 'HEX0':
69 state = 'HEX1';
70 hexchar = c;
71 if (charCode('0') <= c && c <= charCode('9')) {
72 n = c - charCode('0');
73 } else if (charCode('a') <= c && c <= charCode('f')) {
74 n = c - charCode('a') + 10;
75 } else if (charCode('A') <= c && c <= charCode('F')) {
76 n = c - charCode('A') + 10;
77 } else {
78 out[outIndex++] = charCode('%');
79 out[outIndex++] = c;
80 state = 'CHAR';
81 break;
82 }
83 break;
84
85 case 'HEX1':
86 state = 'CHAR';
87 if (charCode('0') <= c && c <= charCode('9')) {
88 m = c - charCode('0');
89 } else if (charCode('a') <= c && c <= charCode('f')) {
90 m = c - charCode('a') + 10;
91 } else if (charCode('A') <= c && c <= charCode('F')) {
92 m = c - charCode('A') + 10;
93 } else {
94 out[outIndex++] = charCode('%');
95 out[outIndex++] = hexchar;
96 out[outIndex++] = c;
97 break;
98 }
99 out[outIndex++] = 16 * n + m;
100 break;
101 }
102 }
103
104 // TODO support returning arbitrary buffers.
105
106 return out.slice(0, outIndex - 1);
107};
108
109
110QueryString.unescape = function(s, decodeSpaces) {
111 try {
112 return decodeURIComponent(s);
113 } catch (e) {
114 return QueryString.unescapeBuffer(s, decodeSpaces).toString();
115 }
116};
117
118
119QueryString.escape = function(str) {
120 return encodeURIComponent(str);
121};
122
123var stringifyPrimitive = function(v) {
124 if (util.isString(v))
125 return v;
126 if (util.isBoolean(v))
127 return v ? 'true' : 'false';
128 if (util.isNumber(v))
129 return isFinite(v) ? v : '';
130 return '';
131};
132
133
134QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
135 sep = sep || '&';
136 eq = eq || '=';
137
138 var encode = QueryString.escape;
139 if (options && typeof options.encodeURIComponent === 'function') {
140 encode = options.encodeURIComponent;
141 }
142
143 if (util.isObject(obj)) {
144 var keys = Object.keys(obj);
145 var fields = [];
146
147 for (var i = 0; i < keys.length; i++) {
148 var k = keys[i];
149 var v = obj[k];
150 var ks = encode(stringifyPrimitive(k)) + eq;
151
152 if (util.isArray(v)) {
153 for (var j = 0; j < v.length; j++)
154 fields.push(ks + encode(stringifyPrimitive(v[j])));
155 } else {
156 fields.push(ks + encode(stringifyPrimitive(v)));
157 }
158 }
159 return fields.join(sep);
160 }
161 return '';
162};
163
164// Parse a key=val string.
165QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
166 sep = sep || '&';
167 eq = eq || '=';
168 var obj = {};
169
170 if (!util.isString(qs) || qs.length === 0) {
171 return obj;
172 }
173
174 var regexp = /\+/g;
175 qs = qs.split(sep);
176
177 var maxKeys = 1000;
178 if (options && util.isNumber(options.maxKeys)) {
179 maxKeys = options.maxKeys;
180 }
181
182 var len = qs.length;
183 // maxKeys <= 0 means that we should not limit keys count
184 if (maxKeys > 0 && len > maxKeys) {
185 len = maxKeys;
186 }
187
188 var decode = QueryString.unescape;
189 if (options && typeof options.decodeURIComponent === 'function') {
190 decode = options.decodeURIComponent;
191 }
192
193 for (var i = 0; i < len; ++i) {
194 var x = qs[i].replace(regexp, '%20'),
195 idx = x.indexOf(eq),
196 kstr, vstr, k, v;
197
198 if (idx >= 0) {
199 kstr = x.substr(0, idx);
200 vstr = x.substr(idx + 1);
201 } else {
202 kstr = x;
203 vstr = '';
204 }
205
206 try {
207 k = decode(kstr);
208 v = decode(vstr);
209 } catch (e) {
210 k = QueryString.unescape(kstr, true);
211 v = QueryString.unescape(vstr, true);
212 }
213
214 if (!hasOwnProperty(obj, k)) {
215 obj[k] = v;
216 } else if (util.isArray(obj[k])) {
217 obj[k].push(v);
218 } else {
219 obj[k] = [obj[k], v];
220 }
221 }
222
223 return obj;
224};
225
226return QueryString;
227});