PageSourceSearch

https://www.alwasatnews.com/js/bootstrap-maxlength.js

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