1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
* Licensed under the MIT License (LICENSE.txt).
*
* Version 1.0
*
* Contributions by:
* - Neil Monroe (neil.monroe[at]gmail.com)
*/
(function($) {
$.fn.extend({
countable: function(givenOptions) {
return this.each(function() {
var $this = $(this), interval, prev_char_diff, $el,
options = $.extend({
threshold: .5,
appendMethod: 'insertAfter', // insertBefore || insertAfter || prependTo || appendTo
target: $this, // relative element with which to place the counter
startOpacity: .25,
maxLength: parseInt( $this.attr('maxlength'), 10 ) || 0,
maxClassName: 'maxed',
className: 'counter',
tagName: 'span',
interval: 750,
positiveCopy: 'You have {n} characters left.',
negativeCopy: 'You are {n} characters over.',
fadeDuration: 'normal',
defaultText: '' // text to disregard in the character count
}, givenOptions);
// create counter element
$el = $('<'+options.tagName+'/>')
.html( options.positiveCopy.replace('{n}', '<span class="num"></span>') )
.addClass( options.className );
// set initial opacity to 0 if opacity is supported
if ( $.support.opacity ) $el.css({ opacity: 0 }); // don't set opacity for IE to avoid clear text issues.
// sppend counter element to the DOM
$el[options.appendMethod](options.target);
// hook up events for the input/textarea being monitored
$this
.bind('keyup', check)
.bind('focus blur', function(event) {
if ( event.type == 'blur' ) clearInterval( interval );
if ( event.type == 'focus' && !interval ) setInterval(check, options.interval);
});
// actual function to do the character counting and notification
function check() {
var val = $this.val(),
length = (val == options.defaultText ? 0 : val.length),
percentage_complete = length/options.maxLength,
char_diff = options.maxLength - length,
opacity;
// return if we haven't made any progress
if ( prev_char_diff != undefined && char_diff == prev_char_diff ) return;
// if counter element is hidden and we are past the given threshold, show it
if ( $el.is(':hidden') && percentage_complete >= options.threshold )
$el.show();
// if the counter element is visible and we are now under the given threshold, hide it
if ( $el.is(':visible') && percentage_complete < options.threshold )
$el.hide();
if ( $.support.opacity ) { // don't set opacity for IE to avoid clear type issues.
// calculate the correct opacity
opacity = options.startOpacity + ((options.threshold - percentage_complete) * ((options.startOpacity * 2) - 2));
// animate to the correct opacity
$el.stop().fadeTo( options.fadeDuration, percentage_complete >= options.threshold ? opacity : 0 );
}
// set the correct copy if under or over the max number of characters
if ( char_diff >= 0 ) {
if ( $el.is( '.'+options.maxClassName ) )
$el.html( options.positiveCopy.replace('{n}', '<span class="num"></span>') );
} else {
if ( !$el.is( '.'+options.maxClassName ) )
$el.html( options.negativeCopy.replace('{n}', '<span class="num"></span>') );
}
// add or remove the max class name
$el[ (char_diff < 0 ? 'add' : 'remove') + 'Class' ]( options.maxClassName );
// set the number of characters left or number of characters over the limit
$el.find('.num').text( Math.abs(char_diff) );
// make sure the plural is necessary or not
if ( char_diff == -1 || char_diff == 1 )
$el.html( $el.html().replace(/characters\b/, 'character') );
else
$el.html( $el.html().replace(/character\b/, 'characters') );
prev_char_diff = char_diff;
};
// run an initial check
check();
});
}
});
})(jQuery);
|