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.