validation.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. import {
  2. determineTestTemplate,
  3. getDecisionTaker,
  4. getPlaceholder,
  5. getTest,
  6. getTests,
  7. getTestTemplate
  8. } from "./validation-tests";
  9. import {keyCode, keys} from "./keycode.js";
  10. import {
  11. determineLastRequiredPosition, determineNewCaretPosition,
  12. getBuffer,
  13. getLastValidPosition,
  14. isMask,
  15. resetMaskSet,
  16. seekNext,
  17. seekPrevious
  18. } from "./positioning";
  19. import {EventHandlers} from "./eventhandlers";
  20. export {
  21. alternate,
  22. checkAlternationMatch,
  23. isComplete,
  24. isSelection,
  25. isValid,
  26. refreshFromBuffer,
  27. revalidateMask,
  28. handleRemove
  29. };
  30. //tobe put on prototype?
  31. function alternate(maskPos, c, strict, fromIsValid, rAltPos, selection) { //pos == true => generalize
  32. const inputmask = this,
  33. $ = this.dependencyLib,
  34. opts = this.opts,
  35. maskset = inputmask.maskset;
  36. var validPsClone = $.extend(true, [], maskset.validPositions),
  37. tstClone = $.extend(true, {}, maskset.tests),
  38. lastAlt,
  39. alternation,
  40. isValidRslt = false, returnRslt = false,
  41. altPos, prevAltPos, i, validPos,
  42. decisionPos,
  43. lAltPos = rAltPos !== undefined ? rAltPos : getLastValidPosition.call(inputmask), nextPos, input, begin, end;
  44. if (selection) {
  45. begin = selection.begin;
  46. end = selection.end;
  47. if (selection.begin > selection.end) {
  48. begin = selection.end;
  49. end = selection.begin;
  50. }
  51. }
  52. if (lAltPos === -1 && rAltPos === undefined) { //do not recurse when already paste the beginning
  53. lastAlt = 0;
  54. prevAltPos = getTest.call(inputmask, lastAlt);
  55. alternation = prevAltPos.alternation;
  56. } else {
  57. //find last modified alternation
  58. for (; lAltPos >= 0; lAltPos--) {
  59. altPos = maskset.validPositions[lAltPos];
  60. if (altPos && altPos.alternation !== undefined) {
  61. if (lAltPos <= (maskPos || 0) && prevAltPos && prevAltPos.locator[altPos.alternation] !== altPos.locator[altPos.alternation]) {
  62. break;
  63. }
  64. lastAlt = lAltPos;
  65. alternation = maskset.validPositions[lastAlt].alternation;
  66. prevAltPos = altPos;
  67. }
  68. }
  69. }
  70. if (alternation !== undefined) {
  71. decisionPos = parseInt(lastAlt);
  72. maskset.excludes[decisionPos] = maskset.excludes[decisionPos] || [];
  73. if (maskPos !== true) { //generalize
  74. maskset.excludes[decisionPos].push(getDecisionTaker(prevAltPos) + ":" + prevAltPos.alternation);
  75. }
  76. var validInputs = [], resultPos = -1;
  77. for (i = decisionPos; i < getLastValidPosition.call(inputmask, undefined, true) + 1; i++) {
  78. if (resultPos === -1 && maskPos <= i && c !== undefined) {
  79. validInputs.push(c);
  80. resultPos = validInputs.length - 1;
  81. }
  82. validPos = maskset.validPositions[i];
  83. if (validPos && validPos.generatedInput !== true && (selection === undefined || (i < begin || i >= end))) {
  84. validInputs.push(validPos.input);
  85. }
  86. delete maskset.validPositions[i];
  87. }
  88. if (resultPos === -1 && c !== undefined) {
  89. validInputs.push(c);
  90. resultPos = validInputs.length - 1;
  91. }
  92. while (maskset.excludes[decisionPos] !== undefined && maskset.excludes[decisionPos].length < 10) {
  93. // maskset.tests[decisionPos] = undefined; //clear decisionPos
  94. maskset.tests = {}; //clear all
  95. resetMaskSet.call(inputmask, true); //clear getbuffer
  96. isValidRslt = true;
  97. for (i = 0; i < validInputs.length; i++) {
  98. nextPos = isValidRslt.caret || (getLastValidPosition.call(inputmask, undefined, true) + 1);
  99. input = validInputs[i];
  100. // nextPos = translatePosition.call(inputmask, nextPos);
  101. if (!(isValidRslt = isValid.call(inputmask, nextPos, input, false, fromIsValid, true))) {
  102. break;
  103. }
  104. if (i === resultPos) {
  105. returnRslt = isValidRslt;
  106. }
  107. if (maskPos == true && isValidRslt) { //return validposition on generalise
  108. returnRslt = {caretPos: i};
  109. }
  110. }
  111. if (!isValidRslt) {
  112. resetMaskSet.call(inputmask);
  113. prevAltPos = getTest.call(inputmask, decisionPos); //get the current decisionPos to exclude ~ needs to be before restoring the initial validation
  114. //reset & revert
  115. maskset.validPositions = $.extend(true, [], validPsClone);
  116. maskset.tests = $.extend(true, {}, tstClone); //refresh tests after possible alternating
  117. if (maskset.excludes[decisionPos]) {
  118. var decisionTaker = getDecisionTaker(prevAltPos);
  119. if (maskset.excludes[decisionPos].indexOf(decisionTaker + ":" + prevAltPos.alternation) !== -1) {
  120. returnRslt = alternate.call(inputmask, maskPos, c, strict, fromIsValid, decisionPos - 1, selection);
  121. break;
  122. }
  123. maskset.excludes[decisionPos].push(decisionTaker + ":" + prevAltPos.alternation);
  124. for (i = decisionPos; i < getLastValidPosition.call(inputmask, undefined, true) + 1; i++) delete maskset.validPositions[i];
  125. } else { //latest alternation
  126. returnRslt = alternate.call(inputmask, maskPos, c, strict, fromIsValid, decisionPos - 1, selection);
  127. break;
  128. }
  129. } else {
  130. break;
  131. }
  132. }
  133. }
  134. //reset alternation excludes
  135. if (!returnRslt || opts.keepStatic !== false) {
  136. delete maskset.excludes[decisionPos];
  137. }
  138. return returnRslt;
  139. }
  140. function casing(elem, test, pos) {
  141. const opts = this.opts,
  142. maskset = this.maskset;
  143. switch (opts.casing || test.casing) {
  144. case "upper":
  145. elem = elem.toUpperCase();
  146. break;
  147. case "lower":
  148. elem = elem.toLowerCase();
  149. break;
  150. case "title":
  151. var posBefore = maskset.validPositions[pos - 1];
  152. if (pos === 0 || posBefore && posBefore.input === String.fromCharCode(keyCode.Space)) {
  153. elem = elem.toUpperCase();
  154. } else {
  155. elem = elem.toLowerCase();
  156. }
  157. break;
  158. default:
  159. if (typeof opts.casing === "function") {
  160. var args = Array.prototype.slice.call(arguments);
  161. args.push(maskset.validPositions);
  162. elem = opts.casing.apply(this, args);
  163. }
  164. }
  165. return elem;
  166. }
  167. //tobe put on prototype?
  168. function checkAlternationMatch(altArr1, altArr2, na) {
  169. const opts = this.opts;
  170. var altArrC = opts.greedy ? altArr2 : altArr2.slice(0, 1),
  171. isMatch = false,
  172. naArr = na !== undefined ? na.split(",") : [],
  173. naNdx;
  174. //remove no alternate indexes from alternation array
  175. for (var i = 0; i < naArr.length; i++) {
  176. if ((naNdx = altArr1.indexOf(naArr[i])) !== -1) {
  177. altArr1.splice(naNdx, 1);
  178. }
  179. }
  180. for (var alndx = 0; alndx < altArr1.length; alndx++) {
  181. if (altArrC.includes(altArr1[alndx])) {
  182. isMatch = true;
  183. break;
  184. }
  185. }
  186. return isMatch;
  187. }
  188. //tobe put on prototype?
  189. function handleRemove(input, c, pos, strict, fromIsValid) {
  190. const inputmask = this, maskset = this.maskset, opts = this.opts;
  191. if (opts.numericInput || inputmask.isRTL) {
  192. if (c === keys.Backspace) {
  193. c = keys.Delete;
  194. } else if (c === keys.Delete) {
  195. c = keys.Backspace;
  196. }
  197. if (inputmask.isRTL) {
  198. var pend = pos.end;
  199. pos.end = pos.begin;
  200. pos.begin = pend;
  201. }
  202. }
  203. var lvp = getLastValidPosition.call(inputmask, undefined, true);
  204. if (pos.end >= getBuffer.call(inputmask).length && lvp >= pos.end) { //handle numeric negate symbol offset, due to dynamic jit masking
  205. pos.end = lvp + 1;
  206. }
  207. if (c === keys.Backspace) {
  208. if ((pos.end - pos.begin < 1)) {
  209. pos.begin = seekPrevious.call(inputmask, pos.begin);
  210. }
  211. } else if (c === keys.Delete) {
  212. if (pos.begin === pos.end) {
  213. pos.end = isMask.call(inputmask, pos.end, true, true) ? pos.end + 1 : seekNext.call(inputmask, pos.end) + 1;
  214. }
  215. }
  216. var offset;
  217. if ((offset = revalidateMask.call(inputmask, pos)) !== false) {
  218. if (strict !== true && opts.keepStatic !== false || (opts.regex !== null && getTest.call(inputmask, pos.begin).match.def.indexOf("|") !== -1)) { //TODO NEEDS BETTER CHECK WHEN TO ALTERNATE ~ opts regex isn"t good enough
  219. var result = alternate.call(inputmask, true);
  220. if (result) {
  221. var newPos = result.caret !== undefined ? result.caret : (result.pos ? seekNext.call(inputmask, result.pos.begin ? result.pos.begin : result.pos) : getLastValidPosition.call(inputmask, -1, true));
  222. if (c !== keys.Delete || pos.begin > newPos) {
  223. pos.begin == newPos;
  224. }
  225. }
  226. }
  227. if (strict !== true) {
  228. maskset.p = c === keys.Delete ? pos.begin + offset : pos.begin;
  229. maskset.p = determineNewCaretPosition.call(inputmask, {
  230. begin: maskset.p,
  231. end: maskset.p
  232. }, false, opts.insertMode === false && c === keys.Backspace ? "none" : undefined).begin;
  233. }
  234. }
  235. }
  236. //tobe put on prototype?
  237. function isComplete(buffer) { //return true / false / undefined (repeat *)
  238. const inputmask = this, opts = this.opts, maskset = this.maskset;
  239. if (typeof opts.isComplete === "function") return opts.isComplete(buffer, opts);
  240. if (opts.repeat === "*") return undefined;
  241. var complete = false,
  242. lrp = determineLastRequiredPosition.call(inputmask, true),
  243. aml = seekPrevious.call(inputmask, lrp.l);
  244. if (lrp.def === undefined || lrp.def.newBlockMarker || lrp.def.optionality || lrp.def.optionalQuantifier) {
  245. complete = true;
  246. for (var i = 0; i <= aml; i++) {
  247. var test = getTestTemplate.call(inputmask, i).match;
  248. if ((test.static !== true && maskset.validPositions[i] === undefined && test.optionality !== true && test.optionalQuantifier !== true) || (test.static === true && buffer[i] !== getPlaceholder.call(inputmask, i, test))) {
  249. complete = false;
  250. break;
  251. }
  252. }
  253. }
  254. return complete;
  255. }
  256. function isSelection(posObj) {
  257. const inputmask = this,
  258. opts = this.opts, insertModeOffset = opts.insertMode ? 0 : 1;
  259. return inputmask.isRTL ? (posObj.begin - posObj.end) > insertModeOffset : (posObj.end - posObj.begin) > insertModeOffset;
  260. }
  261. //tobe put on prototype?
  262. function isValid(pos, c, strict, fromIsValid, fromAlternate, validateOnly, fromCheckval) { //strict true ~ no correction or autofill
  263. const inputmask = this,
  264. $ = this.dependencyLib,
  265. opts = this.opts,
  266. maskset = inputmask.maskset;
  267. strict = strict === true; //always set a value to strict to prevent possible strange behavior in the extensions
  268. var maskPos = pos;
  269. if (pos.begin !== undefined) { //position was a position object - used to handle a delete by typing over a selection
  270. maskPos = inputmask.isRTL ? pos.end : pos.begin;
  271. }
  272. function processCommandObject(commandObj) {
  273. if (commandObj !== undefined) {
  274. if (commandObj.remove !== undefined) { //remove position(s)
  275. if (!Array.isArray(commandObj.remove)) commandObj.remove = [commandObj.remove];
  276. commandObj.remove.sort(function (a, b) {
  277. return inputmask.isRTL ? a.pos - b.pos : b.pos - a.pos;
  278. }).forEach(function (lmnt) {
  279. revalidateMask.call(inputmask, {begin: lmnt, end: lmnt + 1});
  280. });
  281. commandObj.remove = undefined;
  282. }
  283. if (commandObj.insert !== undefined) { //insert position(s)
  284. if (!Array.isArray(commandObj.insert)) commandObj.insert = [commandObj.insert];
  285. commandObj.insert.sort(function (a, b) {
  286. return inputmask.isRTL ? b.pos - a.pos : a.pos - b.pos;
  287. }).forEach(function (lmnt) {
  288. if (lmnt.c !== "") {
  289. isValid.call(inputmask, lmnt.pos, lmnt.c, lmnt.strict !== undefined ? lmnt.strict : true, lmnt.fromIsValid !== undefined ? lmnt.fromIsValid : fromIsValid);
  290. }
  291. });
  292. commandObj.insert = undefined;
  293. }
  294. if (commandObj.refreshFromBuffer && commandObj.buffer) {
  295. var refresh = commandObj.refreshFromBuffer;
  296. refreshFromBuffer.call(inputmask, refresh === true ? refresh : refresh.start, refresh.end, commandObj.buffer);
  297. commandObj.refreshFromBuffer = undefined;
  298. }
  299. if (commandObj.rewritePosition !== undefined) {
  300. maskPos = commandObj.rewritePosition;
  301. // commandObj.rewritePosition = undefined;
  302. commandObj = true;
  303. }
  304. }
  305. return commandObj;
  306. }
  307. function _isValid(position, c, strict) {
  308. var rslt = false;
  309. getTests.call(inputmask, position).every(function (tst, ndx) {
  310. var test = tst.match;
  311. //make sure the buffer is set and correct
  312. getBuffer.call(inputmask, true);
  313. if (test.jit && maskset.validPositions[seekPrevious.call(inputmask, position)] === undefined) //ignore if jit is not desirable
  314. {
  315. rslt = false;
  316. } else {
  317. //return is false or a json object => { pos: ??, c: ??} or true
  318. rslt = test.fn != null ?
  319. test.fn.test(c, maskset, position, strict, opts, isSelection.call(inputmask, pos)) : (c === test.def || c === opts.skipOptionalPartCharacter) && test.def !== "" ? //non mask
  320. {
  321. c: getPlaceholder.call(inputmask, position, test, true) || test.def,
  322. pos: position
  323. } : false;
  324. }
  325. if (rslt !== false) {
  326. var elem = rslt.c !== undefined ? rslt.c : c, validatedPos = position;
  327. elem = (elem === opts.skipOptionalPartCharacter && test.static === true) ?
  328. (getPlaceholder.call(inputmask, position, test, true) || test.def) : elem;
  329. rslt = processCommandObject(rslt);
  330. if (rslt !== true && rslt.pos !== undefined && rslt.pos !== position) { //their is a position offset
  331. validatedPos = rslt.pos;
  332. }
  333. if (rslt !== true && rslt.pos === undefined && rslt.c === undefined) {
  334. return false; //breakout if nothing to insert
  335. }
  336. if (revalidateMask.call(inputmask, pos, $.extend({}, tst, {
  337. "input": casing.call(inputmask, elem, test, validatedPos)
  338. }), fromIsValid, validatedPos) === false) {
  339. rslt = false;
  340. }
  341. return false; //break from loop
  342. }
  343. return true;
  344. });
  345. return rslt;
  346. }
  347. var result = true,
  348. positionsClone = $.extend(true, {}, maskset.validPositions); //clone the currentPositions
  349. if (opts.keepStatic === false && maskset.excludes[maskPos] !== undefined && fromAlternate !== true && fromIsValid !== true) {
  350. for (var i = maskPos; i < (inputmask.isRTL ? pos.begin : pos.end); i++) {
  351. if (maskset.excludes[i] !== undefined) {
  352. maskset.excludes[i] = undefined;
  353. delete maskset.tests[i];
  354. }
  355. }
  356. }
  357. if (typeof opts.preValidation === "function" && fromIsValid !== true && validateOnly !== true) {
  358. result = opts.preValidation.call(inputmask, getBuffer.call(inputmask), maskPos, c, isSelection.call(inputmask, pos), opts, maskset, pos, strict || fromAlternate);
  359. result = processCommandObject(result);
  360. }
  361. if (result === true) { //preValidation result
  362. result = _isValid(maskPos, c, strict);
  363. if ((!strict || fromIsValid === true) && result === false && validateOnly !== true) {
  364. var currentPosValid = maskset.validPositions[maskPos];
  365. if (currentPosValid && currentPosValid.match.static === true && (currentPosValid.match.def === c || c === opts.skipOptionalPartCharacter)) {
  366. result = {
  367. "caret": seekNext.call(inputmask, maskPos)
  368. };
  369. } else {
  370. if (opts.insertMode || maskset.validPositions[seekNext.call(inputmask, maskPos)] === undefined || pos.end > maskPos) { //does the input match on a further position?
  371. var skip = false;
  372. if (maskset.jitOffset[maskPos] && maskset.validPositions[seekNext.call(inputmask, maskPos)] === undefined) {
  373. result = isValid.call(inputmask, maskPos + maskset.jitOffset[maskPos], c, true, true);
  374. if (result !== false) {
  375. if (fromAlternate !== true) result.caret = maskPos;
  376. skip = true;
  377. }
  378. }
  379. if (pos.end > maskPos) {
  380. maskset.validPositions[maskPos] = undefined;
  381. }
  382. if (!skip && !isMask.call(inputmask, maskPos, opts.keepStatic && maskPos === 0)) {
  383. for (var nPos = maskPos + 1, snPos = seekNext.call(inputmask, maskPos, false, maskPos !== 0); nPos <= snPos; nPos++) {
  384. // if (!isMask(nPos, true)) {
  385. // continue;
  386. // }
  387. result = _isValid(nPos, c, strict);
  388. if (result !== false) {
  389. result = trackbackPositions.call(inputmask, maskPos, result.pos !== undefined ? result.pos : nPos) || result;
  390. maskPos = nPos;
  391. break;
  392. }
  393. }
  394. }
  395. }
  396. }
  397. }
  398. if (inputmask.hasAlternator && fromAlternate !== true && !strict) {
  399. if (result === false && opts.keepStatic && (isComplete.call(inputmask, getBuffer.call(inputmask)) || maskPos === 0)) { //try fuzzy alternator logic
  400. result = alternate.call(inputmask, maskPos, c, strict, fromIsValid, undefined, pos);
  401. } else if (isSelection.call(inputmask, pos) && maskset.tests[maskPos] && maskset.tests[maskPos].length > 1 && opts.keepStatic) { //selection clears an alternated keepstatic mask ~ #2189
  402. result = alternate.call(inputmask, true);
  403. } else if (result == true && opts.numericInput !== true && maskset.tests[maskPos] && maskset.tests[maskPos].length > 1 && getLastValidPosition.call(inputmask, undefined, true) > maskPos) {
  404. result = alternate.call(inputmask, true);
  405. }
  406. }
  407. if (result === true) {
  408. result = {
  409. "pos": maskPos
  410. };
  411. }
  412. }
  413. if (typeof opts.postValidation === "function" && fromIsValid !== true && validateOnly !== true) {
  414. var postResult = opts.postValidation.call(inputmask, getBuffer.call(inputmask, true), pos.begin !== undefined ? (inputmask.isRTL ? pos.end : pos.begin) : pos, c, result, opts, maskset, strict, fromCheckval);
  415. if (postResult !== undefined) {
  416. result = postResult === true ? result : postResult;
  417. }
  418. }
  419. if (result && result.pos === undefined) {
  420. result.pos = maskPos;
  421. }
  422. if (result === false || validateOnly === true) {
  423. resetMaskSet.call(inputmask, true);
  424. maskset.validPositions = $.extend(true, [], positionsClone); //revert validation changes
  425. } else {
  426. trackbackPositions.call(inputmask, undefined, maskPos, true);
  427. }
  428. var endResult = processCommandObject(result);
  429. // console.log("returned result " + JSON.stringify(endResult));
  430. if (inputmask.maxLength !== undefined) {
  431. var buffer = getBuffer.call(inputmask);
  432. if (buffer.length > inputmask.maxLength && !fromIsValid) {
  433. resetMaskSet.call(inputmask, true);
  434. maskset.validPositions = $.extend(true, [], positionsClone); //revert validation changes
  435. endResult = false;
  436. }
  437. }
  438. return endResult;
  439. }
  440. //tobe put on prototype?
  441. function positionCanMatchDefinition(pos, testDefinition, opts) {
  442. const inputmask = this,
  443. maskset = this.maskset;
  444. var valid = false,
  445. tests = getTests.call(inputmask, pos);
  446. for (var tndx = 0; tndx < tests.length; tndx++) {
  447. if (tests[tndx].match &&
  448. ((tests[tndx].match["nativeDef"] === testDefinition.match[opts.shiftPositions ? "def" : "nativeDef"] && (!opts.shiftPositions || !testDefinition.match.static)) ||
  449. tests[tndx].match["nativeDef"] === testDefinition.match["nativeDef"] ||
  450. (opts.regex && !tests[tndx].match.static && tests[tndx].match.fn.test(testDefinition.input, maskset, pos, false, opts)))) {
  451. valid = true;
  452. break;
  453. } else if (tests[tndx].match && tests[tndx].match["def"] === testDefinition.match["nativeDef"]) {
  454. valid = undefined;
  455. break;
  456. }
  457. }
  458. if (valid === false) {
  459. if (maskset.jitOffset[pos] !== undefined) {
  460. valid = positionCanMatchDefinition.call(inputmask, pos + maskset.jitOffset[pos], testDefinition, opts);
  461. }
  462. }
  463. return valid;
  464. }
  465. //tobe put on prototype?
  466. function refreshFromBuffer(start, end, buffer) {
  467. const inputmask = this,
  468. maskset = this.maskset,
  469. opts = this.opts,
  470. $ = this.dependencyLib;
  471. // checkVal.call(inputmask, el, false, true, isRTL ? buffer.reverse() : buffer);
  472. var i, p, skipOptionalPartCharacter = opts.skipOptionalPartCharacter,
  473. bffr = inputmask.isRTL ? buffer.slice().reverse() : buffer;
  474. opts.skipOptionalPartCharacter = "";
  475. if (start === true) {
  476. resetMaskSet.call(inputmask);
  477. maskset.tests = {}; //refresh tests after possible alternating
  478. start = 0;
  479. end = buffer.length;
  480. p = determineNewCaretPosition.call(inputmask, {begin: 0, end: 0}, false).begin;
  481. } else {
  482. for (i = start; i < end; i++) {
  483. delete maskset.validPositions[i];
  484. }
  485. p = start;
  486. }
  487. var keypress = new $.Event("keypress");
  488. for (i = start; i < end; i++) {
  489. keypress.key = bffr[i].toString();
  490. inputmask.ignorable = false; //make sure ignorable is ignored ;-)
  491. var valResult = EventHandlers.keypressEvent.call(inputmask, keypress, true, false, false, p);
  492. if (valResult !== false && valResult !== undefined) {
  493. p = valResult.forwardPosition;
  494. }
  495. }
  496. opts.skipOptionalPartCharacter = skipOptionalPartCharacter;
  497. }
  498. //tobe put on prototype?
  499. //fill in best positions according the current input
  500. function trackbackPositions(originalPos, newPos, fillOnly) {
  501. const inputmask = this,
  502. maskset = this.maskset,
  503. $ = this.dependencyLib;
  504. // console.log("trackbackPositions " + originalPos + " " + newPos);
  505. if (originalPos === undefined) {
  506. //find previous valid
  507. for (originalPos = newPos - 1; originalPos > 0; originalPos--) {
  508. if (maskset.validPositions[originalPos]) break;
  509. }
  510. }
  511. for (var ps = originalPos; ps < newPos; ps++) {
  512. if (maskset.validPositions[ps] === undefined && !isMask.call(inputmask, ps, false)) {
  513. var vp = ps == 0 ? getTest.call(inputmask, ps) : maskset.validPositions[ps - 1];
  514. if (vp) {
  515. var tests = getTests.call(inputmask, ps).slice();
  516. if (tests[tests.length - 1].match.def === "") tests.pop();
  517. var bestMatch = determineTestTemplate.call(inputmask, ps, tests), np;
  518. if (bestMatch && (bestMatch.match.jit !== true || (bestMatch.match.newBlockMarker === "master" && (np = maskset.validPositions[ps + 1]) && np.match.optionalQuantifier === true))) {
  519. bestMatch = $.extend({}, bestMatch, {
  520. "input": getPlaceholder.call(inputmask, ps, bestMatch.match, true) || bestMatch.match.def
  521. });
  522. bestMatch.generatedInput = true;
  523. revalidateMask.call(inputmask, ps, bestMatch, true);
  524. if (fillOnly !== true) {
  525. //revalidate the new position to update the locator value
  526. var cvpInput = maskset.validPositions[newPos].input;
  527. maskset.validPositions[newPos] = undefined;
  528. return isValid.call(inputmask, newPos, cvpInput, true, true);
  529. }
  530. }
  531. }
  532. }
  533. }
  534. }
  535. //tobe put on prototype?
  536. function revalidateMask(pos, validTest, fromIsValid, validatedPos) {
  537. const inputmask = this,
  538. maskset = this.maskset,
  539. opts = this.opts,
  540. $ = this.dependencyLib;
  541. function IsEnclosedStatic(pos, valids, selection) {
  542. var posMatch = valids[pos];
  543. if (posMatch !== undefined && posMatch.match.static === true && posMatch.match.optionality !== true && (valids[0] === undefined || valids[0].alternation === undefined)) {
  544. var prevMatch = selection.begin <= pos - 1 ? valids[pos - 1] && valids[pos - 1].match.static === true && valids[pos - 1] : valids[pos - 1],
  545. nextMatch = selection.end > pos + 1 ? valids[pos + 1] && valids[pos + 1].match.static === true && valids[pos + 1] : valids[pos + 1];
  546. return prevMatch && nextMatch;
  547. }
  548. return false;
  549. }
  550. var offset = 0, begin = pos.begin !== undefined ? pos.begin : pos, end = pos.end !== undefined ? pos.end : pos,
  551. valid = true;
  552. if (pos.begin > pos.end) {
  553. begin = pos.end;
  554. end = pos.begin;
  555. }
  556. validatedPos = validatedPos !== undefined ? validatedPos : begin;
  557. if (fromIsValid === undefined && (begin !== end || (opts.insertMode && maskset.validPositions[validatedPos] !== undefined) || validTest === undefined || validTest.match.optionalQuantifier || validTest.match.optionality)) {
  558. //reposition & revalidate others
  559. var positionsClone = $.extend(true, {}, maskset.validPositions),
  560. lvp = getLastValidPosition.call(inputmask, undefined, true),
  561. i;
  562. maskset.p = begin; //needed for alternated position after overtype selection
  563. for (i = lvp; i >= begin; i--) {
  564. delete maskset.validPositions[i];
  565. if (validTest === undefined) delete maskset.tests[i + 1];
  566. }
  567. var j = validatedPos,
  568. posMatch = j, t, canMatch, test;
  569. if (validTest) {
  570. maskset.validPositions[validatedPos] = $.extend(true, {}, validTest);
  571. posMatch++;
  572. j++;
  573. }
  574. for (i = validTest ? end : end - 1; i <= lvp; i++) {
  575. if ((t = positionsClone[i]) !== undefined && t.generatedInput !== true &&
  576. (i >= end || (i >= begin && IsEnclosedStatic(i, positionsClone, {
  577. begin: begin,
  578. end: end
  579. })))) {
  580. while (test = getTest.call(inputmask, posMatch), test.match.def !== "") { //loop needed to match further positions
  581. if ((canMatch = positionCanMatchDefinition.call(inputmask, posMatch, t, opts)) !== false || t.match.def === "+") { //validated match //we still need some hackery for the + validator (numeric alias)
  582. if (t.match.def === "+") getBuffer.call(inputmask, true);
  583. var result = isValid.call(inputmask, posMatch, t.input, t.match.def !== "+", /*t.match.def !== "+"*/ true);
  584. valid = result !== false;
  585. j = (result.pos || posMatch) + 1;
  586. if (!valid && canMatch) break;
  587. } else {
  588. valid = false;
  589. }
  590. if (valid) {
  591. if (validTest === undefined && t.match.static && i === pos.begin) offset++;
  592. break;
  593. }
  594. if (!valid && getBuffer.call(inputmask), posMatch > maskset.maskLength) {
  595. break;
  596. }
  597. posMatch++;
  598. }
  599. if (getTest.call(inputmask, posMatch).match.def == "") {
  600. valid = false;
  601. }
  602. //restore position
  603. posMatch = j;
  604. }
  605. if (!valid) break;
  606. }
  607. if (!valid) {
  608. maskset.validPositions = $.extend(true, [], positionsClone);
  609. resetMaskSet.call(inputmask, true);
  610. return false;
  611. }
  612. } else if (validTest && getTest.call(inputmask, validatedPos).match.cd === validTest.match.cd) {
  613. maskset.validPositions[validatedPos] = $.extend(true, {}, validTest);
  614. }
  615. resetMaskSet.call(inputmask, true);
  616. return offset;
  617. }