From c503a70ba3549d4048425643bc942d045e498656 Mon Sep 17 00:00:00 2001 From: Krinkle Date: Mon, 8 Aug 2011 17:02:29 +0000 Subject: [PATCH] Adding support for a callback to jquery.byteLimit * Fixes (bug 29455) Add support for a filter callback function in jQuery byteLimit plugin. * Adding unit tests for it * Changing if-statements in mw.Title's helper functions for regular expression matches. It should check wether the value is not null or undefined. Before it was just a plain if-statement which meant that an empty string would also return false, which made the new byteLimit's tests fail (mw.Title.getMain() expects _name to be a valid string when it does ucFirst() and substr() etc.) --- RELEASE-NOTES-1.19 | 1 + resources/jquery/jquery.byteLimit.js | 45 ++++++++++++++----- resources/mediawiki/mediawiki.Title.js | 10 ++--- .../resources/jquery/jquery.byteLimit.js | 45 +++++++++++++++++-- 4 files changed, 82 insertions(+), 19 deletions(-) diff --git a/RELEASE-NOTES-1.19 b/RELEASE-NOTES-1.19 index 8216cde0e0..4d3f0f3cd9 100644 --- a/RELEASE-NOTES-1.19 +++ b/RELEASE-NOTES-1.19 @@ -27,6 +27,7 @@ production. * (bug 15558) Parameters to special pages included in wikitext can now be passed as with templates. * Installer now issues a warning if mod_security is present. +* (bug 29455) Add support for a filter callback function in jQuery byteLimit plugin. === Bug fixes in 1.19 === * $wgUploadNavigationUrl should be used for file redlinks if diff --git a/resources/jquery/jquery.byteLimit.js b/resources/jquery/jquery.byteLimit.js index c1d26fc48d..dd7242aafd 100644 --- a/resources/jquery/jquery.byteLimit.js +++ b/resources/jquery/jquery.byteLimit.js @@ -6,24 +6,46 @@ ( function( $ ) { /** - * Enforces a byte limit to a textbox, so that UTF-8 entries are not arbitrarily truncated. + * Enforces a byte limit to a textbox, so that UTF-8 entries are counted as well, when, for example, + * a databae field has a byte limit rather than a character limit. + * Plugin rationale: Browser has native maxlength for number of characters, this plugin exists to + * limit number of bytes instead. + * + * Can be called with a custom limit (to use that limit instead of the maxlength attribute value), + * a filter function (in case the limit should apply to something other than the exact input value), + * or both. Order of arguments is important! + * + * @context {jQuery} Instance of jQuery for one or more input elements + * @param limit {Number} (optional) Limit to enforce, fallsback to maxLength-attribute, + * called with fetched value as argument. + * @param fn {Function} (optional) Function to call on the input string before assessing the length + * @return {jQuery} The context */ - $.fn.byteLimit = function( limit ) { + $.fn.byteLimit = function( limit, fn ) { + // If the first argument is the function, + // set fn to the first argument's value and ignore the second argument. + if ( $.isFunction( limit ) ) { + fn = limit; + limit = undefined; + } - // Default to current attribute value - if ( limit == null ) { + // Default limit to current attribute value + if ( limit === undefined ) { limit = this.attr( 'maxLength' ); - // If passed, update/set attribute value instead + // If limit passed, update/set attribute value instead } else { this.attr( 'maxLength', limit ); } // Nothing passed and/or empty attribute, return without binding an event. - if ( limit == null ) { + if ( limit === undefined ) { return this; } + // Save function for reference + this.data( 'byteLimit-callback', fn ); + // We've got something, go for it: return this.keypress( function( e ) { // First check to see if this is actually a character key @@ -41,11 +63,12 @@ { return true; //a special key (backspace, etc) so don't interfere. } - - var len = $.byteLength( this.value ); - // Note that keypress returns a character code point, not a keycode. - // However, this may not be super reliable depending on how keys come in... - var charLen = $.byteLength( String.fromCharCode( e.which ) ); + + var val = fn !== undefined ? fn( $( this ).val() ): $( this ).val(), + len = $.byteLength( val ), + // Note that keypress returns a character code point, not a keycode. + // However, this may not be super reliable depending on how keys come in... + charLen = $.byteLength( String.fromCharCode( e.which ) ); if ( ( len + charLen ) > limit ) { e.preventDefault(); diff --git a/resources/mediawiki/mediawiki.Title.js b/resources/mediawiki/mediawiki.Title.js index 28e8188f0a..1412eb8fd7 100644 --- a/resources/mediawiki/mediawiki.Title.js +++ b/resources/mediawiki/mediawiki.Title.js @@ -123,9 +123,9 @@ var Title = function( title, namespace ) { var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ), ns_match = getNsIdByName( matches[1] ); if ( matches.length && ns_match ) { - if ( matches[1] ) { title._ns = ns_match; } - if ( matches[2] ) { title._name = fixName( matches[2] ); } - if ( matches[3] ) { title._ext = fixExt( matches[3] ); } + if ( matches[1] != null ) { title._ns = ns_match; } + if ( matches[2] != null ) { title._name = fixName( matches[2] ); } + if ( matches[3] != null ) { title._ext = fixExt( matches[3] ); } } else { // Consistency with MediaWiki: Unknown namespace > fallback to main namespace. title._ns = 0; @@ -144,8 +144,8 @@ var Title = function( title, namespace ) { setNameAndExtension = function( title, raw ) { var matches = raw.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ ); if ( matches.length ) { - if ( matches[1] ) { title._name = fixName( matches[1] ); } - if ( matches[2] ) { title._ext = fixExt( matches[2] ); } + if ( matches[1] != null ) { title._name = fixName( matches[1] ); } + if ( matches[2] != null ) { title._ext = fixExt( matches[2] ); } } else { throw new Error( 'mw.Title: Could not parse title "' + raw + '"' ); } diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js index 6702f4a778..e450e844e3 100644 --- a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js +++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js @@ -48,14 +48,16 @@ var byteLimitTest = function( options ) { // Simulate pressing keys for each of the sample characters $.addChars( opt.$input, opt.sample ); - var newVal = opt.$input.val(); + var rawVal = opt.$input.val(), + fn = opt.$input.data( 'byteLimit-callback' ), + newVal = $.isFunction( fn ) ? fn( rawVal ) : rawVal; if ( opt.hasLimit ) { expect(3); ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' ); - equal( $.byteLength( newVal ), $.byteLength( opt.expected ), 'Not preventing keypresses too early, length has reached the expected length' ); - equal( newVal, opt.expected, 'New value matches the expected string' ); + equal( $.byteLength( rawVal ), $.byteLength( opt.expected ), 'Not preventing keypresses too early, length has reached the expected length' ); + equal( rawVal, opt.expected, 'New value matches the expected string' ); } else { expect(2); @@ -154,3 +156,40 @@ byteLimitTest({ limit: 12, expected: '1234567890' + '12' }); + +byteLimitTest({ + description: 'Pass the limit and a callback as input filter', + $input: $( '' ) + .attr( { + 'type': 'text' + }) + .byteLimit( 6, function( val ) { + _titleConfig(); + + // Return without namespace prefix + return new mw.Title( '' + val ).getMain(); + } ), + sample: 'User:Sample', + hasLimit: true, + limit: 6, // 'Sample' length + expected: 'User:Sample' +}); + +byteLimitTest({ + description: 'Limit using the maxlength attribute and pass a callback as input filter', + $input: $( '' ) + .attr( { + 'type': 'text', + 'maxLength': '6' + }) + .byteLimit( function( val ) { + _titleConfig(); + + // Return without namespace prefix + return new mw.Title( '' + val ).getMain(); + } ), + sample: 'User:Sample', + hasLimit: true, + limit: 6, // 'Sample' length + expected: 'User:Sample' +}); -- 2.20.1