44
55const QueryString = exports ;
66
7- // If obj.hasOwnProperty has been overridden, then calling
8- // obj.hasOwnProperty(prop) will break.
9- // See: https://github.com/joyent/node/issues/1707
10- function hasOwnProperty ( obj , prop ) {
11- return Object . prototype . hasOwnProperty . call ( obj , prop ) ;
12- }
13-
147
158function charCode ( c ) {
169 return c . charCodeAt ( 0 ) ;
@@ -93,19 +86,68 @@ QueryString.unescape = function(s, decodeSpaces) {
9386} ;
9487
9588
89+ var hexTable = new Array ( 256 ) ;
90+ for ( var i = 0 ; i < 256 ; ++ i )
91+ hexTable [ i ] = '%' + ( ( i < 16 ? '0' : '' ) + i . toString ( 16 ) ) . toUpperCase ( ) ;
9692QueryString . escape = function ( str ) {
97- return encodeURIComponent ( str ) ;
93+ var len = str . length ;
94+ var out = '' ;
95+ var i , c ;
96+
97+ if ( len === 0 )
98+ return str ;
99+
100+ for ( i = 0 ; i < len ; ++ i ) {
101+ c = str . charCodeAt ( i ) ;
102+
103+ // These characters do not need escaping (in order):
104+ // ! - . _ ~
105+ // ' ( ) *
106+ // digits
107+ // alpha (uppercase)
108+ // alpha (lowercase)
109+ if ( c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E ||
110+ ( c >= 0x27 && c <= 0x2A ) ||
111+ ( c >= 0x30 && c <= 0x39 ) ||
112+ ( c >= 0x41 && c <= 0x5A ) ||
113+ ( c >= 0x61 && c <= 0x7A ) ) {
114+ out += str [ i ] ;
115+ continue ;
116+ }
117+
118+ // Other ASCII characters
119+ if ( c < 0x80 ) {
120+ out += hexTable [ c ] ;
121+ continue ;
122+ }
123+
124+ // Multi-byte characters ...
125+ if ( c < 0x800 ) {
126+ out += hexTable [ 0xC0 | ( c >> 6 ) ] + hexTable [ 0x80 | ( c & 0x3F ) ] ;
127+ continue ;
128+ }
129+ if ( c < 0xD800 || c >= 0xE000 ) {
130+ out += hexTable [ 0xE0 | ( c >> 12 ) ] +
131+ hexTable [ 0x80 | ( ( c >> 6 ) & 0x3F ) ] +
132+ hexTable [ 0x80 | ( c & 0x3F ) ] ;
133+ continue ;
134+ }
135+ // Surrogate pair
136+ ++ i ;
137+ c = 0x10000 + ( ( ( c & 0x3FF ) << 10 ) | ( str . charCodeAt ( i ) & 0x3FF ) ) ;
138+ out += hexTable [ 0xF0 | ( c >> 18 ) ] +
139+ hexTable [ 0x80 | ( ( c >> 12 ) & 0x3F ) ] +
140+ hexTable [ 0x80 | ( ( c >> 6 ) & 0x3F ) ] +
141+ hexTable [ 0x80 | ( c & 0x3F ) ] ;
142+ }
143+ return out ;
98144} ;
99145
100146var stringifyPrimitive = function ( v ) {
101- let type = typeof v ;
102-
103- if ( type === 'string' )
147+ if ( typeof v === 'string' || ( typeof v === 'number' && isFinite ( v ) ) )
104148 return v ;
105- if ( type === 'boolean' )
149+ if ( typeof v === 'boolean' )
106150 return v ? 'true' : 'false' ;
107- if ( type === 'number' )
108- return isFinite ( v ) ? v : '' ;
109151 return '' ;
110152} ;
111153
@@ -121,21 +163,31 @@ QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
121163
122164 if ( obj !== null && typeof obj === 'object' ) {
123165 var keys = Object . keys ( obj ) ;
124- var fields = [ ] ;
125-
126- for ( var i = 0 ; i < keys . length ; i ++ ) {
166+ var len = keys . length ;
167+ var flast = len - 1 ;
168+ var fields = '' ;
169+ for ( var i = 0 ; i < len ; ++ i ) {
127170 var k = keys [ i ] ;
128171 var v = obj [ k ] ;
129172 var ks = encode ( stringifyPrimitive ( k ) ) + eq ;
130173
131174 if ( Array . isArray ( v ) ) {
132- for ( var j = 0 ; j < v . length ; j ++ )
133- fields . push ( ks + encode ( stringifyPrimitive ( v [ j ] ) ) ) ;
175+ var vlen = v . length ;
176+ var vlast = vlen - 1 ;
177+ for ( var j = 0 ; j < vlen ; ++ j ) {
178+ fields += ks + encode ( stringifyPrimitive ( v [ j ] ) ) ;
179+ if ( j < vlast )
180+ fields += sep ;
181+ }
182+ if ( vlen && i < flast )
183+ fields += sep ;
134184 } else {
135- fields . push ( ks + encode ( stringifyPrimitive ( v ) ) ) ;
185+ fields += ks + encode ( stringifyPrimitive ( v ) ) ;
186+ if ( i < flast )
187+ fields += sep ;
136188 }
137189 }
138- return fields . join ( sep ) ;
190+ return fields ;
139191 }
140192 return '' ;
141193} ;
@@ -169,29 +221,23 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
169221 decode = options . decodeURIComponent ;
170222 }
171223
224+ var keys = [ ] ;
172225 for ( var i = 0 ; i < len ; ++ i ) {
173226 var x = qs [ i ] . replace ( regexp , '%20' ) ,
174227 idx = x . indexOf ( eq ) ,
175- kstr , vstr , k , v ;
228+ k , v ;
176229
177230 if ( idx >= 0 ) {
178- kstr = x . substr ( 0 , idx ) ;
179- vstr = x . substr ( idx + 1 ) ;
231+ k = decodeStr ( x . substring ( 0 , idx ) , decode ) ;
232+ v = decodeStr ( x . substring ( idx + 1 ) , decode ) ;
180233 } else {
181- kstr = x ;
182- vstr = '' ;
234+ k = decodeStr ( x , decode ) ;
235+ v = '' ;
183236 }
184237
185- try {
186- k = decode ( kstr ) ;
187- v = decode ( vstr ) ;
188- } catch ( e ) {
189- k = QueryString . unescape ( kstr , true ) ;
190- v = QueryString . unescape ( vstr , true ) ;
191- }
192-
193- if ( ! hasOwnProperty ( obj , k ) ) {
238+ if ( keys . indexOf ( k ) === - 1 ) {
194239 obj [ k ] = v ;
240+ keys . push ( k ) ;
195241 } else if ( Array . isArray ( obj [ k ] ) ) {
196242 obj [ k ] . push ( v ) ;
197243 } else {
@@ -201,3 +247,12 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
201247
202248 return obj ;
203249} ;
250+
251+
252+ function decodeStr ( s , decoder ) {
253+ try {
254+ return decoder ( s ) ;
255+ } catch ( e ) {
256+ return QueryString . unescape ( s , true ) ;
257+ }
258+ }
0 commit comments