+function ($) { "use strict"; var isIphone = (window.orientation !== undefined) var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1 var isIE = window.navigator.appName == 'Microsoft Internet Explorer' // INPUTMASK PUBLIC CLASS DEFINITION // ================================= var Inputmask = function (element, options) { if (isAndroid) return // No support because caret positioning doesn't work on Android this.$element = $(element) this.options = $.extend({}, Inputmask.DEFAULTS, options) this.mask = String(this.options.mask) this.init() this.listen() this.checkVal() //Perform initial check for existing values } Inputmask.DEFAULTS = { mask: "", placeholder: "_", definitions: { '9': "[0-9]", 'a': "[A-Za-z]", 'w': "[A-Za-z0-9]", '*': "." } } Inputmask.prototype.init = function() { var defs = this.options.definitions var len = this.mask.length this.tests = [] this.partialPosition = this.mask.length this.firstNonMaskPos = null $.each(this.mask.split(""), $.proxy(function(i, c) { if (c == '?') { len-- this.partialPosition = i } else if (defs[c]) { this.tests.push(new RegExp(defs[c])) if (this.firstNonMaskPos === null) this.firstNonMaskPos = this.tests.length - 1 } else { this.tests.push(null) } }, this)) this.buffer = $.map(this.mask.split(""), $.proxy(function(c, i) { if (c != '?') return defs[c] ? this.options.placeholder : c }, this)) this.focusText = this.$element.val() this.$element.data("rawMaskFn", $.proxy(function() { return $.map(this.buffer, function(c, i) { return this.tests[i] && c != this.options.placeholder ? c : null }).join('') }, this)) } Inputmask.prototype.listen = function() { if (this.$element.attr("readonly")) return var pasteEventName = (isIE ? 'paste' : 'input') + ".mask" this.$element .on("unmask.bs.inputmask", $.proxy(this.unmask, this)) .on("focus.bs.inputmask", $.proxy(this.focusEvent, this)) .on("blur.bs.inputmask", $.proxy(this.blurEvent, this)) .on("keydown.bs.inputmask", $.proxy(this.keydownEvent, this)) .on("keypress.bs.inputmask", $.proxy(this.keypressEvent, this)) .on(pasteEventName, $.proxy(this.pasteEvent, this)) } //Helper Function for Caret positioning Inputmask.prototype.caret = function(begin, end) { if (this.$element.length === 0) return if (typeof begin == 'number') { end = (typeof end == 'number') ? end : begin return this.$element.each(function() { if (this.setSelectionRange) { this.setSelectionRange(begin, end) } else if (this.createTextRange) { var range = this.createTextRange() range.collapse(true) range.moveEnd('character', end) range.moveStart('character', begin) range.select() } }) } else { if (this.$element[0].setSelectionRange) { begin = this.$element[0].selectionStart end = this.$element[0].selectionEnd } else if (document.selection && document.selection.createRange) { var range = document.selection.createRange() begin = 0 - range.duplicate().moveStart('character', -100000) end = begin + range.text.length } return { begin: begin, end: end } } } Inputmask.prototype.seekNext = function(pos) { var len = this.mask.length while (++pos <= len && !this.tests[pos]); return pos } Inputmask.prototype.seekPrev = function(pos) { while (--pos >= 0 && !this.tests[pos]); return pos } Inputmask.prototype.shiftL = function(begin,end) { var len = this.mask.length if (begin < 0) return for (var i = begin, j = this.seekNext(end); i < len; i++) { if (this.tests[i]) { if (j < len && this.tests[i].test(this.buffer[j])) { this.buffer[i] = this.buffer[j] this.buffer[j] = this.options.placeholder } else break j = this.seekNext(j) } } this.writeBuffer() this.caret(Math.max(this.firstNonMaskPos, begin)) } Inputmask.prototype.shiftR = function(pos) { var len = this.mask.length for (var i = pos, c = this.options.placeholder; i < len; i++) { if (this.tests[i]) { var j = this.seekNext(i) var t = this.buffer[i] this.buffer[i] = c if (j < len && this.tests[j].test(t)) c = t else break } } }, Inputmask.prototype.unmask = function() { this.$element .unbind(".mask") .removeData("inputmask") } Inputmask.prototype.focusEvent = function() { this.focusText = this.$element.val() var len = this.mask.length var pos = this.checkVal() this.writeBuffer() var that = this var moveCaret = function() { if (pos == len) that.caret(0, pos) else that.caret(pos) } moveCaret() setTimeout(moveCaret, 50) } Inputmask.prototype.blurEvent = function() { this.checkVal() if (this.$element.val() !== this.focusText) this.$element.trigger('change') } Inputmask.prototype.keydownEvent = function(e) { var k = e.which //backspace, delete, and escape get special treatment if (k == 8 || k == 46 || (isIphone && k == 127)) { var pos = this.caret(), begin = pos.begin, end = pos.end if (end - begin === 0) { begin = k != 46 ? this.seekPrev(begin) : (end = this.seekNext(begin - 1)) end = k == 46 ? this.seekNext(end) : end } this.clearBuffer(begin, end) this.shiftL(begin, end - 1) return false } else if (k == 27) {//escape this.$element.val(this.focusText) this.caret(0, this.checkVal()) return false } } Inputmask.prototype.keypressEvent = function(e) { var len = this.mask.length var k = e.which, pos = this.caret() if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore return true } else if (k) { if (pos.end - pos.begin !== 0) { this.clearBuffer(pos.begin, pos.end) this.shiftL(pos.begin, pos.end - 1) } var p = this.seekNext(pos.begin - 1) if (p < len) { var c = String.fromCharCode(k) if (this.tests[p].test(c)) { this.shiftR(p) this.buffer[p] = c this.writeBuffer() var next = this.seekNext(p) this.caret(next) } } return false } } Inputmask.prototype.pasteEvent = function() { var that = this setTimeout(function() { that.caret(that.checkVal(true)) }, 0) } Inputmask.prototype.clearBuffer = function(start, end) { var len = this.mask.length for (var i = start; i < end && i < len; i++) { if (this.tests[i]) this.buffer[i] = this.options.placeholder } } Inputmask.prototype.writeBuffer = function() { return this.$element.val(this.buffer.join('')).val() } Inputmask.prototype.checkVal = function(allow) { var len = this.mask.length //try to place characters where they belong var test = this.$element.val() var lastMatch = -1 for (var i = 0, pos = 0; i < len; i++) { if (this.tests[i]) { this.buffer[i] = this.options.placeholder while (pos++ < test.length) { var c = test.charAt(pos - 1) if (this.tests[i].test(c)) { this.buffer[i] = c lastMatch = i break } } if (pos > test.length) break } else if (this.buffer[i] == test.charAt(pos) && i != this.partialPosition) { pos++ lastMatch = i } } if (!allow && lastMatch + 1 < this.partialPosition) { this.$element.val("") this.clearBuffer(0, len) } else if (allow || lastMatch + 1 >= this.partialPosition) { this.writeBuffer() if (!allow) this.$element.val(this.$element.val().substring(0, lastMatch + 1)) } return (this.partialPosition ? i : this.firstNonMaskPos) } // INPUTMASK PLUGIN DEFINITION // =========================== var old = $.fn.inputmask $.fn.inputmask = function (options) { return this.each(function () { var $this = $(this) var data = $this.data('bs.inputmask') if (!data) $this.data('bs.inputmask', (data = new Inputmask(this, options))) }) } $.fn.inputmask.Constructor = Inputmask // INPUTMASK NO CONFLICT // ==================== $.fn.inputmask.noConflict = function () { $.fn.inputmask = old return this } // INPUTMASK DATA-API // ================== $(document).on('focus.bs.inputmask.data-api', '[data-mask]', function (e) { var $this = $(this) if ($this.data('bs.inputmask')) return $this.inputmask($this.data()) }) }(window.jQuery);