jquery.inputmask-multi.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /**
  2. * @license Input Mask plugin for jquery
  3. * http://github.com/RobinHerbots/jquery.inputmask
  4. * Copyright (c) 2010 - 2014 Robin Herbots
  5. * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
  6. * Version: 0.0.0
  7. */
  8. (function ($) {
  9. if ($.fn.inputmask != undefined) {
  10. function multiMaskScope(actionObj, masksets, opts) {
  11. function isInputEventSupported(eventName) {
  12. var el = document.createElement('input'),
  13. eventName = 'on' + eventName,
  14. isSupported = (eventName in el);
  15. if (!isSupported) {
  16. el.setAttribute(eventName, 'return;');
  17. isSupported = typeof el[eventName] == 'function';
  18. }
  19. el = null;
  20. return isSupported;
  21. }
  22. var PasteEventType = isInputEventSupported('paste') ? 'paste' : isInputEventSupported('input') ? 'input' : "propertychange",
  23. isRTL, el, $el, elmasks, activeMasksetIndex;
  24. function PatchValhookMulti(type) {
  25. if ($.valHooks[type] == undefined || $.valHooks[type].inputmaskmultipatch != true) {
  26. var valueGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function (elem) { return elem.value; };
  27. var valueSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function (elem, value) {
  28. elem.value = value;
  29. return elem;
  30. };
  31. $.valHooks[type] = {
  32. get: function (elem) {
  33. var $elem = $(elem);
  34. if ($elem.data('_inputmask-multi')) {
  35. var data = $elem.data('_inputmask-multi');
  36. return valueGet(data["elmasks"][data["activeMasksetIndex"]]);
  37. } else return valueGet(elem);
  38. },
  39. set: function (elem, value) {
  40. var $elem = $(elem);
  41. var result = valueSet(elem, value);
  42. if ($elem.data('_inputmask-multi')) $elem.triggerHandler('setvalue');
  43. return result;
  44. },
  45. inputmaskmultipatch: true
  46. };
  47. }
  48. }
  49. function mcaret(input, begin, end) {
  50. var npt = input.jquery && input.length > 0 ? input[0] : input, range;
  51. if (typeof begin == 'number') {
  52. begin = TranslatePosition(begin);
  53. end = TranslatePosition(end);
  54. end = (typeof end == 'number') ? end : begin;
  55. //store caret for multi scope
  56. if (npt != el) {
  57. var data = $(npt).data('_inputmask') || {};
  58. data["caret"] = { "begin": begin, "end": end };
  59. $(npt).data('_inputmask', data);
  60. }
  61. if (!$(npt).is(":visible")) {
  62. return;
  63. }
  64. npt.scrollLeft = npt.scrollWidth;
  65. if (opts.insertMode == false && begin == end) end++; //set visualization for insert/overwrite mode
  66. if (npt.setSelectionRange) {
  67. npt.selectionStart = begin;
  68. npt.selectionEnd = end;
  69. } else if (npt.createTextRange) {
  70. range = npt.createTextRange();
  71. range.collapse(true);
  72. range.moveEnd('character', end);
  73. range.moveStart('character', begin);
  74. range.select();
  75. }
  76. } else {
  77. var data = $(npt).data('_inputmask');
  78. if (!$(npt).is(":visible") && data && data["caret"] != undefined) {
  79. begin = data["caret"]["begin"];
  80. end = data["caret"]["end"];
  81. } else if (npt.setSelectionRange) {
  82. begin = npt.selectionStart;
  83. end = npt.selectionEnd;
  84. } else if (document.selection && document.selection.createRange) {
  85. range = document.selection.createRange();
  86. begin = 0 - range.duplicate().moveStart('character', -100000);
  87. end = begin + range.text.length;
  88. }
  89. begin = TranslatePosition(begin);
  90. end = TranslatePosition(end);
  91. return { "begin": begin, "end": end };
  92. }
  93. }
  94. function TranslatePosition(pos) {
  95. if (isRTL && typeof pos == 'number' && (!opts.greedy || opts.placeholder != "")) {
  96. var bffrLght = el.value.length;
  97. pos = bffrLght - pos;
  98. }
  99. return pos;
  100. }
  101. function determineActiveMask(eventType, elmasks) {
  102. if (eventType != "multiMaskScope") {
  103. if ($.isFunction(opts.determineActiveMasksetIndex))
  104. activeMasksetIndex = opts.determineActiveMasksetIndex.call($el, eventType, elmasks);
  105. else {
  106. var lpc = -1, cp = -1, lvp = -1;;
  107. $.each(elmasks, function (ndx, lmsk) {
  108. var data = $(lmsk).data('_inputmask');
  109. var maskset = data["maskset"];
  110. var lastValidPosition = -1, validPositionCount = 0, caretPos = mcaret(lmsk).begin;
  111. for (var posNdx in maskset["validPositions"]) {
  112. var psNdx = parseInt(posNdx);
  113. if (psNdx > lastValidPosition) lastValidPosition = psNdx;
  114. validPositionCount++;
  115. }
  116. if (validPositionCount > lpc
  117. || (validPositionCount == lpc && cp > caretPos && lvp > lastValidPosition)
  118. || (validPositionCount == lpc && cp == caretPos && lvp < lastValidPosition)
  119. ) {
  120. //console.log("lvp " + lastValidPosition + " vpc " + validPositionCount + " caret " + caretPos + " ams " + ndx);
  121. lpc = validPositionCount;
  122. cp = caretPos;
  123. activeMasksetIndex = ndx;
  124. lvp = lastValidPosition;
  125. }
  126. });
  127. }
  128. var data = $el.data('_inputmask-multi') || { "activeMasksetIndex": 0, "elmasks": elmasks };
  129. data["activeMasksetIndex"] = activeMasksetIndex;
  130. $el.data('_inputmask-multi', data);
  131. }
  132. if ($.inArray(eventType, ["focus"]) == -1 && el.value != elmasks[activeMasksetIndex]._valueGet()) {
  133. var value = $(elmasks[activeMasksetIndex]).val() == "" ? elmasks[activeMasksetIndex]._valueGet() : $(elmasks[activeMasksetIndex]).val();
  134. el.value = value;
  135. }
  136. if ($.inArray(eventType, ["blur", "focus"]) == -1) {
  137. if ($(elmasks[activeMasksetIndex]).hasClass("focus.inputmask")) {
  138. var activeCaret = mcaret(elmasks[activeMasksetIndex]);
  139. mcaret(el, activeCaret.begin, activeCaret.end);
  140. }
  141. }
  142. }
  143. opts.multi = true;
  144. function mask(npt) {
  145. el = npt;
  146. $el = $(el);
  147. isRTL = el.dir == "rtl" || opts.numericInput;
  148. activeMasksetIndex = 0;
  149. elmasks = $.map(masksets, function (msk, ndx) {
  150. var elMaskStr = '<input type="text" ';
  151. if ($el.attr("value")) elMaskStr += 'value="' + $el.attr("value") + '" ';
  152. if ($el.attr("dir")) elMaskStr += 'dir="' + $el.attr("dir") + '" ';
  153. elMaskStr += '/>';
  154. var elmask = $(elMaskStr)[0];
  155. $(elmask).inputmask(msk.mask, opts);
  156. return elmask;
  157. });
  158. $el.data('_inputmask-multi', { "activeMasksetIndex": 0, "elmasks": elmasks });
  159. if (el.dir == "rtl" || opts.rightAlign)
  160. $el.css("text-align", "right");
  161. el.dir = "ltr";
  162. $el.removeAttr("dir");
  163. if ($el.attr("value") != "") {
  164. determineActiveMask("init", elmasks);
  165. }
  166. $el.bind("mouseenter blur focus mouseleave click dblclick keydown keypress keypress", function (e) {
  167. var caretPos = mcaret(el), k, goDetermine = true;
  168. if (e.type == "keydown") {
  169. k = e.keyCode;
  170. if (k == opts.keyCode.DOWN && activeMasksetIndex < elmasks.length - 1) {
  171. activeMasksetIndex++;
  172. determineActiveMask("multiMaskScope", elmasks);
  173. return false;
  174. } else if (k == opts.keyCode.UP && activeMasksetIndex > 0) {
  175. activeMasksetIndex--;
  176. determineActiveMask("multiMaskScope", elmasks);
  177. return false;
  178. }
  179. if (e.ctrlKey || e.shiftKey || e.altKey) {
  180. return true;
  181. }
  182. } else if (e.type == "keypress" && (e.ctrlKey || e.shiftKey || e.altKey)) {
  183. return true;
  184. }
  185. $.each(elmasks, function (ndx, lmnt) {
  186. if (e.type == "keydown") {
  187. k = e.keyCode;
  188. if (k == opts.keyCode.BACKSPACE && lmnt._valueGet().length < caretPos.begin) {
  189. return;
  190. } else if (k == opts.keyCode.TAB) {
  191. goDetermine = false;
  192. } else if (k == opts.keyCode.RIGHT) {
  193. mcaret(lmnt, caretPos.begin + 1, caretPos.end + 1);
  194. goDetermine = false;
  195. return;
  196. } else if (k == opts.keyCode.LEFT) {
  197. mcaret(lmnt, caretPos.begin - 1, caretPos.end - 1);
  198. goDetermine = false;
  199. return;
  200. }
  201. }
  202. if ($.inArray(e.type, ["click"]) != -1) {
  203. mcaret(lmnt, TranslatePosition(caretPos.begin), TranslatePosition(caretPos.end));
  204. if (caretPos.begin != caretPos.end) {
  205. goDetermine = false;
  206. return;
  207. }
  208. }
  209. if ($.inArray(e.type, ["keydown"]) != -1 && caretPos.begin != caretPos.end) {
  210. mcaret(lmnt, caretPos.begin, caretPos.end);
  211. }
  212. $(lmnt).triggerHandler(e);
  213. });
  214. if (goDetermine) {
  215. setTimeout(function () {
  216. determineActiveMask(e.type, elmasks);
  217. }, 0);
  218. }
  219. });
  220. $el.bind(PasteEventType + " dragdrop drop setvalue", function (e) {
  221. var caretPos = mcaret(el);
  222. setTimeout(function () {
  223. $.each(elmasks, function (ndx, lmnt) {
  224. lmnt._valueSet(el.value);
  225. $(lmnt).triggerHandler(e);
  226. });
  227. setTimeout(function () {
  228. determineActiveMask(e.type, elmasks);
  229. }, 0);
  230. }, 0);
  231. });
  232. PatchValhookMulti(el.type);
  233. }
  234. //action object
  235. if (actionObj != undefined) {
  236. switch (actionObj["action"]) {
  237. case "isComplete":
  238. $el = $(actionObj["el"]);
  239. var imdata = $el.data('_inputmask-multi'),
  240. activeMask = imdata["elmasks"][imdata["activeMasksetIndex"]];
  241. return $(activeMask).inputmask("isComplete");
  242. case "unmaskedvalue":
  243. $el = actionObj["$input"];
  244. var imdata = $el.data('_inputmask-multi'),
  245. activeMask = imdata["elmasks"][imdata["activeMasksetIndex"]];
  246. return $(activeMask).inputmask("unmaskedvalue");
  247. case "mask":
  248. mask(actionObj["el"]);
  249. break;
  250. case "format": //TODO
  251. $el = $({});
  252. $el.data('_inputmask', {
  253. 'maskset': maskset,
  254. 'opts': opts,
  255. 'isRTL': opts.numericInput
  256. });
  257. if (opts.numericInput) {
  258. isRTL = true;
  259. }
  260. var valueBuffer = actionObj["value"].split('');
  261. checkVal($el, false, false, isRTL ? valueBuffer.reverse() : valueBuffer, true);
  262. return isRTL ? getBuffer().reverse().join('') : getBuffer().join('');
  263. case "isValid": //TODO
  264. $el = $({});
  265. $el.data('_inputmask', {
  266. 'maskset': maskset,
  267. 'opts': opts,
  268. 'isRTL': opts.numericInput
  269. });
  270. if (opts.numericInput) {
  271. isRTL = true;
  272. }
  273. var valueBuffer = actionObj["value"].split('');
  274. checkVal($el, false, true, isRTL ? valueBuffer.reverse() : valueBuffer);
  275. return isComplete(getBuffer());
  276. case "getemptymask": //TODO
  277. $el = $(actionObj["el"]);
  278. maskset = $el.data('_inputmask')['maskset'];
  279. opts = $el.data('_inputmask')['opts'];
  280. return getBufferTemplate();
  281. case "remove": //TODO
  282. var el = actionObj["el"];
  283. $el = $(el);
  284. maskset = $el.data('_inputmask')['maskset'];
  285. opts = $el.data('_inputmask')['opts'];
  286. //writeout the unmaskedvalue
  287. el._valueSet(unmaskedvalue($el));
  288. //unbind all events
  289. $el.unbind(".inputmask");
  290. $el.removeClass('focus.inputmask');
  291. //clear data
  292. $el.removeData('_inputmask');
  293. //restore the value property
  294. var valueProperty;
  295. if (Object.getOwnPropertyDescriptor)
  296. valueProperty = Object.getOwnPropertyDescriptor(el, "value");
  297. if (valueProperty && valueProperty.get) {
  298. if (el._valueGet) {
  299. Object.defineProperty(el, "value", {
  300. get: el._valueGet,
  301. set: el._valueSet
  302. });
  303. }
  304. } else if (document.__lookupGetter__ && el.__lookupGetter__("value")) {
  305. if (el._valueGet) {
  306. el.__defineGetter__("value", el._valueGet);
  307. el.__defineSetter__("value", el._valueSet);
  308. }
  309. }
  310. try { //try catch needed for IE7 as it does not supports deleting fns
  311. delete el._valueGet;
  312. delete el._valueSet;
  313. } catch (e) {
  314. el._valueGet = undefined;
  315. el._valueSet = undefined;
  316. }
  317. break;
  318. }
  319. }
  320. };
  321. $.extend($.inputmask.defaults, {
  322. //multi-masks
  323. multi: false, //do not alter - internal use
  324. nojumps: false, //do not jump over fixed parts in the mask
  325. nojumpsThreshold: 0, //start nojumps as of
  326. determineActiveMasksetIndex: undefined //override determineActiveMasksetIndex - args => eventType, elmasks - return int
  327. });
  328. $.inputmask._fn = $.fn.inputmask;
  329. $.fn.inputmask = function (fn, options) {
  330. var opts = $.extend(true, {}, $.inputmask.defaults, options);
  331. if (typeof fn === "string") {
  332. opts.mask = fn;
  333. if ($.inputmask._fn("_detectScope", opts))
  334. return $.inputmask._fn.call(this, fn, options, multiMaskScope, "_inputmask-multi");
  335. else return $.inputmask._fn.call(this, fn, options);
  336. } else if (typeof fn == "object") {
  337. opts = $.extend(true, {}, $.inputmask.defaults, fn);
  338. if ($.inputmask._fn("_detectScope", opts))
  339. return $.inputmask._fn.call(this, fn, options, multiMaskScope, "_inputmask-multi");
  340. else return $.inputmask._fn.call(this, fn, options);
  341. } else if (fn == undefined)
  342. return $.inputmask._fn.call(this, fn, options);
  343. };
  344. }
  345. })(jQuery);