1/* =========================================================== 2 * bootstrap-inputmask.js j2 3 * http://twitter.github.com/bootstrap/javascript.html#tooltips 4 * Based on Masked Input plugin by Josh Bush (digitalbush.com) 5 * =========================================================== 6 * Copyright 2012 Jasny BV, Netherlands. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License") 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * ========================================================== */ 20 21!function ($) { 22 23 "use strict"; // jshint ;_; 24 25 var isIphone = (window.orientation !== undefined), 26 isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1 27 28 $.mask = { 29 //Predefined character definitions 30 definitions: { 31 '9': "[0-9]", 32 'a': "[A-Za-z]", 33 '?': "[A-Za-z0-9]", 34 '*': "." 35 }, 36 dataName:"rawMaskFn" 37 } 38 39 40 /* INPUTMASK PUBLIC CLASS DEFINITION 41 * ================================= */ 42 43 var Inputmask = function (element, options) { 44 if (isAndroid) return // No support because caret positioning doesn't work on Android 45 46 this.$element = $(element) 47 this.mask = String(options.mask) 48 this.options = $.extend({}, $.fn.inputmask.defaults, options) 49 50 this.init() 51 this.listen() 52 53 this.checkVal() //Perform initial check for existing values 54 } 55 56 Inputmask.prototype = { 57 58 init: function() { 59 var defs = $.mask.definitions 60 var len = this.mask.length 61 62 this.tests = [] 63 this.partialPosition = this.mask.length 64 this.firstNonMaskPos = null 65 66 $.each(this.mask.split(""), $.proxy(function(i, c) { 67 if (c == '?') { 68 len-- 69 this.partialPosition = i 70 } else if (defs[c]) { 71 this.tests.push(new RegExp(defs[c])) 72 if(this.firstNonMaskPos === null) 73 this.firstNonMaskPos = this.tests.length - 1 74 } else { 75 this.tests.push(null) 76 } 77 }, this)) 78 79 this.buffer = $.map(this.mask.split(""), $.proxy(function(c, i) { 80 if (c != '?') return defs[c] ? this.options.placeholder : c 81 }, this)) 82 83 this.focusText = this.$element.val() 84 85 this.$element.data($.mask.dataName, $.proxy(function() { 86 return $.map(this.buffer, function(c, i) { 87 return this.tests[i] && c != this.options.placeholder ? c : null 88 }).join('') 89 }, this)) 90 }, 91 92 listen: function() { 93 if (this.$element.attr("readonly")) return 94 95 var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask" 96 97 this.$element 98 .on("unmask", $.proxy(this.unmask, this)) 99 100 .on("focus.mask", $.proxy(this.focusEvent, this)) 101 .on("blur.mask", $.proxy(this.blurEvent, this)) 102 103 .on("keydown.mask", $.proxy(this.keydownEvent, this)) 104 .on("keypress.mask", $.proxy(this.keypressEvent, this)) 105 106 .on(pasteEventName, $.proxy(this.pasteEvent, this)) 107 }, 108 109 //Helper Function for Caret positioning 110 caret: function(begin, end) { 111 if (this.$element.length === 0) return 112 if (typeof begin == 'number') { 113 end = (typeof end == 'number') ? end : begin 114 return this.$element.each(function() { 115 if (this.setSelectionRange) { 116 this.setSelectionRange(begin, end) 117 } else if (this.createTextRange) { 118 var range = this.createTextRange() 119 range.collapse(true) 120 range.moveEnd('character', end) 121 range.moveStart('character', begin) 122 range.select() 123 } 124 }) 125 } else { 126 if (this.$element[0].setSelectionRange) { 127 begin = this.$element[0].selectionStart 128 end = this.$element[0].selectionEnd 129 } else if (document.selection && document.selection.createRange) { 130 var range = document.selection.createRange() 131 begin = 0 - range.duplicate().moveStart('character', -100000) 132 end = begin + range.text.length 133 } 134 return { 135 begin: begin, 136 end: end 137 } 138 } 139 }, 140 141 seekNext: function(pos) { 142 var len = this.mask.length 143 while (++pos <= len && !this.tests[pos]); 144 145 return pos 146 }, 147 148 seekPrev: function(pos) { 149 while (--pos >= 0 && !this.tests[pos]); 150 151 return pos 152 }, 153 154 shiftL: function(begin,end) { 155 var len = this.mask.length 156 157 if(begin<0) return 158 159 for (var i = begin,j = this.seekNext(end); i < len; i++) { 160 if (this.tests[i]) { 161 if (j < len && this.tests[i].test(this.buffer[j])) { 162 this.buffer[i] = this.buffer[j] 163 this.buffer[j] = this.options.placeholder 164 } else 165 break 166 j = this.seekNext(j) 167 } 168 } 169 this.writeBuffer() 170 this.caret(Math.max(this.firstNonMaskPos, begin)) 171 }, 172 173 shiftR: function(pos) { 174 var len = this.mask.length 175 176 for (var i = pos, c = this.options.placeholder; i < len; i++) { 177 if (this.tests[i]) { 178 var j = this.seekNext(i) 179 var t = this.buffer[i] 180 this.buffer[i] = c 181 if (j < len && this.tests[j].test(t)) 182 c = t 183 else 184 break 185 } 186 } 187 }, 188 189 unmask: function() { 190 this.$element 191 .unbind(".mask") 192 .removeData("inputmask") 193 }, 194 195 focusEvent: function() { 196 this.focusText = this.$element.val() 197 var len = this.mask.length 198 var pos = this.checkVal() 199 this.writeBuffer() 200 201 var that = this 202 var moveCaret = function() { 203 if (pos == len) 204 that.caret(0, pos) 205 else 206 that.caret(pos) 207 } 208 209 if ($.browser.msie) 210 moveCaret() 211 else 212 setTimeout(moveCaret, 0) 213 }, 214 215 blurEvent: function() { 216 this.checkVal() 217 if (this.$element.val() != this.focusText) 218 this.$element.trigger('change') 219 }, 220 221 keydownEvent: function(e) { 222 var k=e.which 223 224 //backspace, delete, and escape get special treatment 225 if (k == 8 || k == 46 || (isIphone && k == 127)) { 226 var pos = this.caret(), 227 begin = pos.begin, 228 end = pos.end 229 230 if (end-begin === 0) { 231 begin = k!=46 ? this.seekPrev(begin) : (end=this.seekNext(begin-1)) 232 end = k==46 ? this.seekNext(end) : end 233 } 234 this.clearBuffer(begin, end) 235 this.shiftL(begin,end-1) 236 237 return false 238 } else if (k == 27) {//escape 239 this.$element.val(this.focusText) 240 this.caret(0, this.checkVal()) 241 return false 242 } 243 }, 244
245 keypressEvent: function(e) { 246 var len = this.mask.length 247 248 var k = e.which, 249 pos = this.caret() 250 251 if (e.ctrlKey || e.altKey || e.metaKey || k<32) {//Ignore 252 return true 253 } else if (k) { 254 if (pos.end - pos.begin !== 0) { 255 this.clearBuffer(pos.begin, pos.end) 256 this.shiftL(pos.begin, pos.end-1) 257 } 258 259 var p = this.seekNext(pos.begin - 1) 260 if (p < len) { 261 var c = String.fromCharCode(k) 262 if (this.tests[p].test(c)) { 263 this.shiftR(p) 264 this.buffer[p] = c 265 this.writeBuffer() 266 var next = this.seekNext(p) 267 this.caret(next) 268 } 269 } 270 return false 271 } 272 }, 273 274 pasteEvent: function() { 275 var that = this 276 277 setTimeout(function() { 278 that.caret(that.checkVal(true)) 279 }, 0) 280 }, 281 282 clearBuffer: function(start, end) { 283 var len = this.mask.length 284 285 for (var i = start; i < end && i < len; i++) { 286 if (this.tests[i]) 287 this.buffer[i] = this.options.placeholder 288 } 289 }, 290 291 writeBuffer: function() { 292 return this.$element.val(this.buffer.join('')).val() 293 }, 294 295 checkVal: function(allow) { 296 var len = this.mask.length 297 //try to place characters where they belong 298 var test = this.$element.val() 299 var lastMatch = -1 300 301 for (var i = 0, pos = 0; i < len; i++) { 302 if (this.tests[i]) { 303 this.buffer[i] = this.options.placeholder 304 while (pos++ < test.length) { 305 var c = test.charAt(pos - 1) 306 if (this.tests[i].test(c)) { 307 this.buffer[i] = c 308 lastMatch = i 309 break 310 } 311 } 312 if (pos > test.length) 313 break 314 } else if (this.buffer[i] == test.charAt(pos) && i != this.partialPosition) { 315 pos++ 316 lastMatch = i 317 } 318 } 319 if (!allow && lastMatch + 1 < this.partialPosition) { 320 this.$element.val("") 321 this.clearBuffer(0, len) 322 } else if (allow || lastMatch + 1 >= this.partialPosition) { 323 this.writeBuffer() 324 if (!allow) this.$element.val(this.$element.val().substring(0, lastMatch + 1)) 325 } 326 return (this.partialPosition ? i : this.firstNonMaskPos) 327 } 328 } 329 330 331 /* INPUTMASK PLUGIN DEFINITION 332 * =========================== */ 333 334 $.fn.inputmask = function (options) { 335 return this.each(function () { 336 var $this = $(this) 337 , data = $this.data('inputmask') 338 if (!data) $this.data('inputmask', (data = new Inputmask(this, options))) 339 }) 340 } 341 342 $.fn.inputmask.defaults = { 343 placeholder: "_" 344 } 345 346 $.fn.inputmask.Constructor = Inputmask 347 348 349 /* INPUTMASK DATA-API 350 * ================== */ 351 352 $(function () { 353 $('body').on('focus.inputmask.data-api', '[data-mask]', function (e) { 354 var $this = $(this) 355 if ($this.data('inputmask')) return 356 e.preventDefault() 357 $this.inputmask($this.data()) 358 }) 359 }) 360 361}(window.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.