Index: trunk/phase3/tests/jasmine/spec/mediawiki.Uri.spec.js |
— | — | @@ -92,9 +92,23 @@ |
93 | 93 | expect( uri.getQueryString() ).toEqual( 'q=uri' ); |
94 | 94 | } ); |
95 | 95 | |
96 | | - describe( "should handle multiple value query args", function() { |
97 | | - var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1' ); |
| 96 | + describe( "should handle multiple value query args (overrideKeys on)", function() { |
| 97 | + var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1', { overrideKeys: true } ); |
98 | 98 | it ( "should parse with multiple values", function() { |
| 99 | + expect( uri.query.m ).toEqual( 'bar' ); |
| 100 | + expect( uri.query.n ).toEqual( '1' ); |
| 101 | + } ); |
| 102 | + it ( "should accept multiple values", function() { |
| 103 | + uri.query.n = [ "x", "y", "z" ]; |
| 104 | + expect( uri.toString() ).toContain( 'm=bar' ); |
| 105 | + expect( uri.toString() ).toContain( 'n=x&n=y&n=z' ); |
| 106 | + expect( uri.toString().length ).toEqual( 'http://www.sample.com/dir/?m=bar&n=x&n=y&n=z'.length ); |
| 107 | + } ); |
| 108 | + } ); |
| 109 | + |
| 110 | + describe( "should handle multiple value query args (overrideKeys off)", function() { |
| 111 | + var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1', { overrideKeys: false } ); |
| 112 | + it ( "should parse with multiple values", function() { |
99 | 113 | expect( uri.query.m.length ).toEqual( 2 ); |
100 | 114 | expect( uri.query.m[0] ).toEqual( 'foo' ); |
101 | 115 | expect( uri.query.m[1] ).toEqual( 'bar' ); |
Index: trunk/phase3/resources/mediawiki/mediawiki.Uri.js |
— | — | @@ -89,7 +89,7 @@ |
90 | 90 | 'host', // www.test.com |
91 | 91 | 'port', // 81 |
92 | 92 | 'path', // /dir/dir.2/index.htm |
93 | | - 'query', // q1=0&&test1&test2=value (will become { q1: 0, test1: '', test2: 'value' } ) |
| 93 | + 'query', // q1=0&&test1&test2=value (will become { q1: '0', test1: '', test2: 'value' } ) |
94 | 94 | 'fragment' // top |
95 | 95 | ]; |
96 | 96 | |
— | — | @@ -103,14 +103,23 @@ |
104 | 104 | /** |
105 | 105 | * Constructs URI object. Throws error if arguments are illegal/impossible, or otherwise don't parse. |
106 | 106 | * @constructor |
107 | | - * @param {!Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone). Object must have non-blank 'protocol', 'host', and 'path' properties. |
108 | | - * @param {Boolean} strict mode (when parsing a string) |
| 107 | + * @param {Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone). |
| 108 | + * Object must have non-blank 'protocol', 'host', and 'path' properties. |
| 109 | + * @param {Object|Boolean} Object with options, or (backwards compatibility) a boolean for strictMode |
| 110 | + * - strictMode {Boolean} Trigger strict mode parsing of the url. Default: false |
| 111 | + * - overrideKeys {Boolean} Wether to let duplicate query parameters override eachother (true) or automagically |
| 112 | + * convert to an array (false, default). |
109 | 113 | */ |
110 | | - function Uri( uri, strictMode ) { |
111 | | - strictMode = !!strictMode; |
| 114 | + function Uri( uri, options ) { |
| 115 | + options = typeof options === 'object' ? options : { strictMode: !!options }; |
| 116 | + options = $.extend( { |
| 117 | + strictMode: false, |
| 118 | + overrideKeys: false |
| 119 | + }, options ); |
| 120 | + |
112 | 121 | if ( uri !== undefined && uri !== null || uri !== '' ) { |
113 | 122 | if ( typeof uri === 'string' ) { |
114 | | - this._parse( uri, strictMode ); |
| 123 | + this._parse( uri, options ); |
115 | 124 | } else if ( typeof uri === 'object' ) { |
116 | 125 | var _this = this; |
117 | 126 | $.each( properties, function( i, property ) { |
— | — | @@ -159,11 +168,11 @@ |
160 | 169 | /** |
161 | 170 | * Parse a string and set our properties accordingly. |
162 | 171 | * @param {String} URI |
163 | | - * @param {Boolean} strictness |
| 172 | + * @param {Object} options |
164 | 173 | * @return {Boolean} success |
165 | 174 | */ |
166 | | - _parse: function( str, strictMode ) { |
167 | | - var matches = parser[ strictMode ? 'strict' : 'loose' ].exec( str ); |
| 175 | + _parse: function( str, options ) { |
| 176 | + var matches = parser[ options.strictMode ? 'strict' : 'loose' ].exec( str ); |
168 | 177 | var uri = this; |
169 | 178 | $.each( properties, function( i, property ) { |
170 | 179 | uri[ property ] = matches[ i+1 ]; |
— | — | @@ -179,13 +188,22 @@ |
180 | 189 | if ( $1 ) { |
181 | 190 | var k = Uri.decode( $1 ); |
182 | 191 | var v = ( $2 === '' || $2 === undefined ) ? null : Uri.decode( $3 ); |
183 | | - if ( typeof q[ k ] === 'string' ) { |
184 | | - q[ k ] = [ q[ k ] ]; |
185 | | - } |
186 | | - if ( typeof q[ k ] === 'object' ) { |
187 | | - q[ k ].push( v ); |
| 192 | + |
| 193 | + // If overrideKeys, always (re)set top level value. |
| 194 | + // If not overrideKeys but this key wasn't set before, then we set it as well. |
| 195 | + if ( options.overrideKeys || q[ k ] === undefined ) { |
| 196 | + q[ k ] = v; |
| 197 | + |
| 198 | + // Use arrays if overrideKeys is false and key was already seen before |
188 | 199 | } else { |
189 | | - q[ k ] = v; |
| 200 | + // Once before, still a string, turn into an array |
| 201 | + if ( typeof q[ k ] === 'string' ) { |
| 202 | + q[ k ] = [ q[ k ] ]; |
| 203 | + } |
| 204 | + // Add to the array |
| 205 | + if ( $.isArray( q[ k ] ) ) { |
| 206 | + q[ k ].push( v ); |
| 207 | + } |
190 | 208 | } |
191 | 209 | } |
192 | 210 | } ); |