From 0867987577a386a0e0d47ed48bc496c2a11daaef Mon Sep 17 00:00:00 2001 From: Krinkle Date: Thu, 10 Nov 2011 02:08:47 +0000 Subject: [PATCH] [mediawiki.Uri] Add overrideKeys option * The behavior of turning foo=bar&foo=quux&lorem=ipsum into "{ lorem: 'ipsum', foo: ['bar', 'quux'] }", has annoyed me several times. As it is in contrary with the way PHP works (PHP only makes numeral arrays if the key ends in '[]'). Adding in an option to make mw.Uri behave like that. * We may wanna make that the default at some point, leaving default behavior unchanged for now. * Converted 'strictMode' into 'options', kept backwards compatibility * Fixed documentation comment for example (q1=0 -> "{ q1: '0'") * Updated unit tests to account for this option. --- resources/mediawiki/mediawiki.Uri.js | 48 ++++++++++++++++-------- tests/jasmine/spec/mediawiki.Uri.spec.js | 18 ++++++++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/resources/mediawiki/mediawiki.Uri.js b/resources/mediawiki/mediawiki.Uri.js index 16a724aa47..21d70cc3d8 100644 --- a/resources/mediawiki/mediawiki.Uri.js +++ b/resources/mediawiki/mediawiki.Uri.js @@ -89,7 +89,7 @@ 'host', // www.test.com 'port', // 81 'path', // /dir/dir.2/index.htm - 'query', // q1=0&&test1&test2=value (will become { q1: 0, test1: '', test2: 'value' } ) + 'query', // q1=0&&test1&test2=value (will become { q1: '0', test1: '', test2: 'value' } ) 'fragment' // top ]; @@ -103,14 +103,23 @@ /** * Constructs URI object. Throws error if arguments are illegal/impossible, or otherwise don't parse. * @constructor - * @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. - * @param {Boolean} strict mode (when parsing a string) + * @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. + * @param {Object|Boolean} Object with options, or (backwards compatibility) a boolean for strictMode + * - strictMode {Boolean} Trigger strict mode parsing of the url. Default: false + * - overrideKeys {Boolean} Wether to let duplicate query parameters override eachother (true) or automagically + * convert to an array (false, default). */ - function Uri( uri, strictMode ) { - strictMode = !!strictMode; + function Uri( uri, options ) { + options = typeof options === 'object' ? options : { strictMode: !!options }; + options = $.extend( { + strictMode: false, + overrideKeys: false + }, options ); + if ( uri !== undefined && uri !== null || uri !== '' ) { if ( typeof uri === 'string' ) { - this._parse( uri, strictMode ); + this._parse( uri, options ); } else if ( typeof uri === 'object' ) { var _this = this; $.each( properties, function( i, property ) { @@ -159,11 +168,11 @@ /** * Parse a string and set our properties accordingly. * @param {String} URI - * @param {Boolean} strictness + * @param {Object} options * @return {Boolean} success */ - _parse: function( str, strictMode ) { - var matches = parser[ strictMode ? 'strict' : 'loose' ].exec( str ); + _parse: function( str, options ) { + var matches = parser[ options.strictMode ? 'strict' : 'loose' ].exec( str ); var uri = this; $.each( properties, function( i, property ) { uri[ property ] = matches[ i+1 ]; @@ -179,13 +188,22 @@ if ( $1 ) { var k = Uri.decode( $1 ); var v = ( $2 === '' || $2 === undefined ) ? null : Uri.decode( $3 ); - if ( typeof q[ k ] === 'string' ) { - q[ k ] = [ q[ k ] ]; - } - if ( typeof q[ k ] === 'object' ) { - q[ k ].push( v ); - } else { + + // If overrideKeys, always (re)set top level value. + // If not overrideKeys but this key wasn't set before, then we set it as well. + if ( options.overrideKeys || q[ k ] === undefined ) { q[ k ] = v; + + // Use arrays if overrideKeys is false and key was already seen before + } else { + // Once before, still a string, turn into an array + if ( typeof q[ k ] === 'string' ) { + q[ k ] = [ q[ k ] ]; + } + // Add to the array + if ( $.isArray( q[ k ] ) ) { + q[ k ].push( v ); + } } } } ); diff --git a/tests/jasmine/spec/mediawiki.Uri.spec.js b/tests/jasmine/spec/mediawiki.Uri.spec.js index c8df13f64e..e9bdd299ce 100644 --- a/tests/jasmine/spec/mediawiki.Uri.spec.js +++ b/tests/jasmine/spec/mediawiki.Uri.spec.js @@ -92,8 +92,22 @@ expect( uri.getQueryString() ).toEqual( 'q=uri' ); } ); - describe( "should handle multiple value query args", function() { - var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1' ); + describe( "should handle multiple value query args (overrideKeys on)", function() { + var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1', { overrideKeys: true } ); + it ( "should parse with multiple values", function() { + expect( uri.query.m ).toEqual( 'bar' ); + expect( uri.query.n ).toEqual( '1' ); + } ); + it ( "should accept multiple values", function() { + uri.query.n = [ "x", "y", "z" ]; + expect( uri.toString() ).toContain( 'm=bar' ); + expect( uri.toString() ).toContain( 'n=x&n=y&n=z' ); + expect( uri.toString().length ).toEqual( 'http://www.sample.com/dir/?m=bar&n=x&n=y&n=z'.length ); + } ); + } ); + + describe( "should handle multiple value query args (overrideKeys off)", function() { + var uri = new mw.Uri( 'http://www.sample.com/dir/?m=foo&m=bar&n=1', { overrideKeys: false } ); it ( "should parse with multiple values", function() { expect( uri.query.m.length ).toEqual( 2 ); expect( uri.query.m[0] ).toEqual( 'foo' ); -- 2.20.1