jquery.maskedinput.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. Masked Input plugin for jQuery
  3. Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
  4. Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
  5. Version: 1.3.1
  6. */
  7. (function($) {
  8. function getPasteEvent() {
  9. var el = document.createElement('input'),
  10. name = 'onpaste';
  11. el.setAttribute(name, '');
  12. return (typeof el[name] === 'function')?'paste':'input';
  13. }
  14. var pasteEventName = getPasteEvent() + ".mask",
  15. ua = navigator.userAgent,
  16. iPhone = /iphone/i.test(ua),
  17. android=/android/i.test(ua),
  18. caretTimeoutId;
  19. $.mask = {
  20. //Predefined character definitions
  21. definitions: {
  22. '9': "[0-9]",
  23. 'a': "[A-Za-z]",
  24. '*': "[A-Za-z0-9]"
  25. },
  26. dataName: "rawMaskFn",
  27. placeholder: '_',
  28. };
  29. $.fn.extend({
  30. //Helper Function for Caret positioning
  31. caret: function(begin, end) {
  32. var range;
  33. if (this.length === 0 || this.is(":hidden")) {
  34. return;
  35. }
  36. if (typeof begin == 'number') {
  37. end = (typeof end === 'number') ? end : begin;
  38. return this.each(function() {
  39. if (this.setSelectionRange) {
  40. this.setSelectionRange(begin, end);
  41. } else if (this.createTextRange) {
  42. range = this.createTextRange();
  43. range.collapse(true);
  44. range.moveEnd('character', end);
  45. range.moveStart('character', begin);
  46. range.select();
  47. }
  48. });
  49. } else {
  50. if (this[0].setSelectionRange) {
  51. begin = this[0].selectionStart;
  52. end = this[0].selectionEnd;
  53. } else if (document.selection && document.selection.createRange) {
  54. range = document.selection.createRange();
  55. begin = 0 - range.duplicate().moveStart('character', -100000);
  56. end = begin + range.text.length;
  57. }
  58. return { begin: begin, end: end };
  59. }
  60. },
  61. unmask: function() {
  62. return this.trigger("unmask");
  63. },
  64. mask: function(mask, settings) {
  65. var input,
  66. defs,
  67. tests,
  68. partialPosition,
  69. firstNonMaskPos,
  70. len;
  71. if (!mask && this.length > 0) {
  72. input = $(this[0]);
  73. return input.data($.mask.dataName)();
  74. }
  75. settings = $.extend({
  76. placeholder: $.mask.placeholder, // Load default placeholder
  77. completed: null
  78. }, settings);
  79. defs = $.mask.definitions;
  80. tests = [];
  81. partialPosition = len = mask.length;
  82. firstNonMaskPos = null;
  83. $.each(mask.split(""), function(i, c) {
  84. if (c == '?') {
  85. len--;
  86. partialPosition = i;
  87. } else if (defs[c]) {
  88. tests.push(new RegExp(defs[c]));
  89. if (firstNonMaskPos === null) {
  90. firstNonMaskPos = tests.length - 1;
  91. }
  92. } else {
  93. tests.push(null);
  94. }
  95. });
  96. return this.trigger("unmask").each(function() {
  97. var input = $(this),
  98. buffer = $.map(
  99. mask.split(""),
  100. function(c, i) {
  101. if (c != '?') {
  102. return defs[c] ? settings.placeholder : c;
  103. }
  104. }),
  105. focusText = input.val();
  106. function seekNext(pos) {
  107. while (++pos < len && !tests[pos]);
  108. return pos;
  109. }
  110. function seekPrev(pos) {
  111. while (--pos >= 0 && !tests[pos]);
  112. return pos;
  113. }
  114. function shiftL(begin,end) {
  115. var i,
  116. j;
  117. if (begin<0) {
  118. return;
  119. }
  120. for (i = begin, j = seekNext(end); i < len; i++) {
  121. if (tests[i]) {
  122. if (j < len && tests[i].test(buffer[j])) {
  123. buffer[i] = buffer[j];
  124. buffer[j] = settings.placeholder;
  125. } else {
  126. break;
  127. }
  128. j = seekNext(j);
  129. }
  130. }
  131. writeBuffer();
  132. input.caret(Math.max(firstNonMaskPos, begin));
  133. }
  134. function shiftR(pos) {
  135. var i,
  136. c,
  137. j,
  138. t;
  139. for (i = pos, c = settings.placeholder; i < len; i++) {
  140. if (tests[i]) {
  141. j = seekNext(i);
  142. t = buffer[i];
  143. buffer[i] = c;
  144. if (j < len && tests[j].test(t)) {
  145. c = t;
  146. } else {
  147. break;
  148. }
  149. }
  150. }
  151. }
  152. function keydownEvent(e) {
  153. var k = e.which,
  154. pos,
  155. begin,
  156. end;
  157. //backspace, delete, and escape get special treatment
  158. if (k === 8 || k === 46 || (iPhone && k === 127)) {
  159. pos = input.caret();
  160. begin = pos.begin;
  161. end = pos.end;
  162. if (end - begin === 0) {
  163. begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
  164. end=k===46?seekNext(end):end;
  165. }
  166. clearBuffer(begin, end);
  167. shiftL(begin, end - 1);
  168. e.preventDefault();
  169. } else if (k == 27) {//escape
  170. input.val(focusText);
  171. input.caret(0, checkVal());
  172. e.preventDefault();
  173. }
  174. }
  175. function keypressEvent(e) {
  176. var k = e.which,
  177. pos = input.caret(),
  178. p,
  179. c,
  180. next;
  181. if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
  182. return;
  183. } else if (k) {
  184. if (pos.end - pos.begin !== 0){
  185. clearBuffer(pos.begin, pos.end);
  186. shiftL(pos.begin, pos.end-1);
  187. }
  188. p = seekNext(pos.begin - 1);
  189. if (p < len) {
  190. c = String.fromCharCode(k);
  191. if (tests[p].test(c)) {
  192. shiftR(p);
  193. buffer[p] = c;
  194. writeBuffer();
  195. next = seekNext(p);
  196. if(android){
  197. setTimeout($.proxy($.fn.caret,input,next),0);
  198. }else{
  199. input.caret(next);
  200. }
  201. if (settings.completed && next >= len) {
  202. settings.completed.call(input);
  203. }
  204. }
  205. }
  206. e.preventDefault();
  207. }
  208. }
  209. function clearBuffer(start, end) {
  210. var i;
  211. for (i = start; i < end && i < len; i++) {
  212. if (tests[i]) {
  213. buffer[i] = settings.placeholder;
  214. }
  215. }
  216. }
  217. function writeBuffer() { input.val(buffer.join('')); }
  218. function checkVal(allow) {
  219. //try to place characters where they belong
  220. var test = input.val(),
  221. lastMatch = -1,
  222. i,
  223. c;
  224. for (i = 0, pos = 0; i < len; i++) {
  225. if (tests[i]) {
  226. buffer[i] = settings.placeholder;
  227. while (pos++ < test.length) {
  228. c = test.charAt(pos - 1);
  229. if (tests[i].test(c)) {
  230. buffer[i] = c;
  231. lastMatch = i;
  232. break;
  233. }
  234. }
  235. if (pos > test.length) {
  236. break;
  237. }
  238. } else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
  239. pos++;
  240. lastMatch = i;
  241. }
  242. }
  243. if (allow) {
  244. writeBuffer();
  245. } else if (lastMatch + 1 < partialPosition) {
  246. input.val("");
  247. clearBuffer(0, len);
  248. } else {
  249. writeBuffer();
  250. input.val(input.val().substring(0, lastMatch + 1));
  251. }
  252. return (partialPosition ? i : firstNonMaskPos);
  253. }
  254. input.data($.mask.dataName,function(){
  255. return $.map(buffer, function(c, i) {
  256. return tests[i]&&c!=settings.placeholder ? c : null;
  257. }).join('');
  258. });
  259. if (!input.attr("readonly"))
  260. input
  261. .one("unmask", function() {
  262. input
  263. .unbind(".mask")
  264. .removeData($.mask.dataName);
  265. })
  266. .bind("focus.mask", function() {
  267. clearTimeout(caretTimeoutId);
  268. var pos,
  269. moveCaret;
  270. focusText = input.val();
  271. pos = checkVal();
  272. caretTimeoutId = setTimeout(function(){
  273. writeBuffer();
  274. if (pos == mask.length) {
  275. input.caret(0, pos);
  276. } else {
  277. input.caret(pos);
  278. }
  279. }, 10);
  280. })
  281. .bind("blur.mask", function() {
  282. checkVal();
  283. if (input.val() != focusText)
  284. input.change();
  285. })
  286. .bind("keydown.mask", keydownEvent)
  287. .bind("keypress.mask", keypressEvent)
  288. .bind(pasteEventName, function() {
  289. setTimeout(function() {
  290. var pos=checkVal(true);
  291. input.caret(pos);
  292. if (settings.completed && pos == input.val().length)
  293. settings.completed.call(input);
  294. }, 0);
  295. });
  296. checkVal(); //Perform initial check for existing values
  297. });
  298. }
  299. });
  300. })(jQuery);