Blame | Letzte Änderung | Log anzeigen | RSS feed
(function ($) {'use strict';/*** We need an event when the elements are destroyed* because if an input is removed, we have to remove the* maxlength object associated (if any).* From:* http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom*/if (!$.event.special.destroyed) {$.event.special.destroyed = {remove: function (o) {if (o.handler) {o.handler();}}};}$.fn.extend({maxlength: function (options, callback) {var documentBody = $('body'),defaults = {showOnReady: false, // true to always show when indicator is readyalwaysShow: false, // if true the indicator it's always shown.threshold: 10, // Represents how many chars left are needed to show up the counterwarningClass: 'label label-success',limitReachedClass: 'label label-important label-danger',separator: ' / ',preText: '',postText: '',showMaxLength: true,placement: 'bottom',message: null, // an alternative way to provide the message textshowCharsTyped: true, // show the number of characters typed and not the number of characters remainingvalidate: 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.appendToParent: false, // append the indicator to the input field's parent instead of bodytwoCharLinebreak: true, // count linebreak as 2 characters to match IE/Chrome textarea validation. As well as DB storage.customMaxAttribute: null // null = use maxlength attribute and browser functionality, string = use specified attribute instead.// Form submit validation is handled on your own. when maxlength has been exceeded 'overmax' class added to element};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();if (options.twoCharLinebreak) {// Count all line breaks as 2 characterstext = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');} else {// Remove all double-character (\r\n) linebreaks, so they're counted only once.text = text.replace(new RegExp('\r?\n', 'g'), '\n');}var currentLength = 0;if (options.utf8) {currentLength = utf8Length(text);} else {currentLength = text.length;}return currentLength;}/*** Truncate the text of the specified input.** @param input* @param limit*/function truncateChars(input, maxlength) {var text = input.val();var newlines = 0;if (options.twoCharLinebreak) {text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');if (text.substr(text.length - 1) === '\n' && text.length % 2 === 1) {newlines = 1;}}input.val(text.substr(0, maxlength - newlines));}/*** 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(currentInput, indicator) {indicator.css({display: 'block'});currentInput.trigger('maxlength.shown');}/*** When called shows the indicator.** @param indicator*/function hideRemaining(currentInput, indicator) {indicator.css({display: 'none'});currentInput.trigger('maxlength.hidden');}/*** This function updates the value in the indicator** @param maxLengthThisInput* @param typedChars* @return String*/function updateMaxLengthHTML(currentInputText, maxLengthThisInput, typedChars) {var output = '';if (options.message) {if (typeof options.message === 'function') {output = options.message(currentInputText, maxLengthThisInput);} else {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) {if (maxLengthIndicator) {maxLengthIndicator.html(updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, (maxLengthCurrentInput - remaining)));if (remaining > 0) {if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) {showRemaining(currentInput, maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass));} else {hideRemaining(currentInput, maxLengthIndicator);}} else {showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass));}}if (options.customMaxAttribute) {// class to use for form validation on custom maxlength attributeif (remaining < 0) {currentInput.addClass('overmax');} else {currentInput.removeClass('overmax');}}}/*** 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);// Supports custom placement handlerif ($.type(options.placement) === 'function'){options.placement(currentInput, maxLengthIndicator, pos);return;}// Supports custom placement via css positional propertiesif ($.isPlainObject(options.placement)){placeWithCSS(options.placement, maxLengthIndicator);return;}var inputOuter = currentInput.outerWidth(),outerWidth = maxLengthIndicator.outerWidth(),actualWidth = maxLengthIndicator.width(),actualHeight = maxLengthIndicator.height();// get the right position if the indicator is appended to the input's parentif (options.appendToParent) {pos.top -= currentInput.parent().offset().top;pos.left -= currentInput.parent().offset().left;}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;// Some more options for placementscase 'bottom-right-inside':maxLengthIndicator.css({ top: pos.top + pos.height, left: pos.left + pos.width - outerWidth });break;case 'top-right-inside':maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left + inputOuter - outerWidth });break;case 'top-left-inside':maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left });break;case 'bottom-left-inside':maxLengthIndicator.css({ top: pos.top + currentInput.outerHeight(), left: pos.left });break;}}/*** This function places the maxLengthIndicator based on placement config object.** @param {object} placement* @param {$} maxLengthIndicator* @return null**/function placeWithCSS(placement, maxLengthIndicator) {if (!placement || !maxLengthIndicator){return;}var POSITION_KEYS = ['top','bottom','left','right','position'];var cssPos = {};// filter css properties to position$.each(POSITION_KEYS, function (i, key) {var val = options.placement[key];if (typeof val !== 'undefined'){cssPos[key] = val;}});maxLengthIndicator.css(cssPos);return;}/*** This function returns true if the indicator position needs to* be recalculated when the currentInput changes** @return {boolean}**/function isPlacementMutable() {return options.placement === 'bottom-right-inside' || options.placement === 'top-right-inside' || typeof options.placement === 'function' || (options.message && typeof options.message === 'function');}/*** This function retrieves the maximum length of currentInput** @param currentInput* @return {number}**/function getMaxLength(currentInput) {var max = currentInput.attr('maxlength');if (options.customMaxAttribute) {var custom = currentInput.attr(options.customMaxAttribute);if (!max || custom < max) {max = custom;}}if (!max) {max = currentInput.attr('size');}return max;}return this.each(function () {var currentInput = $(this),maxLengthCurrentInput,maxLengthIndicator;$(window).resize(function () {if (maxLengthIndicator) {place(currentInput, maxLengthIndicator);}});function firstInit() {var maxlengthContent = updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, '0');maxLengthCurrentInput = getMaxLength(currentInput);if (!maxLengthIndicator) {maxLengthIndicator = $('<span class="bootstrap-maxlength"></span>').css({display: 'none',position: 'absolute',whiteSpace: 'nowrap',zIndex: 1099}).html(maxlengthContent);}// 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());});}if (options.appendToParent) {currentInput.parent().append(maxLengthIndicator);currentInput.parent().css('position', 'relative');} else {documentBody.append(maxLengthIndicator);}var remaining = remainingChars(currentInput, getMaxLength(currentInput));manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);place(currentInput, maxLengthIndicator);}if (options.showOnReady) {currentInput.ready(function () {firstInit();});} else {currentInput.focus(function () {firstInit();});}currentInput.on('maxlength.reposition', function () {place(currentInput, maxLengthIndicator);});currentInput.on('destroyed', function () {if (maxLengthIndicator) {maxLengthIndicator.remove();}});currentInput.on('blur', function () {if (maxLengthIndicator && !options.showOnReady) {maxLengthIndicator.remove();}});currentInput.on('input', function () {var maxlength = getMaxLength(currentInput),remaining = remainingChars(currentInput, maxlength),output = true;if (options.validate && remaining < 0) {truncateChars(currentInput, maxlength);output = false;} else {manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);}if (isPlacementMutable()) {place(currentInput, maxLengthIndicator);}return output;});});}});}(jQuery));