forked from craigmccoy/jquery-charcount
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jquery.charcount.js
160 lines (142 loc) · 5.58 KB
/
jquery.charcount.js
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
(function($) {
var MAXLENGTH_MIN = 1;
var POSITION_BEFORE = 'before';
var POSITION_AFTER = 'after';
var POSITION_NONE = 'none';
/**
* Returns filtered options map from a user supplied map
* @param options
* @return Object
*/
var filterSettings_ = function(options) {
if(!$.isPlainObject(options)) { //must be an object
return {};
}
$.each(options, function(key, value) {
if(key == 'maxLength') { //allows only integers greater than zero
options.maxLength = parseInt(value);
if(isNaN(options.maxLength)) {
delete options.maxLength;
} else if(options.maxLength < MAXLENGTH_MIN) {
options.maxLength = MAXLENGTH_MIN;
} else if(options.maxLength > $.charcount.config.max) {
options.maxLength = $.charcount.config.max;
}
} else if(key == 'position') { //allows only the defined positions (before, after, none)
options.position = $.trim(value.toString().toLowerCase());
if($.inArray(options.position, [POSITION_BEFORE, POSITION_AFTER, POSITION_NONE]) < 0) {
delete options.position;
}
} else if(key == 'preventOverage') { //boolean value of whatever is provided
options.preventOverage = Boolean(value);
} else if(key == 'classPrefix') { //any string, lower case, no leading/trailing whitespace
options.classPrefix = $.trim(value.toString().toLowerCase());
} else { //removes unused data provided
delete options[key];
}
});
return options;
};
/**
* Custom selector to choose which form field types can use this plugin
* @param obj
* @return Boolean
*/
$.expr[':'].charcountable = function(obj) {
return $(obj).is('textarea,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="search"]');
};
/**
* Convenience function for setting/getting of the 'maxlength' attribute
* @return jQuery|int
*/
$.fn.maxlength = function(/*value*/) {
var element = $(this);
if(arguments.length > 0) {
var value = parseInt(arguments[0]);
if(!isNaN(value)) {
element.attr('maxlength', value);
}
return element;
} else {
var maxlength = parseInt(element.attr('maxlength'));
return (maxlength < MAXLENGTH_MIN || maxlength > $.charcount.config.max || isNaN(maxlength)) ? undefined : maxlength;
}
};
/**
* Configuration and default settings
*/
$.charcount = {
util: {
//returns pluralized form of a string based on a count
pluralize: function(count, singular/*, plural*/) {
var plural = (arguments.length > 2) ? arguments[2] : singular + 's';
return (count == 1) ? singular : plural;
}
},
config: {
//maximum allowed value for $.fn.maxlength and maxLength setting
max: 4000
},
defaults: {
//maximum length of string length of value of element (TODO: word count support?)
maxLength: 250,
//where to position the count display element (before, after or none)
position: POSITION_BEFORE,
//should the element still not display characters if over the set maxlength
preventOverage: true,
//prefix for used css classes
classPrefix: 'charcount'
}
};
/**
* Initializes the plugin using the options (if any) provided by the user
* @param options (optional)
* @return jQuery
*/
$.fn.charcount = function(/*options*/) {
var options = arguments.length > 0 ? filterSettings_(arguments[0]) : {};
var globalSettings = $.extend({}, $.charcount.defaults, options);
return this.each(function(index) {
var element = $(this);
if(element.is(':charcountable')) {
//settings from element attributes
var elementSettings = { maxLength: element.maxlength() };
if(elementSettings.maxLength == undefined) {
delete elementSettings.maxLength;
}
var settings = $.extend({}, globalSettings, filterSettings_(elementSettings));
element.removeAttr('maxlength');
if(settings.preventOverage) { //in HTML5, 'maxlength' attribute is allowed on textareas
element.maxlength(settings.maxLength);
}
if(settings.position != POSITION_NONE) {
element.wrap('<span />').addClass(settings.classPrefix);
var countDisplay = $('<span />').addClass(settings.classPrefix + '-display');
if(settings.position == POSITION_BEFORE) {
countDisplay.insertBefore(element).addClass(settings.classPrefix + '-position-' + POSITION_BEFORE);
} else if(settings.position == POSITION_AFTER) {
countDisplay.insertAfter(element).addClass(settings.classPrefix + '-position-' + POSITION_AFTER);
}
}
element.bind('keyup keypress charcount', function(evt) {
var length = $(evt.target).val().length;
var remaining = settings.maxLength - length;
if(settings.preventOverage && remaining < 1) {
evt.preventDefault();
element.val(element.val().substr(0, settings.maxLength)); //trims text for elements that do not natively limit input
}
if(evt.type == 'keyup' || evt.type == 'charcount') {
element.trigger('update', [length, remaining]);
if(settings.position != POSITION_NONE) {
var display = element.parent().find('.' + settings.classPrefix + '-display').removeClass(settings.classPrefix + '-overage');
if(remaining < 0) {
display.addClass(settings.classPrefix + '-overage');
}
display.text(remaining >= 0 ? remaining + ' ' + $.charcount.util.pluralize(remaining, 'character') + ' remaining' : Math.abs(remaining) + ' too many characters');
}
}
}).trigger('charcount');
}
});
};
})(jQuery);