/* ==========================================================
|
* bootstrap-maxlength.js v1.4.2
|
*
|
* Copyright (c) 2013 Maurizio Napoleoni;
|
*
|
* Licensed under the terms of the MIT license.
|
* See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE
|
* ========================================================== */
|
/*jslint browser:true*/
|
/*global jQuery*/
|
(function ($) {
|
"use strict";
|
|
$.fn.extend({
|
maxlength: function (options, callback) {
|
|
var documentBody = $('body'),
|
defaults = {
|
alwaysShow: false, // if true the indicator it's always shown.
|
threshold: 10, // Represents how many chars left are needed to show up the counter
|
warningClass: "label label-success",
|
limitReachedClass: "label label-important",
|
separator: ' / ',
|
preText: '',
|
postText: '',
|
showMaxLength : true,
|
placement: 'bottom',
|
showCharsTyped: true, // show the number of characters typed and not the number of characters remaining
|
validate: false, // if the browser doesn't support the maxlength attribute, attempt to type more than
|
// the indicated chars, will be prevented.
|
utf8: false // counts using bytesize rather than length. eg: '£' is counted as 2 characters.
|
};
|
|
if ($.isFunction(options) && !callback) {
|
callback = options;
|
options = {};
|
}
|
options = $.extend(defaults, options);
|
|
/**
|
* Return the length of the specified input.
|
*
|
* @param input
|
* @return {number}
|
*/
|
function inputLength(input) {
|
var text = input.val();
|
var matches = text.match(/\n/g);
|
|
var breaks = 0;
|
var inputLength = 0;
|
|
if (options.utf8) {
|
breaks = matches ? getUTF8Length(matches) : 0;
|
inputLength = getUTF8Length(input.val()) + breaks;
|
} else {
|
breaks = matches ? matches.length : 0;
|
inputLength = input.val().length + breaks;
|
}
|
return inputLength;
|
}
|
|
/**
|
* Return the length of the specified input in UTF8 encoding.
|
*
|
* @param input
|
* @return {number}
|
*/
|
function utf8Length(string) {
|
var utf8length = 0;
|
for (var n = 0; n < string.length; n++) {
|
var c = string.charCodeAt(n);
|
if (c < 128) {
|
utf8length++;
|
}
|
else if((c > 127) && (c < 2048)) {
|
utf8length = utf8length+2;
|
}
|
else {
|
utf8length = utf8length+3;
|
}
|
}
|
return utf8length;
|
}
|
|
/**
|
* Return true if the indicator should be showing up.
|
*
|
* @param input
|
* @param thereshold
|
* @param maxlength
|
* @return {number}
|
*/
|
function charsLeftThreshold(input, thereshold, maxlength) {
|
var output = true;
|
if (!options.alwaysShow && (maxlength - inputLength(input) > thereshold)) {
|
output = false;
|
}
|
return output;
|
}
|
|
/**
|
* Returns how many chars are left to complete the fill up of the form.
|
*
|
* @param input
|
* @param maxlength
|
* @return {number}
|
*/
|
function remainingChars(input, maxlength) {
|
var length = maxlength - inputLength(input);
|
return length;
|
}
|
|
/**
|
* When called displays the indicator.
|
*
|
* @param indicator
|
*/
|
function showRemaining(indicator) {
|
indicator.css({
|
display: 'block'
|
});
|
}
|
|
/**
|
* When called shows the indicator.
|
*
|
* @param indicator
|
*/
|
function hideRemaining(indicator) {
|
indicator.css({
|
display: 'none'
|
});
|
}
|
|
/**
|
* This function updates the value in the indicator
|
*
|
* @param maxLengthThisInput
|
* @param typedChars
|
* @return String
|
*/
|
function updateMaxLengthHTML(maxLengthThisInput, typedChars) {
|
var output = '';
|
if (options.message){
|
output = options.message.replace('%charsTyped%', typedChars)
|
.replace('%charsRemaining%', maxLengthThisInput - typedChars)
|
.replace('%charsTotal%', maxLengthThisInput);
|
} else {
|
if (options.preText) {
|
output += options.preText;
|
}
|
if (!options.showCharsTyped) {
|
output += maxLengthThisInput - typedChars;
|
}
|
else {
|
output += typedChars;
|
}
|
if (options.showMaxLength) {
|
output += options.separator + maxLengthThisInput;
|
}
|
if (options.postText) {
|
output += options.postText;
|
}
|
}
|
return output;
|
}
|
|
/**
|
* This function updates the value of the counter in the indicator.
|
* Wants as parameters: the number of remaining chars, the element currently managed,
|
* the maxLength for the current input and the indicator generated for it.
|
*
|
* @param remaining
|
* @param currentInput
|
* @param maxLengthCurrentInput
|
* @param maxLengthIndicator
|
*/
|
function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) {
|
maxLengthIndicator.html(updateMaxLengthHTML(maxLengthCurrentInput, (maxLengthCurrentInput - remaining)));
|
|
if (remaining > 0) {
|
if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) {
|
showRemaining(maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass));
|
} else {
|
hideRemaining(maxLengthIndicator);
|
}
|
} else {
|
showRemaining(maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass));
|
}
|
}
|
|
/**
|
* This function returns an object containing all the
|
* informations about the position of the current input
|
*
|
* @param currentInput
|
* @return object {bottom height left right top width}
|
*
|
*/
|
function getPosition(currentInput) {
|
var el = currentInput[0];
|
return $.extend({}, (typeof el.getBoundingClientRect === 'function') ? el.getBoundingClientRect() : {
|
width: el.offsetWidth,
|
height: el.offsetHeight
|
}, currentInput.offset());
|
}
|
|
/**
|
* This function places the maxLengthIndicator at the
|
* top / bottom / left / right of the currentInput
|
*
|
* @param currentInput
|
* @param maxLengthIndicator
|
* @return null
|
*
|
*/
|
function place(currentInput, maxLengthIndicator) {
|
var pos = getPosition(currentInput),
|
inputOuter = currentInput.outerWidth(),
|
outerWidth = maxLengthIndicator.outerWidth(),
|
actualWidth = maxLengthIndicator.width(),
|
actualHeight = maxLengthIndicator.height();
|
|
switch (options.placement) {
|
case 'bottom':
|
maxLengthIndicator.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2});
|
break;
|
case 'top':
|
maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2});
|
break;
|
case 'left':
|
maxLengthIndicator.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth});
|
break;
|
case 'right':
|
maxLengthIndicator.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width});
|
break;
|
case 'bottom-right':
|
maxLengthIndicator.css({top: pos.top + pos.height, left: pos.left + pos.width});
|
break;
|
case 'top-right':
|
maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left + inputOuter});
|
break;
|
case 'top-left':
|
maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left - outerWidth});
|
break;
|
case 'bottom-left':
|
maxLengthIndicator.css({top: pos.top + currentInput.outerHeight(), left: pos.left - outerWidth});
|
break;
|
case 'centered-right':
|
maxLengthIndicator.css({top: pos.top + (actualHeight / 2), left: pos.left + inputOuter - outerWidth - 3});
|
break;
|
}
|
}
|
|
/**
|
* This function retrieves the maximum length of currentInput
|
*
|
* @param currentInput
|
* @return {number}
|
*
|
*/
|
function getMaxLength(currentInput) {
|
return currentInput.attr('maxlength') || currentInput.attr('size');
|
}
|
|
return this.each(function() {
|
|
var currentInput = $(this),
|
maxLengthCurrentInput = getMaxLength(currentInput),
|
maxLengthIndicator = $('<span></span>').css({
|
display: 'none',
|
position: 'absolute',
|
whiteSpace: 'nowrap',
|
zIndex: 1099
|
}).html(updateMaxLengthHTML(maxLengthCurrentInput, '0'));
|
|
// We need to detect resizes if we are dealing with a textarea:
|
if (currentInput.is('textarea')) {
|
currentInput.data('maxlenghtsizex', currentInput.outerWidth());
|
currentInput.data('maxlenghtsizey', currentInput.outerHeight());
|
|
currentInput.mouseup(function() {
|
if (currentInput.outerWidth() !== currentInput.data('maxlenghtsizex') || currentInput.outerHeight() !== currentInput.data('maxlenghtsizey')) {
|
place(currentInput, maxLengthIndicator);
|
}
|
|
currentInput.data('maxlenghtsizex', currentInput.outerWidth());
|
currentInput.data('maxlenghtsizey', currentInput.outerHeight());
|
});
|
}
|
|
documentBody.append(maxLengthIndicator);
|
|
currentInput.focus(function() {
|
var remaining = remainingChars(currentInput, getMaxLength(currentInput));
|
manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
|
place(currentInput, maxLengthIndicator);
|
});
|
|
$(window).resize(function() {
|
place(currentInput, maxLengthIndicator);
|
});
|
|
currentInput.blur(function() {
|
maxLengthIndicator.css('display', 'none');
|
});
|
|
currentInput.keyup(function(e) {
|
var remaining = remainingChars(currentInput, getMaxLength(currentInput)),
|
output = true,
|
keyCode = e.keyCode || e.which;
|
// Handle the tab press when the maxlength have been reached.
|
if (remaining===0 && keyCode===9 && !e.shiftKey) {
|
currentInput.attr('maxlength',getMaxLength(currentInput)+1)
|
.trigger({
|
type: 'keypress',
|
which: 9
|
}).attr('maxlength',getMaxLength(currentInput)-1);
|
|
}
|
if (options.validate && remaining < 0) {
|
output = false;
|
} else {
|
manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
|
}
|
return output;
|
});
|
});
|
}
|
});
|
}(jQuery));
|