PageSourceSearch

https://reg2.kru.ac.th/admissions2/jslib/jquery.formatter.js

js kru.ac.th collected 2026-09-24 10:15:28 UTC 26,527 bytes, 861 lines download raw bytes

1/*!
2 * v0.1.5
3 * Copyright (c) 2014 First Opinion
4 * formatter.js is open sourced under the MIT license.
5 *
6 * thanks to digitalBush/jquery.maskedinput for some of the trickier
7 * keycode handling
8 */ 
9
10//
11// Uses CommonJS, AMD or browser globals to create a jQuery plugin.
12//
13// Similar to jqueryPlugin.js but also tries to
14// work in a CommonJS environment.
15// It is unlikely jQuery will run in a CommonJS
16// environment. See jqueryPlugin.js if you do
17// not want to add the extra CommonJS detection.
18//
19(function (root, factory) {
20  if (typeof define === 'function' && define.amd) {
21    // AMD. Register as an anonymous module.
22    define(['jQuery'], factory);
23  } else if (typeof exports === 'object') {
24    factory(require('jQuery'));
25  } else {
26    // Browser globals
27    factory(root.jQuery);
28  }
29}(this, function (jQuery) {
30
31
32/*
33 * pattern.js
34 *
35 * Utilities to parse str pattern and return info
36 *
37 */
38var pattern = function () {
39    // Define module
40    var pattern = {};
41    // Match information
42    var DELIM_SIZE = 4;
43    // Our regex used to parse
44    var regexp = new RegExp('{{([^}]+)}}', 'g');
45    //
46    // Helper method to parse pattern str
47    //
48    var getMatches = function (pattern) {
49      // Populate array of matches
50      var matches = [], match;
51      while (match = regexp.exec(pattern)) {
52        matches.push(match);
53      }
54      return matches;
55    };
56    //
57    // Create an object holding all formatted characters
58    // with corresponding positions
59    //
60    pattern.parse = function (pattern) {
61      // Our obj to populate
62      var info = {
63          inpts: {},
64          chars: {}
65        };
66      // Pattern information
67      var matches = getMatches(pattern), pLength = pattern.length;
68      // Counters
69      var mCount = 0, iCount = 0, i = 0;
70      // Add inpts, move to end of match, and process
71      var processMatch = function (val) {
72        var valLength = val.length;
73        for (var j = 0; j < valLength; j++) {
74          info.inpts[iCount] = val.charAt(j);
75          iCount++;
76        }
77        mCount++;
78        i += val.length + DELIM_SIZE - 1;
79      };
80      // Process match or add chars
81      for (i; i < pLength; i++) {
82        if (mCount < matches.length && i === matches[mCount].index) {
83          processMatch(matches[mCount][1]);
84        } else {
85          info.chars[i - mCount * DELIM_SIZE] = pattern.charAt(i);
86        }
87      }
88      // Set mLength and return
89      info.mLength = i - mCount * DELIM_SIZE;
90      return info;
91    };
92    // Expose
93    return pattern;
94  }();
95/*
96 * utils.js
97 *
98 * Independent helper methods (cross browser, etc..)
99 *
100 */
101var utils = function () {
102    // Define module
103    var utils = {};
104    // Useragent info for keycode handling
105    var uAgent = typeof navigator !== 'undefined' ? navigator.userAgent : null;
106    //
107    // Shallow copy properties from n objects to destObj
108    //
109    utils.extend = function (destObj) {
110      for (var i = 1; i < arguments.length; i++) {
111        for (var key in arguments[i]) {
112          destObj[key] = arguments[i][key];
113        }
114      }
115      return destObj;
116    };
117    //
118    // Add a given character to a string at a defined pos
119    //
120    utils.addChars = function (str, chars, pos) {
121      return str.substr(0, pos) + chars + str.substr(pos, str.length);
122    };
123    //
124    // Remove a span of characters
125    //
126    utils.removeChars = function (str, start, end) {
127      return str.substr(0, start) + str.substr(end, str.length);
128    };
129    //
130    // Return true/false is num false between bounds
131    //
132    utils.isBetween = function (num, bounds) {
133      bounds.sort(function (a, b) {
134        return a - b;
135      });
136      return num > bounds[0] && num < bounds[1];
137    };
138    //
139    // Helper method for cross browser event listeners
140    //
141    utils.addListener = function (el, evt, handler) {
142      return typeof el.addEventListener !== 'undefined' ? el.addEventListener(evt, handler, false) : el.attachEvent('on' + evt, handler);
143    };
144    //
145    // Helper method for cross browser implementation of preventDefault
146    //
147    utils.preventDefault = function (evt) {
148      return evt.preventDefault ? evt.preventDefault() : evt.returnValue = false;
149    };
150    //
151    // Helper method for cross browser implementation for grabbing
152    // clipboard data
153    //
154    utils.getClip = function (evt) {
155      if (evt.clipboardData) {
156        return evt.clipboardData.getData('Text');
157      }
158      if (window.clipboardData) {
159        return window.clipboardData.getData('Text');
160      }
161    };
162    //
163    // Loop over object and checking for matching properties
164    //
165    utils.getMatchingKey = function (which, keyCode, keys) {
166      // Loop over and return if matched.
167      for (var k in keys) {
168        var key = keys[k];
169        if (which === key.which && keyCode === key.keyCode) {
170          return k;
171        }
172      }
173    };
174    //
175    // Returns true/false if k is a del keyDown
176    //
177    utils.isDelKeyDown = function (which, keyCode) {
178      var keys = {
179          'backspace': {
180            'which': 8,
181            'keyCode': 8
182          },
183          'delete': {
184            'which': 46,
185            'keyCode': 46
186          }
187        };
188      return utils.getMatchingKey(which, keyCode, keys);
189    };
190    //
191    // Returns true/false if k is a del keyPress
192    //
193    utils.isDelKeyPress = function (which, keyCode) {
194      var keys = {
195          'backspace': {
196            'which': 8,
197            'keyCode': 8,
198            'shiftKey': false
199          },
200          'delete': {
201            'which': 0,
202            'keyCode': 46
203          }
204        };
205      return utils.getMatchingKey(which, keyCode, keys);
206    };
207    // //
208    // // Determine if keydown relates to specialKey
209    // //
210    // utils.isSpecialKeyDown = function (which, keyCode) {
211    //   var keys = {
212    //     'tab': { 'which': 9, 'keyCode': 9 },
213    //     'enter': { 'which': 13, 'keyCode': 13 },
214    //     'end': { 'which': 35, 'keyCode': 35 },
215    //     'home': { 'which': 36, 'keyCode': 36 },
216    //     'leftarrow': { 'which': 37, 'keyCode': 37 },
217    //     'uparrow': { 'which': 38, 'keyCode': 38 },
218    //     'rightarrow': { 'which': 39, 'keyCode': 39 },
219    //     'downarrow': { 'which': 40, 'keyCode': 40 },
220    //     'F5': { 'which': 116, 'keyCode': 116 }
221    //   };
222    //   return utils.getMatchingKey(which, keyCode, keys);
223    // };
224    //
225    // Determine if keypress relates to specialKey
226    //
227    utils.isSpecialKeyPress = function (which, keyCode) {
228      var keys = {
229          'tab': {
230            'which': 0,
231            'keyCode': 9
232          },
233          'enter': {
234            'which': 13,
235            'keyCode': 13
236          },
237          'end': {
238            'which': 0,
239            'keyCode': 35
240          },
241          'home': {
242            'which': 0,
243            'keyCode': 36
244          },
245          'leftarrow': {
246            'which': 0,
247            'keyCode': 37
248          },
249          'uparrow': {
250            'which': 0,
251            'keyCode': 38
252          },
253          'rightarrow': {
254            'which': 0,
255            'keyCode': 39
256          },
257          'downarrow': {
258            'which': 0,
259            'keyCode': 40
260          },
261          'F5': {
262            'which': 116,
263            'keyCode': 116
264          }
265        };
266      return utils.getMatchingKey(which, keyCode, keys);
267    };
268    //
269    // Returns true/false if modifier key is held down
270    //
271    utils.isModifier = function (evt) {
272      return evt.ctrlKey || evt.altKey || evt.metaKey;
273    };
274    //
275    // Iterates over each property of object or array.
276    //
277    utils.forEach = function (collection, callback, thisArg) {
278      if (collection.hasOwnProperty('length')) {
279        for (var index = 0, len = collection.length; index < len; index++) {
280          if (callback.call(thisArg, collection[index], index, collection) === false) {
281            break;
282          }
283        }
284      } else {
285        for (var key in collection) {
286          if (collection.hasOwnProperty(key)) {
287            if (callback.call(thisArg, collection[key], key, collection) === false) {
288              break;
289            }
290          }
291        }
292      }
293    };
294    // Expose
295    return utils;
296  }();
297/*
298* pattern-matcher.js
299*
300* Parses a pattern specification and determines appropriate pattern for an
301* input string
302*
303*/
304var patternMatcher = function (pattern, utils) {
305    //
306    // Parse a matcher string into a RegExp. Accepts valid regular
307    // expressions and the catchall '*'.
308    // @private
309    //
310    var parseMatcher = function (matcher) {
311      if (matcher === '*') {
312        return /.*/;
313      }
314      return new RegExp(matcher);
315    };
316    //
317    // Parse a pattern spec and return a function that returns a pattern
318    // based on user input. The first matching pattern will be chosen.
319    // Pattern spec format:
320    // Array [
321    //  Object: { Matcher(RegExp String) : Pattern(Pattern String) },
322    //  ...
323    // ]
324    function patternMatcher(patternSpec) {
325      var matchers = [], patterns = [];
326      // Iterate over each pattern in order.
327      utils.forEach(patternSpec, function (patternMatcher) {
328        // Process single property object to obtain pattern and matcher.
329        utils.forEach(patternMatcher, function (patternStr, matcherStr) {
330          var parsedPattern = pattern.parse(patternStr), regExpMatcher = parseMatcher(matcherStr);
331          matchers.push(regExpMatcher);
332          patterns.push(parsedPattern);
333          // Stop after one iteration.
334          return false;
335        });
336      });
337      var getPattern = function (input) {
338        var matchedIndex;
339        utils.forEach(matchers, function (matcher, index) {
340          if (matcher.test(input)) {
341            matchedIndex = index;
342            return false;
343          }
344        });
345        return matchedIndex === undefined ? null : patterns[matchedIndex];
346      };
347      return {
348        getPattern: getPattern,
349        patterns: patterns,
350        matchers: matchers
351      };
352    }
353    // Expose
354    return patternMatcher;
355  }(pattern, utils);
356/*
357 * inpt-sel.js
358 *
359 * Cross browser implementation to get and set input selections
360 *
361 */
362var inptSel = function () {
363    // Define module
364    var inptSel = {};
365    //
366    // Get begin and end positions of selected input. Return 0's
367    // if there is no selectiion data
368    //
369    inptSel.get = function (el) {
370      // If normal browser return with result
371      if (typeof el.selectionStart === 'number') {
372        return {
373          begin: el.selectionStart,
374          end: el.selectionEnd
375        };
376      }
377      // Uh-Oh. We must be IE. Fun with TextRange!!
378      var range = document.selection.createRange();
379      // Determine if there is a selection
380      if (range && range.parentElement() === el) {
381        var inputRange = el.createTextRange(), endRange = el.createTextRange(), length = el.value.length;
382        // Create a working TextRange for the input selection
383        inputRange.moveToBookmark(range.getBookmark());
384        // Move endRange begin pos to end pos (hence endRange)
385        endRange.collapse(false);
386        // If we are at the very end of the input, begin and end
387        // must both be the length of the el.value
388        if (inputRange.compareEndPoints('StartToEnd', endRange) > -1) {
389          return {
390            begin: length,
391            end: length
392          };
393        }
394        // Note: moveStart usually returns the units moved, which 
395        // one may think is -length, however, it will stop when it
396        // gets to the begin of the range, thus giving us the
397        // negative value of the pos.
398        return {
399          begin: -inputRange.moveStart('character', -length),
400          end: -inputRange.moveEnd('character', -length)
401        };
402      }
403      //Return 0's on no selection data
404      return {
405        begin: 0,
406        end: 0
407      };
408    };
409    //
410    // Set the caret position at a specified location
411    //
412    inptSel.set = function (el, pos) {
413      // Normalize pos
414      if (typeof pos !== 'object') {
415        pos = {
416          begin: pos,
417          end: pos
418        };
419      }
420      // If normal browser
421      if (el.setSelectionRange) {
422        el.focus();
423        el.setSelectionRange(pos.begin, pos.end);
424      } else if (el.createTextRange) {
425        var range = el.createTextRange();
426        range.collapse(true);
427        range.moveEnd('character', pos.end);
428        range.moveStart('character', pos.begin);
429        range.select();
430      }
431    };
432    // Expose
433    return inptSel;
434  }();
435/*
436 * formatter.js
437 *
438 * Class used to format input based on passed pattern
439 *
440 */
441var formatter = function (patternMatcher, inptSel, utils) {
442    // Defaults
443    var defaults = {
444        persistent: false,
445        repeat: false,
446        placeholder: ' '
447      };
448    // Regexs for input validation
449    var inptRegs = {
450        '9': /[0-9]/,
451        'a': /[A-Za-z]/,
452        '*': /[A-Za-z0-9]/
453      };
454    //
455    // Class Constructor - Called with new Formatter(el, opts)
456    // Responsible for setting up required instance variables, and
457    // attaching the event listener to the element.
458    //
459    function Formatter(el, opts) {
460      // Cache this
461      var self = this;
462      // Make sure we have an element. Make accesible to instance
463      self.el = el;
464      if (!self.el) {
465        throw new TypeError('Must provide an existing element');
466      }
467      // Merge opts with defaults
468      self.opts = utils.extend({}, defaults, opts);
469      // 1 pattern is special case
470      if (typeof self.opts.pattern !== 'undefined') {
471        self.opts.patterns = self._specFromSinglePattern(self.opts.pattern);
472        delete self.opts.pattern;
473      }
474      // Make sure we have valid opts
475      if (typeof self.opts.patterns === 'undefined') {
476        throw new TypeError('Must provide a pattern or array of patterns');
477      }
478      self.patternMatcher = patternMatcher(self.opts.patterns);
479      // Upate pattern with initial value
480      self._updatePattern();
481      // Init values
482      self.hldrs = {};
483      self.focus = 0;
484      // Add Listeners
485      utils.addListener(self.el, 'keydown', function (evt) {
486        self._keyDown(evt);
487      });
488      utils.addListener(self.el, 'keypress', function (evt) {
489        self._keyPress(evt);
490      });
491      utils.addListener(self.el, 'paste', function (evt) {
492        self._paste(evt);
493      });
494      // Persistence
495      if (self.opts.persistent) {
496        // Format on start
497        self._processKey('', false);
498        self.el.blur();
499        // Add Listeners
500        utils.addListener(self.el, 'focus', function (evt) {
501          self._focus(evt);
502        });
503        utils.addListener(self.el, 'click', function (evt) {
504          self._focus(evt);
505        });
506        utils.addListener(self.el, 'touchstart', function (evt) {
507          self._focus(evt);
508        });
509      }
510    }
511    //
512    // @public
513    // Add new char
514    //
515    Formatter.addInptType = function (chr, reg) {
516      inptRegs[chr] = reg;
517    };
518    //
519    // @public
520    // Apply the given pattern to the current input without moving caret.
521    //
522    Formatter.prototype.resetPattern = function (str) {
523      // Update opts to hold new pattern
524      this.opts.patterns = str ? this._specFromSinglePattern(str) : this.opts.patterns;
525      // Get current state
526      this.sel = inptSel.get(this.el);
527      this.val = this.el.value;
528      // Init values
529      this.delta = 0;
530      // Remove all formatted chars from val
531      this._removeChars();
532      this.patternMatcher = patternMatcher(this.opts.patterns);
533      // Update pattern
534      var newPattern = this.patternMatcher.getPattern(this.val);
535      this.mLength = newPattern.mLength;
536      this.chars = newPattern.chars;
537      this.inpts = newPattern.inpts;
538      // Format on start
539      this._processKey('', false, true);
540    };
541    //
542    // @private
543    // Determine correct format pattern based on input val
544    //
545    Formatter.prototype._updatePattern = function () {
546      // Determine appropriate pattern
547      var newPattern = this.patternMatcher.getPattern(this.val);
548      // Only update the pattern if there is an appropriate pattern for the value.
549      // Otherwise, leave the current pattern (and likely delete the latest character.)
550      if (newPattern) {
551        // Get info about the given pattern
552        this.mLength = newPattern.mLength;
553        this.chars = newPattern.chars;
554        this.inpts = newPattern.inpts;
555      }
556    };
557    //
558    // @private
559    // Handler called on all keyDown strokes. All keys trigger
560    // this handler. Only process delete keys.
561    //
562    Formatter.prototype._keyDown = function (evt) {
563      // The first thing we need is the character code
564      var k = evt.which || evt.keyCode;
565      // If delete key
566      if (k && utils.isDelKeyDown(evt.which, evt.keyCode)) {
567        // Process the keyCode and prevent default
568        this._processKey(null, k);
569        return utils.preventDefault(evt);
570      }
571    };
572    //
573    // @private
574    // Handler called on all keyPress strokes. Only processes
575    // character keys (as long as no modifier key is in use).
576    //
577    Formatter.prototype._keyPress = function (evt) {
578      // The first thing we need is the character code
579      var k, isSpecial;
580      // Mozilla will trigger on special keys and assign the the value 0
581      // We want to use that 0 rather than the keyCode it assigns.
582      k = evt.which || evt.keyCode;
583      isSpecial = utils.isSpecialKeyPress(evt.which, evt.keyCode);
584      // Process the keyCode and prevent default
585      if (!utils.isDelKeyPress(evt.which, evt.keyCode) && !isSpecial && !utils.isModifier(evt)) {
586        this._processKey(String.fromCharCode(k), false);
587        return utils.preventDefault(evt);
588      }
589    };
590    //
591    // @private
592    // Handler called on paste event.
593    //
594    Formatter.prototype._paste = function (evt) {
595      // Process the clipboard paste and prevent default
596      this._processKey(utils.getClip(evt), false);
597      return utils.preventDefault(evt);
598    };
599    //
600    // @private
601    // Handle called on focus event.
602    //
603    Formatter.prototype._focus = function () {
604      // Wrapped in timeout so that we can grab input selection
605      var self = this;
606      setTimeout(function () {
607        // Grab selection
608        var selection = inptSel.get(self.el);
609        // Char check
610        var isAfterStart = selection.end > self.focus, isFirstChar = selection.end === 0;
611        // If clicked in front of start, refocus to start
612        if (isAfterStart || isFirstChar) {
613          inptSel.set(self.el, self.focus);
614        }
615      }, 0);
616    };
617    //
618    // @private
619    // Using the provided key information, alter el value.
620    //
621    Formatter.prototype._processKey = function (chars, delKey, ignoreCaret) {
622      // Get current state
623      this.sel = inptSel.get(this.el);
624      this.val = this.el.value;
625      // Init values
626      this.delta = 0;
627      // If chars were highlighted, we need to remove them
628      if (this.sel.begin !== this.sel.end) {
629        this.delta = -1 * Math.abs(this.sel.begin - this.sel.end);
630        this.val = utils.removeChars(this.val, this.sel.begin, this.sel.end);
631      } else if (delKey && delKey === 46) {
632        this._delete();
633      } else if (delKey && this.sel.begin - 1 >= 0) {
634        // Always have a delta of at least -1 for the character being deleted.
635        this.val = utils.removeChars(this.val, this.sel.end - 1, this.sel.end);
636        this.delta -= 1;
637      } else if (delKey) {
638        return true;
639      }
640      // If the key is not a del key, it should convert to a str
641      if (!delKey) {
642        // Add char at position and increment delta
643        this.val = utils.addChars(this.val, chars, this.sel.begin);
644        this.delta += chars.length;
645      }
646      // Format el.value (also handles updating caret position)
647      this._formatValue(ignoreCaret);
648    };
649    //
650    // @private
651    // Deletes the character in front of it
652    //
653    Formatter.prototype._delete = function () {
654      // Adjust focus to make sure its not on a formatted char
655      while (this.chars[this.sel.begin]) {
656        this._nextPos();
657      }
658      // As long as we are not at the end
659      if (this.sel.begin < this.val.length) {
660        // We will simulate a delete by moving the caret to the next char
661        // and then deleting
662        this._nextPos();
663        this.val = utils.removeChars(this.val, this.sel.end - 1, this.sel.end);
664        this.delta = -1;
665      }
666    };
667    //
668    // @private
669    // Quick helper method to move the caret to the next pos
670    //
671    Formatter.prototype._nextPos = function () {
672      this.sel.end++;
673      this.sel.begin++;
674    };
675    //
676    // @private
677    // Alter element value to display characters matching the provided
678    // instance pattern. Also responsible for updating
679    //
680    Formatter.prototype._formatValue = function (ignoreCaret) {
681      // Set caret pos
682      this.newPos = this.sel.end + this.delta;
683      // Remove all formatted chars from val
684      this._removeChars();
685      // Switch to first matching pattern based on val
686      this._updatePattern();
687      // Validate inputs
688      this._validateInpts();
689      // Add formatted characters
690      this._addChars();
691      // Set value and adhere to maxLength
692      this.el.value = this.val.substr(0, this.mLength);
693      // Set new caret position
694      if (typeof ignoreCaret === 'undefined' || ignoreCaret === false) {
695        inptSel.set(this.el, this.newPos);
696      }
697    };
698    //
699    // @private
700    // Remove all formatted before and after a specified pos
701    //
702    Formatter.prototype._removeChars = function () {
703      // Delta shouldn't include placeholders
704      if (this.sel.end > this.focus) {
705        this.delta += this.sel.end - this.focus;
706      }
707      // Account for shifts during removal
708      var shift = 0;
709      // Loop through all possible char positions
710      for (var i = 0; i <= this.mLength; i++) {
711        // Get transformed position
712        var curChar = this.chars[i], curHldr = this.hldrs[i], pos = i + shift, val;
713        // If after selection we need to account for delta
714        pos = i >= this.sel.begin ? pos + this.delta : pos;
715        val = this.val.charAt(pos);
716        // Remove char and account for shift
717        if (curChar && curChar === val || curHldr && curHldr === val) {
718          this.val = utils.removeChars(this.val, pos, pos + 1);
719          shift--;
720        }
721      }
722      // All hldrs should be removed now
723      this.hldrs = {};
724      // Set focus to last character
725      this.focus = this.val.length;
726    };
727    //
728    // @private
729    // Make sure all inpts are valid, else remove and update delta
730    //
731    Formatter.prototype._validateInpts = function () {
732      // Loop over each char and validate
733      for (var i = 0; i < this.val.length; i++) {
734        // Get char inpt type
735        var inptType = this.inpts[i];
736        // Checks
737        var isBadType = !inptRegs[inptType], isInvalid = !isBadType && !inptRegs[inptType].test(this.val.charAt(i)), inBounds = this.inpts[i];
738        // Remove if incorrect and inbounds
739        if ((isBadType || isInvalid) && inBounds) {
740          this.val = utils.removeChars(this.val, i, i + 1);
741          this.focusStart--;
742          this.newPos--;
743          this.delta--;
744          i--;
745        }
746      }
747    };
748    //
749    // @private
750    // Loop over val and add formatted chars as necessary
751    //
752    Formatter.prototype._addChars = function () {
753      if (this.opts.persistent) {
754        // Loop over all possible characters
755        for (var i = 0; i <= this.mLength; i++) {
756          if (!this.val.charAt(i)) {
757            // Add placeholder at pos
758            this.val = utils.addChars(this.val, this.opts.placeholder, i);
759            this.hldrs[i] = this.opts.placeholder;
760          }
761          this._addChar(i);
762        }
763        // Adjust focus to make sure its not on a formatted char
764        while (this.chars[this.focus]) {
765          this.focus++;
766        }
767      } else {
768        // Avoid caching val.length, as they may change in _addChar.
769        for (var j = 0; j <= this.val.length; j++) {
770          // When moving backwards there are some race conditions where we
771          // dont want to add the character
772          if (this.delta <= 0 && j === this.focus) {
773            return true;
774          }
775          // Place character in current position of the formatted string.
776          this._addChar(j);
777        }
778      }
779    };
780    //
781    // @private
782    // Add formattted char at position
783    //
784    Formatter.prototype._addChar = function (i) {
785      // If char exists at position
786      var chr = this.chars[i];
787      if (!chr) {
788        return true;
789      }
790      // If chars are added in between the old pos and new pos
791      // we need to increment pos and delta
792      if (utils.isBetween(i, [
793          this.sel.begin - 1,
794          this.newPos + 1
795        ])) {
796        this.newPos++;
797        this.delta++;
798      }
799      // If character added before focus, incr
800      if (i <= this.focus) {
801        this.focus++;
802      }
803      // Updateholder
804      if (this.hldrs[i]) {
805        delete this.hldrs[i];
806        this.hldrs[i + 1] = this.opts.placeholder;
807      }
808      // Update value
809      this.val = utils.addChars(this.val, chr, i);
810    };
811    //
812    // @private
813    // Create a patternSpec for passing into patternMatcher that
814    // has exactly one catch all pattern.
815    //
816    Formatter.prototype._specFromSinglePattern = function (patternStr) {
817      return [{ '*': patternStr }];
818    };
819    // Expose
820    return Formatter;
821  }(patternMatcher, inptSel, utils);
822
823
824
825// A really lightweight plugin wrapper around the constructor,
826// preventing against multiple instantiations
827var pluginName = 'formatter';
828
829$.fn[pluginName] = function (options) {
830
831  // Initiate plugin if options passed
832  if (typeof options == 'object') {
833    this.each(function () {
834      if (!$.data(this, 'plugin_' + pluginName)) {
835        $.data(this, 'plugin_' + pluginName,
836        new formatter(this, options));
837      }
838    });
839  }
840
841  // Add resetPattern method to plugin
842  this.resetPattern = function (str) {
843    this.each(function () {
844      var formatted = $.data(this, 'plugin_' + pluginName);
845      // resetPattern for instance
846      if (formatted) { formatted.resetPattern(str); }
847    });
848    // Chainable please
849    return this;
850  };
851
852  // Chainable please
853  return this;
854};
855
856$.fn[pluginName].addInptType = function (chr, regexp) {
857  formatter.addInptType(chr, regexp);
858};
859
860
861}));

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.