Merge "(bug 31895) Fix bug in mediawiki.js with early domready handlers."
[lhc/web/wiklou.git] / resources / jquery / jquery.byteLimit.js
1 /**
2 * jQuery byteLimit plugin
3 *
4 * @author Jan Paul Posma, 2011
5 * @author Timo Tijhof, 2011-2012
6 */
7 ( function ( $, undefined ) {
8
9 /**
10 * Enforces a byte limit on an input field, so that UTF-8 entries are counted
11 * as well, when, for example, a database field has a byte limit rather than
12 * a character limit.
13 * Plugin rationale: Browser has native maxlength for number of characters,
14 * this plugin exists to limit number of bytes instead.
15 *
16 * Can be called with a custom limit (to use that limit instead of the
17 * maxlength attribute value), a filter function (in case the limit should
18 * apply to something other than the exact input value), or both.
19 * Order of parameters is important!
20 *
21 * @context {jQuery} Instance of jQuery for one or more input elements.
22 * @param limit {Number} [optional] Limit to enforce, fallsback to
23 * maxLength-attribute, called with fetched value as argument.
24 * @param fn {Function} [optional] Function to call on the input string
25 * before assessing the length.
26 * @return {jQuery} The context.
27 */
28 $.fn.byteLimit = function ( limit, fn ) {
29 // If the first argument is the function,
30 // set fn to the first argument's value and ignore the second argument.
31 if ( $.isFunction( limit ) ) {
32 fn = limit;
33 limit = undefined;
34 }
35
36 // The following is specific to each element in the collection
37 return this.each( function ( i, el ) {
38 var $el, elLimit;
39
40 $el = $( el );
41
42 // Default limit to current attribute value
43 // Can't re-use 'limit' variable because it's in the higher scope
44 // that affects the next each() iteration as well.
45 elLimit = limit === undefined ? $el.prop( 'maxLength' ) : limit;
46
47 // If there is no (valid) limit passed or found in the property,
48 // skip this. The < 0 check is required for Firefox, which returns
49 // -1 (instead of undefined) for maxLength if it is not set.
50 if ( !elLimit || elLimit < 0 ) {
51 return;
52 }
53
54 // If there's a callback set, it's possible that the limit being enforced
55 // is lower or higher (ie. if the callback would return "Foo" for "User:Foo").
56 // Usually this isn't a problem since browsers ignore maxLength when setting
57 // the value property through JavaScript, but Safari 4 violates that rule,
58 // and makes sense to generally make sure the native browser limit doesn't
59 // interfere
60 $el.removeProp( 'maxLength' );
61
62 // Save function for reference
63 $el.data( 'byteLimitCallback', fn );
64
65 // Using keyup instead of keypress so that we don't have to account
66 // for the infinite number of methods for character insertion (typing,
67 // holding down for multiple characters, special characters inserted
68 // with shift/alt, backspace, drag/drop, cut/copy/paste, selecting
69 // text and replacing).
70 // Also using onchange. Usually only triggered when field loses focus,
71 // (incl. before submission), which seems redundant. But this is used
72 // to allow other JavaScript libraries (e.g. for custom input methods of
73 // special characters) which tend to trigger onchange as convienience for
74 // plugins like these.
75 $el.on( 'keyup change', function ( e ) {
76 var len,
77 $el = $( this ),
78 curVal = $el.val(),
79 val = curVal;
80
81 // Run any value modifier (e.g. a function to apply the limit to
82 // "Foo" in value "User:Foo").
83 while ( $.byteLength( fn ? fn( val ) : val ) > elLimit ) {
84 val = val.substr( 0, val.length - 1 );
85 };
86
87 if ( val !== curVal ) {
88 $el.val( val );
89 }
90 });
91 });
92 };
93 }( jQuery ) );