Adding support for a callback to jquery.byteLimit
authorKrinkle <krinkle@users.mediawiki.org>
Mon, 8 Aug 2011 17:02:29 +0000 (17:02 +0000)
committerKrinkle <krinkle@users.mediawiki.org>
Mon, 8 Aug 2011 17:02:29 +0000 (17:02 +0000)
* 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
resources/jquery/jquery.byteLimit.js
resources/mediawiki/mediawiki.Title.js
tests/qunit/suites/resources/jquery/jquery.byteLimit.js

index 8216cde..4d3f0f3 100644 (file)
@@ -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
index c1d26fc..dd7242a 100644 (file)
@@ -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
                        {
                                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();
index 28e8188..1412eb8 100644 (file)
@@ -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 + '"' );
                }
index 6702f4a..e450e84 100644 (file)
@@ -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: $( '<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'
+});