* 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.)
* (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
( 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
{
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();
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;
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 + '"' );
}
// 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);
limit: 12,
expected: '1234567890' + '12'
});
+
+byteLimitTest({
+ description: 'Pass the limit and a callback as input filter',
+ $input: $( '<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: $( '<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'
+});