PageSourceSearch

https://www.havelberg.de/res/js/jquery/bootstrap-maxlength/src/bootstrap-maxlength.js?v=v5.52.00

js havelberg.de collected 2026-10-02 15:00:55 UTC 17,928 bytes, 537 lines download raw bytes

1(function ($) {
2  'use strict';
3  /**
4   * We need an event when the elements are destroyed
5   * because if an input is removed, we have to remove the
6   * maxlength object associated (if any).
7   * From:
8   * http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
9   */
10  if (!$.event.special.destroyed) {
11    $.event.special.destroyed = {
12      remove: function (o) {
13        if (o.handler) {
14          o.handler();
15        }
16      }
17    };
18  }
19
20
21  $.fn.extend({
22    maxlength: function (options, callback) {
23      var documentBody = $('body'),
24        defaults = {
25          showOnReady: false, // true to always show when indicator is ready
26          alwaysShow: false, // if true the indicator it's always shown.
27          threshold: 10, // Represents how many chars left are needed to show up the counter
28          warningClass: 'label label-success',
29          limitReachedClass: 'label label-important label-danger',
30          separator: ' / ',
31          preText: '',
32          postText: '',
33          showMaxLength: true,
34          placement: 'bottom',
35          message: null, // an alternative way to provide the message text
36          showCharsTyped: true, // show the number of characters typed and not the number of characters remaining
37          validate: false, // if the browser doesn't support the maxlength attribute, attempt to type more than
38          // the indicated chars, will be prevented.
39          utf8: false, // counts using bytesize rather than length. eg: '£' is counted as 2 characters.
40          appendToParent: false, // append the indicator to the input field's parent instead of body
41          twoCharLinebreak: true,  // count linebreak as 2 characters to match IE/Chrome textarea validation. As well as DB storage.
42          customMaxAttribute: null,  // null = use maxlength attribute and browser functionality, string = use specified attribute instead.
43          allowOverMax: false
44          // Form submit validation is handled on your own.  when maxlength has been exceeded 'overmax' class added to element
45        };
46
47      if ($.isFunction(options) && !callback) {
48        callback = options;
49        options = {};
50      }
51      options = $.extend(defaults, options);
52
53
54      /**
55      * Return the byte count of the specified character in UTF8 encoding.
56      * Note: This won't cover UTF-8 characters that are 4 bytes long.
57      *
58      * @param input
59      * @return {number}
60      */
61      function utf8CharByteCount(character) {
62        var c = character.charCodeAt();
63        // Not c then 0, else c < 128 then 1, else c < 2048 then 2, else 3
64        return !c ? 0 : c < 128 ? 1 : c < 2048 ? 2 : 3;
65      }
66
67      /**
68      * Return the length of the specified input in UTF8 encoding.
69      *
70      * @param input
71      * @return {number}
72      */
73      function utf8Length(string) {
74        return string.split("")
75          .map(utf8CharByteCount)
76          // Prevent reduce from throwing an error if the string is empty.
77          .concat(0)
78          .reduce(function(sum, val) { return sum + val; });
79      }
80
81      /**
82      * Return the length of the specified input.
83      *
84      * @param input
85      * @return {number}
86      */
87      function inputLength(input) {
88        var text = input.val();
89
90        if (options.twoCharLinebreak) {
91          // Count all line breaks as 2 characters
92          text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');
93        } else {
94          // Remove all double-character (\r\n) linebreaks, so they're counted only once.
95          text = text.replace(new RegExp('\r?\n', 'g'), '\n');
96        }
97
98        var currentLength = 0;
99
100        if (options.utf8) {
101          currentLength = utf8Length(text);
102        } else {
103          currentLength = text.length;
104        }
105        return currentLength;
106      }
107
108      /**
109      * Truncate the text of the specified input.
110      *
111      * @param input
112      * @param limit
113      */
114      function truncateChars(input, maxlength) {
115        var text = input.val();
116
117        if (options.twoCharLinebreak) {
118          text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');
119
120          if (text[text.length - 1] === '\n') {
121            maxlength -= text.length % 2;
122          }
123        }
124
125        if (options.utf8) {
126          var indexedSize = text.split("").map(utf8CharByteCount);
127          for (
128            var removedBytes = 0,
129                bytesPastMax = utf8Length(text) - maxlength
130            ;removedBytes < bytesPastMax
131            ;removedBytes += indexedSize.pop()
132          );
133          maxlength -= (maxlength - indexedSize.length);
134        }
135
136        input.val(text.substr(0, maxlength));
137      }
138
139      /**
140       * Return true if the indicator should be showing up.
141       *
142       * @param input
143       * @param threshold
144       * @param maxlength
145       * @return {number}
146       */
147      function charsLeftThreshold(input, threshold, maxlength) {
148        var output = true;
149        if (!options.alwaysShow && (maxlength - inputLength(input) > threshold)) {
150          output = false;
vendor: 3,686 bytes, lines 151-268
151        }
152        return output;
153      }
154
155      /**
156       * Returns how many chars are left to complete the fill up of the form.
157       *
158       * @param input
159       * @param maxlength
160       * @return {number}
161       */
162      function remainingChars(input, maxlength) {
163        var length = maxlength - inputLength(input);
164        return length;
165      }
166
167      /**
168       * When called displays the indicator.
169       *
170       * @param indicator
171       */
172      function showRemaining(currentInput, indicator) {
173        indicator.css({
174          display: 'block'
175        });
176        currentInput.trigger('maxlength.shown');
177      }
178
179      /**
180       * When called shows the indicator.
181       *
182       * @param indicator
183       */
184      function hideRemaining(currentInput, indicator) {
185
186        if (options.alwaysShow) {
187          return;
188        }
189
190        indicator.css({
191          display: 'none'
192        });
193        currentInput.trigger('maxlength.hidden');
194      }
195
196      /**
197      * This function updates the value in the indicator
198      *
199      * @param maxLengthThisInput
200      * @param typedChars
201      * @return String
202      */
203      function updateMaxLengthHTML(currentInputText, maxLengthThisInput, typedChars) {
204        var output = '';
205        if (options.message) {
206          if (typeof options.message === 'function') {
207            output = options.message(currentInputText, maxLengthThisInput);
208          } else {
209            output = options.message.replace('%charsTyped%', typedChars)
210              .replace('%charsRemaining%', maxLengthThisInput - typedChars)
211              .replace('%charsTotal%', maxLengthThisInput);
212          }
213        } else {
214          if (options.preText) {
215            output += options.preText;
216          }
217          if (!options.showCharsTyped) {
218            output += maxLengthThisInput - typedChars;
219          }
220          else {
221            output += typedChars;
222          }
223          if (options.showMaxLength) {
224            output += options.separator + maxLengthThisInput;
225          }
226          if (options.postText) {
227            output += options.postText;
228          }
229        }
230        return output;
231      }
232
233      /**
234       * This function updates the value of the counter in the indicator.
235       * Wants as parameters: the number of remaining chars, the element currently managed,
236       * the maxLength for the current input and the indicator generated for it.
237       *
238       * @param remaining
239       * @param currentInput
240       * @param maxLengthCurrentInput
241       * @param maxLengthIndicator
242       */
243      function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) {
244        if (maxLengthIndicator) {
245          maxLengthIndicator.html(updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, (maxLengthCurrentInput - remaining)));
246
247          if (remaining > 0) {
248            if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) {
249              showRemaining(currentInput, maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass));
250            } else {
251              hideRemaining(currentInput, maxLengthIndicator);
252            }
253          } else {
254            showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass));
255          }
256        }
257
258        if (options.customMaxAttribute) {
259          // class to use for form validation on custom maxlength attribute
260          if (remaining < 0) {
261            currentInput.addClass('overmax');
262          } else {
263            currentInput.removeClass('overmax');
264          }
265        }
266      }
267
268      /**
269       * This function returns an object containing all the
270       * informations about the position of the current input
271       *
272       * @param currentInput
273       * @return object {bottom height left right top width}
274       *
275       */
276      function getPosition(currentInput) {
277        var el = currentInput[0];
278        return $.extend({}, (typeof el.getBoundingClientRect === 'function') ? el.getBoundingClientRect() : {
279          width: el.offsetWidth,
280          height: el.offsetHeight
281        }, currentInput.offset());
282      }
283
284      /**
285       * This function places the maxLengthIndicator based on placement config object.
286       *
287       * @param {object} placement
288       * @param {$} maxLengthIndicator
289       * @return null
290       *
291       */
292      function placeWithCSS(placement, maxLengthIndicator) {
293        if (!placement || !maxLengthIndicator){
294          return;
295        }
296
297        var POSITION_KEYS = [
298          'top',
299          'bottom',
300          'left',
301          'right',
302          'position'
303        ];
304
305        var cssPos = {};
306
307        // filter css properties to position
308        $.each(POSITION_KEYS, function (i, key) {
309          var val = options.placement[key];
310          if (typeof val !== 'undefined'){
311            cssPos[key] = val;
312          }
313        });
314
315        maxLengthIndicator.css(cssPos);
316
317        return;
318      }
319
320
321      /**
322       * This function places the maxLengthIndicator at the
323       * top / bottom / left / right of the currentInput
324       *
325       * @param currentInput
326       * @param maxLengthIndicator
327       * @return null
328       *
329       */
330      function place(currentInput, maxLengthIndicator) {
331        var pos = getPosition(currentInput);
332
333        // Supports custom placement handler
334        if ($.type(options.placement) === 'function'){
335          options.placement(currentInput, maxLengthIndicator, pos);
336          return;
337        }
338
339        // Supports custom placement via css positional properties
340        if ($.isPlainObject(options.placement)){
341          placeWithCSS(options.placement, maxLengthIndicator);
342          return;
343        }
344
345        var inputOuter = currentInput.outerWidth(),
346          outerWidth = maxLengthIndicator.outerWidth(),
347          actualWidth = maxLengthIndicator.width(),
348          actualHeight = maxLengthIndicator.height();
349
350        // get the right position if the indicator is appended to the input's parent
351        if (options.appendToParent) {
352          pos.top -= currentInput.parent().offset().top;
353          pos.left -= currentInput.parent().offset().left;
354        }
355
356        switch (options.placement) {
357          case 'bottom':
358            maxLengthIndicator.css({ top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 });
359            break;
360          case 'top':
361            maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 });
362            break;
363          case 'left':
364            maxLengthIndicator.css({ top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth });
365            break;
366          case 'right':
367            maxLengthIndicator.css({ top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width });
368            break;
369          case 'bottom-right':
370            maxLengthIndicator.css({ top: pos.top + pos.height, left: pos.left + pos.width });
371            break;
372          case 'top-right':
373            maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left + inputOuter });
374            break;
375          case 'top-left':
376            maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left - outerWidth });
377            break;
378          case 'bottom-left':
379            maxLengthIndicator.css({ top: pos.top + currentInput.outerHeight(), left: pos.left - outerWidth });
380            break;
381          case 'centered-right':
382            maxLengthIndicator.css({ top: pos.top + (actualHeight / 2), left: pos.left + inputOuter - outerWidth - 3 });
383            break;
384
385            // Some more options for placements
386          case 'bottom-right-inside':
387            maxLengthIndicator.css({ top: pos.top + pos.height, left: pos.left + pos.width - outerWidth });
388            break;
389          case 'top-right-inside':
390            maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left + inputOuter - outerWidth });
391            break;
392          case 'top-left-inside':
393            maxLengthIndicator.css({ top: pos.top - actualHeight, left: pos.left });
394            break;
395          case 'bottom-left-inside':
396            maxLengthIndicator.css({ top: pos.top + currentInput.outerHeight(), left: pos.left });
397            break;
398        }
399      }
400
401      /**
402       * This function returns true if the indicator position needs to
403       * be recalculated when the currentInput changes
404       *
405       * @return {boolean}
406       *
407       */
408      function isPlacementMutable() {
409        return options.placement === 'bottom-right-inside' || options.placement === 'top-right-inside' || typeof options.placement === 'function' || (options.message && typeof options.message === 'function');
410      }
411
412      /**
413       * This function retrieves the maximum length of currentInput
414       *
415       * @param currentInput
416       * @return {number}
417       *
418       */
419      function getMaxLength(currentInput) {
420        var max = currentInput.attr('maxlength') || options.customMaxAttribute;
421
422        console.debug(max);
423
424        if (options.customMaxAttribute && !options.allowOverMax) {
425          var custom = currentInput.attr(options.customMaxAttribute);
426          if (!max || custom < max) {
427            max = custom;
428          }
429        }
430
431        if (!max) {
432          max = currentInput.attr('size');
433        }
434        return max;
435      }
436
437      return this.each(function () {
438
439        var currentInput = $(this),
440          maxLengthCurrentInput,
441          maxLengthIndicator;
442
443        $(window).resize(function () {
444          if (maxLengthIndicator) {
445            place(currentInput, maxLengthIndicator);
446          }
447        });
448
449        function firstInit() {
450          var maxlengthContent = updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, '0');
451          maxLengthCurrentInput = getMaxLength(currentInput);
452
453          if (!maxLengthIndicator) {
454            maxLengthIndicator = $('<span class="bootstrap-maxlength"></span>').css({
455              display: 'none',
456              position: 'absolute',
457              whiteSpace: 'nowrap',
458              zIndex: 1099
459            }).html(maxlengthContent);
460          }
461
462          // We need to detect resizes if we are dealing with a textarea:
463          if (currentInput.is('textarea')) {
464            currentInput.data('maxlenghtsizex', currentInput.outerWidth());
465            currentInput.data('maxlenghtsizey', currentInput.outerHeight());
466
467            currentInput.mouseup(function () {
468              if (currentInput.outerWidth() !== currentInput.data('maxlenghtsizex') || currentInput.outerHeight() !== currentInput.data('maxlenghtsizey')) {
469                place(currentInput, maxLengthIndicator);
470              }
471
472              currentInput.data('maxlenghtsizex', currentInput.outerWidth());
473              currentInput.data('maxlenghtsizey', currentInput.outerHeight());
474            });
475          }
476
477          if (options.appendToParent) {
478            currentInput.parent().append(maxLengthIndicator);
479            currentInput.parent().css('position', 'relative');
480          } else {
481            documentBody.append(maxLengthIndicator);
482          }
483
484          var remaining = remainingChars(currentInput, getMaxLength(currentInput));
485          manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
486          place(currentInput, maxLengthIndicator);
487        }
488
489        if (options.showOnReady) {
490          currentInput.ready(function () {
491            firstInit();
492          });
493        } else {
494          currentInput.focus(function () {
495            firstInit();
496          });
497        }
498
499        currentInput.on('maxlength.reposition', function () {
500          place(currentInput, maxLengthIndicator);
501        });
502
503
504        currentInput.on('destroyed', function () {
505          if (maxLengthIndicator) {
506            maxLengthIndicator.remove();
507          }
508        });
509
510        currentInput.on('blur', function () {
511          if (maxLengthIndicator && !options.showOnReady) {
512            maxLengthIndicator.remove();
513          }
514        });
515
516        currentInput.on('input', function () {
517          var maxlength = getMaxLength(currentInput),
518            remaining = remainingChars(currentInput, maxlength),
519            output = true;
520
521          if (options.validate && remaining < 0) {
522            truncateChars(currentInput, maxlength);
523            output = false;
524          } else {
525            manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
526          }
527
528          if (isPlacementMutable()) {
529            place(currentInput, maxLengthIndicator);
530          }
531
532          return output;
533        });
534      });
535    }
536  });
537}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.