validation.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. import { EventHandlers } from "./eventhandlers";
  2. import { keyCode, keys } from "./keycode.js";
  3. import {
  4. determineLastRequiredPosition,
  5. determineNewCaretPosition,
  6. getBuffer,
  7. getLastValidPosition,
  8. isMask,
  9. resetMaskSet,
  10. seekNext,
  11. seekPrevious
  12. } from "./positioning";
  13. import {
  14. determineTestTemplate,
  15. getDecisionTaker,
  16. getPlaceholder,
  17. getTest,
  18. getTests,
  19. getTestTemplate
  20. } from "./validation-tests";
  21. export {
  22. alternate,
  23. checkAlternationMatch,
  24. isComplete,
  25. isSelection,
  26. isValid,
  27. refreshFromBuffer,
  28. revalidateMask,
  29. handleRemove
  30. };
  31. // tobe put on prototype?
  32. function alternate(maskPos, c, strict, fromIsValid, rAltPos, selection) {
  33. // pos == true => generalize
  34. const inputmask = this,
  35. $ = this.dependencyLib,
  36. opts = this.opts,
  37. maskset = inputmask.maskset;
  38. if (!inputmask.hasAlternator) return false;
  39. const validPsClone = $.extend(true, [], maskset.validPositions),
  40. tstClone = $.extend(true, {}, maskset.tests);
  41. let lastAlt,
  42. alternation,
  43. isValidRslt = false,
  44. returnRslt = false,
  45. altPos,
  46. prevAltPos,
  47. i,
  48. validPos,
  49. decisionPos,
  50. lAltPos =
  51. rAltPos !== undefined ? rAltPos : getLastValidPosition.call(inputmask),
  52. nextPos,
  53. input,
  54. begin,
  55. end;
  56. if (selection) {
  57. begin = selection.begin;
  58. end = selection.end;
  59. if (selection.begin > selection.end) {
  60. begin = selection.end;
  61. end = selection.begin;
  62. }
  63. }
  64. if (lAltPos === -1 && rAltPos === undefined) {
  65. // do not recurse when already passed the beginning
  66. lastAlt = 0;
  67. prevAltPos = getTest.call(inputmask, lastAlt);
  68. alternation = prevAltPos.alternation;
  69. } else {
  70. // find last modified alternation
  71. for (; lAltPos >= 0; lAltPos--) {
  72. altPos =
  73. lAltPos === 0
  74. ? getTest.call(inputmask, 0)
  75. : maskset.validPositions[lAltPos];
  76. if (altPos && altPos.alternation !== undefined) {
  77. if (
  78. lAltPos <= (maskPos || 0) &&
  79. prevAltPos &&
  80. prevAltPos.locator[altPos.alternation] !==
  81. altPos.locator[altPos.alternation]
  82. ) {
  83. break;
  84. }
  85. lastAlt = lAltPos;
  86. alternation = altPos.alternation;
  87. prevAltPos = altPos;
  88. }
  89. }
  90. }
  91. if (alternation !== undefined) {
  92. decisionPos = parseInt(lastAlt);
  93. maskset.excludes[decisionPos] = maskset.excludes[decisionPos] || [];
  94. // generalize
  95. if (maskPos !== true) {
  96. maskset.excludes[decisionPos].push(
  97. getDecisionTaker(prevAltPos) + ":" + prevAltPos.alternation
  98. );
  99. }
  100. const validInputs = [];
  101. let resultPos = -1;
  102. for (
  103. i = decisionPos;
  104. decisionPos < getLastValidPosition.call(inputmask, undefined, true) + 1;
  105. i++
  106. ) {
  107. if (resultPos === -1 && maskPos <= i && c !== undefined) {
  108. validInputs.push(c);
  109. resultPos = validInputs.length - 1;
  110. }
  111. validPos = maskset.validPositions[decisionPos];
  112. if (
  113. validPos &&
  114. validPos.generatedInput !== true &&
  115. (decisionPos !== 0 ||
  116. validPos.input !== opts.skipOptionalPartCharacter) &&
  117. (selection === undefined || i < begin || i >= end)
  118. ) {
  119. validInputs.push(validPos.input);
  120. }
  121. // delete maskset.validPositions[i++];
  122. maskset.validPositions.splice(decisionPos, 1);
  123. }
  124. if (resultPos === -1 && c !== undefined) {
  125. validInputs.push(c);
  126. resultPos = validInputs.length - 1;
  127. }
  128. while (
  129. maskset.excludes[decisionPos] !== undefined &&
  130. maskset.excludes[decisionPos].length < 10
  131. ) {
  132. // maskset.tests[decisionPos] = undefined; //clear decisionPos
  133. maskset.tests = {}; // clear all
  134. resetMaskSet.call(inputmask, true); // clear getbuffer
  135. isValidRslt = true;
  136. for (i = 0; i < validInputs.length; i++) {
  137. nextPos =
  138. isValidRslt.caret ||
  139. (opts.insertMode == false && nextPos != undefined)
  140. ? seekNext.call(inputmask, nextPos)
  141. : getLastValidPosition.call(inputmask, undefined, true) + 1;
  142. input = validInputs[i];
  143. // nextPos = translatePosition.call(inputmask, nextPos);
  144. if (
  145. !(isValidRslt = isValid.call(
  146. inputmask,
  147. nextPos,
  148. input,
  149. false,
  150. fromIsValid,
  151. true
  152. ))
  153. ) {
  154. break;
  155. }
  156. if (i === resultPos) {
  157. returnRslt = isValidRslt;
  158. }
  159. if (maskPos == true && isValidRslt) {
  160. // return validposition on generalise
  161. returnRslt = { caretPos: i };
  162. }
  163. }
  164. if (!isValidRslt) {
  165. resetMaskSet.call(inputmask);
  166. prevAltPos = getTest.call(inputmask, decisionPos); // get the current decisionPos to exclude ~ needs to be before restoring the initial validation
  167. // reset & revert
  168. maskset.validPositions = $.extend(true, [], validPsClone);
  169. maskset.tests = $.extend(true, {}, tstClone); // refresh tests after possible alternating
  170. if (maskset.excludes[decisionPos]) {
  171. if (prevAltPos.alternation != undefined) {
  172. const decisionTaker = getDecisionTaker(prevAltPos);
  173. if (
  174. maskset.excludes[decisionPos].indexOf(
  175. decisionTaker + ":" + prevAltPos.alternation
  176. ) !== -1
  177. ) {
  178. returnRslt = alternate.call(
  179. inputmask,
  180. maskPos,
  181. c,
  182. strict,
  183. fromIsValid,
  184. decisionPos - 1,
  185. selection
  186. );
  187. break;
  188. }
  189. maskset.excludes[decisionPos].push(
  190. decisionTaker + ":" + prevAltPos.alternation
  191. );
  192. for (
  193. i = decisionPos;
  194. i < getLastValidPosition.call(inputmask, undefined, true) + 1;
  195. i++
  196. )
  197. maskset.validPositions.splice(decisionPos);
  198. } else delete maskset.excludes[decisionPos];
  199. } else {
  200. // latest alternation
  201. returnRslt = alternate.call(
  202. inputmask,
  203. maskPos,
  204. c,
  205. strict,
  206. fromIsValid,
  207. decisionPos - 1,
  208. selection
  209. );
  210. break;
  211. }
  212. } else {
  213. break;
  214. }
  215. }
  216. }
  217. // reset alternation excludes
  218. if (!returnRslt || opts.keepStatic !== false) {
  219. delete maskset.excludes[decisionPos];
  220. }
  221. return returnRslt;
  222. }
  223. function casing(elem, test, pos) {
  224. const opts = this.opts,
  225. maskset = this.maskset;
  226. switch (opts.casing || test.casing) {
  227. case "upper":
  228. elem = elem.toUpperCase();
  229. break;
  230. case "lower":
  231. elem = elem.toLowerCase();
  232. break;
  233. case "title":
  234. var posBefore = maskset.validPositions[pos - 1];
  235. if (
  236. pos === 0 ||
  237. (posBefore && posBefore.input === String.fromCharCode(keyCode.Space))
  238. ) {
  239. elem = elem.toUpperCase();
  240. } else {
  241. elem = elem.toLowerCase();
  242. }
  243. break;
  244. default:
  245. if (typeof opts.casing === "function") {
  246. const args = Array.prototype.slice.call(arguments);
  247. args.push(maskset.validPositions);
  248. elem = opts.casing.apply(this, args);
  249. }
  250. }
  251. return elem;
  252. }
  253. // tobe put on prototype?
  254. function checkAlternationMatch(altArr1, altArr2, na) {
  255. const opts = this.opts;
  256. let altArrC = opts.greedy ? altArr2 : altArr2.slice(0, 1),
  257. isMatch = false,
  258. naArr = na !== undefined ? na.split(",") : [],
  259. naNdx;
  260. // remove no alternate indexes from alternation array
  261. for (let i = 0; i < naArr.length; i++) {
  262. if ((naNdx = altArr1.indexOf(naArr[i])) !== -1) {
  263. altArr1.splice(naNdx, 1);
  264. }
  265. }
  266. for (let alndx = 0; alndx < altArr1.length; alndx++) {
  267. if (altArrC.includes(altArr1[alndx])) {
  268. isMatch = true;
  269. break;
  270. }
  271. }
  272. return isMatch;
  273. }
  274. // tobe put on prototype?
  275. function handleRemove(input, c, pos, strict, fromIsValid) {
  276. const inputmask = this,
  277. maskset = this.maskset,
  278. opts = this.opts;
  279. if (opts.numericInput || inputmask.isRTL) {
  280. if (c === keys.Backspace) {
  281. c = keys.Delete;
  282. } else if (c === keys.Delete) {
  283. c = keys.Backspace;
  284. }
  285. if (inputmask.isRTL) {
  286. const pend = pos.end;
  287. pos.end = pos.begin;
  288. pos.begin = pend;
  289. }
  290. }
  291. const lvp = getLastValidPosition.call(inputmask, undefined, true);
  292. if (pos.end >= getBuffer.call(inputmask).length && lvp >= pos.end) {
  293. // handle numeric negate symbol offset, due to dynamic jit masking
  294. pos.end = lvp + 1;
  295. }
  296. if (c === keys.Backspace) {
  297. if (pos.end - pos.begin < 1) {
  298. pos.begin = seekPrevious.call(inputmask, pos.begin);
  299. }
  300. } else if (c === keys.Delete) {
  301. if (pos.begin === pos.end) {
  302. pos.end = isMask.call(inputmask, pos.end, true, true)
  303. ? pos.end + 1
  304. : seekNext.call(inputmask, pos.end) + 1;
  305. }
  306. }
  307. let offset;
  308. if ((offset = revalidateMask.call(inputmask, pos)) !== false) {
  309. if (
  310. (strict !== true && opts.keepStatic !== false) ||
  311. (opts.regex !== null &&
  312. getTest.call(inputmask, pos.begin).match.def.indexOf("|") !== -1)
  313. ) {
  314. // TODO NEEDS BETTER CHECK WHEN TO ALTERNATE ~ opts regex isn"t good enough
  315. alternate.call(inputmask, true);
  316. }
  317. if (strict !== true) {
  318. maskset.p = c === keys.Delete ? pos.begin + offset : pos.begin;
  319. maskset.p = determineNewCaretPosition.call(
  320. inputmask,
  321. {
  322. begin: maskset.p,
  323. end: maskset.p
  324. },
  325. false,
  326. opts.insertMode === false && c === keys.Backspace ? "none" : undefined
  327. ).begin;
  328. }
  329. }
  330. }
  331. // tobe put on prototype?
  332. function isComplete(buffer) {
  333. // return true / false / undefined (repeat *)
  334. const inputmask = this,
  335. opts = this.opts,
  336. maskset = this.maskset;
  337. if (typeof opts.isComplete === "function")
  338. return opts.isComplete(buffer, opts);
  339. if (opts.repeat === "*") return undefined;
  340. let complete = false,
  341. lrp = determineLastRequiredPosition.call(inputmask, true),
  342. aml = lrp.l; // seekPrevious.call(inputmask, lrp.l);
  343. if (
  344. lrp.def === undefined ||
  345. lrp.def.newBlockMarker ||
  346. lrp.def.optionality ||
  347. lrp.def.optionalQuantifier
  348. ) {
  349. complete = true;
  350. for (let i = 0; i <= aml; i++) {
  351. const test = getTestTemplate.call(inputmask, i).match;
  352. if (
  353. (test.static !== true &&
  354. maskset.validPositions[i] === undefined &&
  355. (test.optionality === false ||
  356. test.optionality === undefined ||
  357. (test.optionality && test.newBlockMarker == false)) &&
  358. (test.optionalQuantifier === false ||
  359. test.optionalQuantifier === undefined)) ||
  360. (test.static === true &&
  361. test.def != "" &&
  362. buffer[i] !== getPlaceholder.call(inputmask, i, test))
  363. ) {
  364. complete = false;
  365. break;
  366. }
  367. }
  368. }
  369. return complete;
  370. }
  371. function isSelection(posObj) {
  372. const inputmask = this,
  373. opts = this.opts,
  374. insertModeOffset = opts.insertMode ? 0 : 1;
  375. return inputmask.isRTL
  376. ? posObj.begin - posObj.end > insertModeOffset
  377. : posObj.end - posObj.begin > insertModeOffset;
  378. }
  379. // tobe put on prototype?
  380. function isValid(
  381. pos,
  382. c,
  383. strict,
  384. fromIsValid,
  385. fromAlternate,
  386. validateOnly,
  387. fromCheckval
  388. ) {
  389. // strict true ~ no correction or autofill
  390. const inputmask = this,
  391. $ = this.dependencyLib,
  392. opts = this.opts,
  393. maskset = inputmask.maskset;
  394. strict = strict === true; // always set a value to strict to prevent possible strange behavior in the extensions
  395. let maskPos = pos;
  396. if (pos.begin !== undefined) {
  397. // position was a position object - used to handle a delete by typing over a selection
  398. maskPos = inputmask.isRTL ? pos.end : pos.begin;
  399. }
  400. function processCommandObject(commandObj) {
  401. if (commandObj !== undefined) {
  402. if (commandObj.remove !== undefined) {
  403. // remove position(s)
  404. if (!Array.isArray(commandObj.remove))
  405. commandObj.remove = [commandObj.remove];
  406. commandObj.remove
  407. .sort(function (a, b) {
  408. return inputmask.isRTL ? a.pos - b.pos : b.pos - a.pos;
  409. })
  410. .forEach(function (lmnt) {
  411. revalidateMask.call(inputmask, { begin: lmnt, end: lmnt + 1 });
  412. });
  413. commandObj.remove = undefined;
  414. }
  415. if (commandObj.insert !== undefined) {
  416. // insert position(s)
  417. if (!Array.isArray(commandObj.insert))
  418. commandObj.insert = [commandObj.insert];
  419. commandObj.insert
  420. .sort(function (a, b) {
  421. return inputmask.isRTL ? b.pos - a.pos : a.pos - b.pos;
  422. })
  423. .forEach(function (lmnt) {
  424. if (lmnt.c !== "") {
  425. isValid.call(
  426. inputmask,
  427. lmnt.pos,
  428. lmnt.c,
  429. lmnt.strict !== undefined ? lmnt.strict : true,
  430. lmnt.fromIsValid !== undefined ? lmnt.fromIsValid : fromIsValid
  431. );
  432. }
  433. });
  434. commandObj.insert = undefined;
  435. }
  436. if (commandObj.refreshFromBuffer && commandObj.buffer) {
  437. const refresh = commandObj.refreshFromBuffer;
  438. refreshFromBuffer.call(
  439. inputmask,
  440. refresh === true ? refresh : refresh.start,
  441. refresh.end,
  442. commandObj.buffer
  443. );
  444. commandObj.refreshFromBuffer = undefined;
  445. }
  446. if (commandObj.rewritePosition !== undefined) {
  447. maskPos = commandObj.rewritePosition;
  448. // commandObj.rewritePosition = undefined;
  449. commandObj = true; // see prevalidation in isValid
  450. }
  451. }
  452. return commandObj;
  453. }
  454. function _isValid(position, c, strict) {
  455. let rslt = false;
  456. getTests.call(inputmask, position).every(function (tst, ndx) {
  457. const test = tst.match;
  458. // make sure the buffer is set and correct
  459. getBuffer.call(inputmask, true);
  460. if (
  461. test.jit &&
  462. maskset.validPositions[seekPrevious.call(inputmask, position)] ===
  463. undefined
  464. ) {
  465. // ignore if jit is not desirable
  466. rslt = false;
  467. } else {
  468. // return is false or a json object => { pos: ??, c: ??} or true
  469. rslt =
  470. test.fn != null
  471. ? test.fn.test(
  472. c,
  473. maskset,
  474. position,
  475. strict,
  476. opts,
  477. isSelection.call(inputmask, pos)
  478. )
  479. : (c === test.def || c === opts.skipOptionalPartCharacter) &&
  480. test.def !== "" // non mask
  481. ? {
  482. c:
  483. getPlaceholder.call(inputmask, position, test, true) ||
  484. test.def,
  485. pos: position
  486. }
  487. : false;
  488. }
  489. if (rslt !== false) {
  490. let elem = rslt.c !== undefined ? rslt.c : c,
  491. validatedPos = position;
  492. elem =
  493. elem === opts.skipOptionalPartCharacter && test.static === true
  494. ? getPlaceholder.call(inputmask, position, test, true) || test.def
  495. : elem;
  496. rslt = processCommandObject(rslt);
  497. if (rslt !== true && rslt.pos !== undefined && rslt.pos !== position) {
  498. // their is a position offset
  499. validatedPos = rslt.pos;
  500. }
  501. if (rslt !== true && rslt.pos === undefined && rslt.c === undefined) {
  502. return false; // breakout if nothing to insert
  503. }
  504. if (
  505. revalidateMask.call(
  506. inputmask,
  507. pos,
  508. $.extend({}, tst, {
  509. input: casing.call(inputmask, elem, test, validatedPos)
  510. }),
  511. fromIsValid,
  512. validatedPos
  513. ) === false
  514. ) {
  515. rslt = false;
  516. }
  517. return false; // break from loop
  518. }
  519. return true;
  520. });
  521. return rslt;
  522. }
  523. let result = true,
  524. positionsClone = $.extend(true, [], maskset.validPositions); // clone the currentPositions
  525. if (
  526. opts.keepStatic === false &&
  527. maskset.excludes[maskPos] !== undefined &&
  528. fromAlternate !== true &&
  529. fromIsValid !== true
  530. ) {
  531. for (let i = maskPos; i < (inputmask.isRTL ? pos.begin : pos.end); i++) {
  532. if (maskset.excludes[i] !== undefined) {
  533. maskset.excludes[i] = undefined;
  534. delete maskset.tests[i];
  535. }
  536. }
  537. }
  538. if (
  539. typeof opts.preValidation === "function" &&
  540. fromIsValid !== true &&
  541. validateOnly !== true
  542. ) {
  543. result = opts.preValidation.call(
  544. inputmask,
  545. getBuffer.call(inputmask),
  546. maskPos,
  547. c,
  548. isSelection.call(inputmask, pos),
  549. opts,
  550. maskset,
  551. pos,
  552. strict || fromAlternate
  553. );
  554. result = processCommandObject(result);
  555. }
  556. if (result === true) {
  557. // preValidation result
  558. result = _isValid(maskPos, c, strict);
  559. if (
  560. (!strict || fromIsValid === true) &&
  561. result === false &&
  562. validateOnly !== true
  563. ) {
  564. const currentPosValid = maskset.validPositions[maskPos];
  565. if (
  566. currentPosValid &&
  567. currentPosValid.match.static === true &&
  568. (currentPosValid.match.def === c ||
  569. c === opts.skipOptionalPartCharacter)
  570. ) {
  571. result = {
  572. caret: seekNext.call(inputmask, maskPos)
  573. };
  574. } else {
  575. if (
  576. opts.insertMode ||
  577. maskset.validPositions[seekNext.call(inputmask, maskPos)] ===
  578. undefined ||
  579. pos.end > maskPos
  580. ) {
  581. // does the input match on a further position?
  582. let skip = false;
  583. if (
  584. maskset.jitOffset[maskPos] &&
  585. maskset.validPositions[seekNext.call(inputmask, maskPos)] ===
  586. undefined
  587. ) {
  588. result = isValid.call(
  589. inputmask,
  590. maskPos + maskset.jitOffset[maskPos],
  591. c,
  592. true,
  593. true
  594. );
  595. if (result !== false) {
  596. if (fromAlternate !== true) result.caret = maskPos;
  597. skip = true;
  598. }
  599. }
  600. if (pos.end > maskPos) {
  601. maskset.validPositions[maskPos] = undefined;
  602. }
  603. if (
  604. !skip &&
  605. !isMask.call(inputmask, maskPos, opts.keepStatic && maskPos === 0)
  606. ) {
  607. for (
  608. let nPos = maskPos + 1,
  609. snPos = seekNext.call(inputmask, maskPos, false, maskPos !== 0);
  610. nPos <= snPos;
  611. nPos++
  612. ) {
  613. // if (!isMask(nPos, true)) {
  614. // continue;
  615. // }
  616. result = _isValid(nPos, c, strict);
  617. if (result !== false) {
  618. result =
  619. trackbackPositions.call(
  620. inputmask,
  621. maskPos,
  622. result.pos !== undefined ? result.pos : nPos
  623. ) || result;
  624. maskPos = nPos;
  625. break;
  626. }
  627. }
  628. }
  629. }
  630. }
  631. }
  632. if (inputmask.hasAlternator && fromAlternate !== true && !strict) {
  633. fromAlternate = true; // stop possible loop
  634. if (
  635. result === false &&
  636. opts.keepStatic &&
  637. (isComplete.call(inputmask, getBuffer.call(inputmask)) || maskPos === 0)
  638. ) {
  639. // try fuzzy alternator logic
  640. result = alternate.call(
  641. inputmask,
  642. maskPos,
  643. c,
  644. strict,
  645. fromIsValid,
  646. undefined,
  647. pos
  648. );
  649. } else if (
  650. isSelection.call(inputmask, pos) &&
  651. maskset.tests[maskPos] &&
  652. maskset.tests[maskPos].length > 1 &&
  653. opts.keepStatic
  654. ) {
  655. // selection clears an alternated keepstatic mask ~ #2189
  656. result = alternate.call(inputmask, true) || result;
  657. } else if (
  658. result === true &&
  659. opts.numericInput !== true &&
  660. maskset.tests[maskPos] &&
  661. maskset.tests[maskPos].length > 1 &&
  662. getLastValidPosition.call(inputmask, undefined, true) > maskPos
  663. ) {
  664. // console.log("Alternating");
  665. result = alternate.call(inputmask, true) || result;
  666. }
  667. }
  668. if (result === true) {
  669. result = {
  670. pos: maskPos
  671. };
  672. }
  673. if (
  674. typeof opts.postValidation === "function" &&
  675. fromIsValid !== true &&
  676. validateOnly !== true
  677. ) {
  678. const postResult = opts.postValidation.call(
  679. inputmask,
  680. getBuffer.call(inputmask, true),
  681. pos.begin !== undefined ? (inputmask.isRTL ? pos.end : pos.begin) : pos,
  682. c,
  683. result,
  684. opts,
  685. maskset,
  686. strict,
  687. fromCheckval,
  688. fromAlternate
  689. );
  690. if (postResult !== undefined) {
  691. result = postResult === true ? result : postResult;
  692. }
  693. }
  694. }
  695. if (result && result.pos === undefined) {
  696. result.pos = maskPos;
  697. }
  698. if (result === false || validateOnly === true) {
  699. resetMaskSet.call(inputmask, true);
  700. maskset.validPositions = $.extend(true, [], positionsClone); // revert validation changes
  701. } else {
  702. trackbackPositions.call(inputmask, undefined, maskPos, true);
  703. }
  704. let endResult = processCommandObject(result);
  705. // console.log("returned result " + JSON.stringify(endResult));
  706. if (inputmask.maxLength !== undefined) {
  707. const buffer = getBuffer.call(inputmask);
  708. if (buffer.length > inputmask.maxLength && !fromIsValid) {
  709. resetMaskSet.call(inputmask, true);
  710. maskset.validPositions = $.extend(true, [], positionsClone); // revert validation changes
  711. endResult = false;
  712. }
  713. }
  714. return endResult;
  715. }
  716. // tobe put on prototype?
  717. function positionCanMatchDefinition(pos, testDefinition, opts) {
  718. const inputmask = this,
  719. maskset = this.maskset;
  720. let valid = false,
  721. tests = getTests.call(inputmask, pos);
  722. for (let tndx = 0; tndx < tests.length; tndx++) {
  723. if (
  724. tests[tndx].match &&
  725. ((tests[tndx].match.nativeDef ===
  726. testDefinition.match[opts.shiftPositions ? "def" : "nativeDef"] &&
  727. (!opts.shiftPositions || !testDefinition.match.static)) ||
  728. tests[tndx].match.nativeDef === testDefinition.match.nativeDef ||
  729. (opts.regex &&
  730. !tests[tndx].match.static &&
  731. tests[tndx].match.fn.test(
  732. testDefinition.input,
  733. maskset,
  734. pos,
  735. false,
  736. opts
  737. )))
  738. ) {
  739. valid = true;
  740. break;
  741. } else if (
  742. tests[tndx].match &&
  743. tests[tndx].match.def === testDefinition.match.nativeDef
  744. ) {
  745. valid = undefined;
  746. break;
  747. }
  748. }
  749. if (valid === false) {
  750. if (maskset.jitOffset[pos] !== undefined) {
  751. valid = positionCanMatchDefinition.call(
  752. inputmask,
  753. pos + maskset.jitOffset[pos],
  754. testDefinition,
  755. opts
  756. );
  757. }
  758. }
  759. return valid;
  760. }
  761. // tobe put on prototype?
  762. function refreshFromBuffer(start, end, buffer) {
  763. const inputmask = this,
  764. maskset = this.maskset,
  765. opts = this.opts,
  766. $ = this.dependencyLib;
  767. // checkVal.call(inputmask, el, false, true, isRTL ? buffer.reverse() : buffer);
  768. let i,
  769. p,
  770. skipOptionalPartCharacter = opts.skipOptionalPartCharacter,
  771. bffr = inputmask.isRTL ? buffer.slice().reverse() : buffer;
  772. opts.skipOptionalPartCharacter = "";
  773. if (start === true) {
  774. resetMaskSet.call(inputmask, false);
  775. start = 0;
  776. end = buffer.length;
  777. p = determineNewCaretPosition.call(
  778. inputmask,
  779. { begin: 0, end: 0 },
  780. false
  781. ).begin;
  782. } else {
  783. for (i = start; i < end; i++) {
  784. delete maskset.validPositions[i];
  785. }
  786. p = start;
  787. }
  788. const keypress = new $.Event("keypress");
  789. for (i = start; i < end; i++) {
  790. keypress.key = bffr[i].toString();
  791. inputmask.ignorable = false; // make sure ignorable is ignored ;-)
  792. const valResult = EventHandlers.keypressEvent.call(
  793. inputmask,
  794. keypress,
  795. true,
  796. false,
  797. false,
  798. p
  799. );
  800. if (valResult !== false && valResult !== undefined) {
  801. p = valResult.forwardPosition;
  802. }
  803. }
  804. opts.skipOptionalPartCharacter = skipOptionalPartCharacter;
  805. }
  806. // tobe put on prototype?
  807. // fill in best positions according the current input
  808. function trackbackPositions(originalPos, newPos, fillOnly) {
  809. const inputmask = this,
  810. maskset = this.maskset,
  811. $ = this.dependencyLib;
  812. // console.log("trackbackPositions " + originalPos + " " + newPos);
  813. if (originalPos === undefined) {
  814. // find previous valid
  815. for (originalPos = newPos - 1; originalPos > 0; originalPos--) {
  816. if (maskset.validPositions[originalPos]) break;
  817. }
  818. }
  819. for (let ps = originalPos; ps < newPos; ps++) {
  820. if (
  821. maskset.validPositions[ps] === undefined &&
  822. !isMask.call(inputmask, ps, false)
  823. ) {
  824. const vp =
  825. ps == 0 ? getTest.call(inputmask, ps) : maskset.validPositions[ps - 1];
  826. if (vp) {
  827. const tests = getTests.call(inputmask, ps).slice();
  828. if (tests[tests.length - 1].match.def === "") tests.pop();
  829. var bestMatch = determineTestTemplate.call(inputmask, ps, tests),
  830. np;
  831. if (
  832. bestMatch &&
  833. (bestMatch.match.jit !== true ||
  834. (bestMatch.match.newBlockMarker === "master" &&
  835. (np = maskset.validPositions[ps + 1]) &&
  836. np.match.optionalQuantifier === true))
  837. ) {
  838. bestMatch = $.extend({}, bestMatch, {
  839. input:
  840. getPlaceholder.call(inputmask, ps, bestMatch.match, true) ||
  841. bestMatch.match.def
  842. });
  843. bestMatch.generatedInput = true;
  844. revalidateMask.call(inputmask, ps, bestMatch, true);
  845. if (fillOnly !== true) {
  846. // revalidate the new position to update the locator value
  847. const cvpInput = maskset.validPositions[newPos].input;
  848. maskset.validPositions[newPos] = undefined;
  849. return isValid.call(inputmask, newPos, cvpInput, true, true);
  850. }
  851. }
  852. }
  853. }
  854. }
  855. }
  856. // tobe put on prototype?
  857. function revalidateMask(pos, validTest, fromIsValid, validatedPos) {
  858. // console.log("revalidateMask " + fromIsValid);
  859. const inputmask = this,
  860. maskset = this.maskset,
  861. opts = this.opts,
  862. $ = this.dependencyLib;
  863. function IsEnclosedStatic(pos, valids, selection) {
  864. const posMatch = valids[pos];
  865. if (
  866. posMatch !== undefined &&
  867. posMatch.match.static === true &&
  868. posMatch.match.optionality !== true &&
  869. (valids[0] === undefined || valids[0].alternation === undefined)
  870. ) {
  871. const prevMatch =
  872. selection.begin <= pos - 1
  873. ? valids[pos - 1] &&
  874. valids[pos - 1].match.static === true &&
  875. valids[pos - 1]
  876. : valids[pos - 1],
  877. nextMatch =
  878. selection.end > pos + 1
  879. ? valids[pos + 1] &&
  880. valids[pos + 1].match.static === true &&
  881. valids[pos + 1]
  882. : valids[pos + 1];
  883. return prevMatch && nextMatch;
  884. }
  885. return false;
  886. }
  887. let offset = 0,
  888. begin = pos.begin !== undefined ? pos.begin : pos,
  889. end = pos.end !== undefined ? pos.end : pos,
  890. valid = true;
  891. if (pos.begin > pos.end) {
  892. begin = pos.end;
  893. end = pos.begin;
  894. }
  895. validatedPos = validatedPos !== undefined ? validatedPos : begin;
  896. if (
  897. fromIsValid === undefined &&
  898. (begin !== end ||
  899. (opts.insertMode && maskset.validPositions[validatedPos] !== undefined) ||
  900. validTest === undefined ||
  901. validTest.match.optionalQuantifier ||
  902. validTest.match.optionality)
  903. ) {
  904. // reposition & revalidate others
  905. let positionsClone = $.extend(true, [], maskset.validPositions),
  906. lvp = getLastValidPosition.call(inputmask, undefined, true),
  907. i;
  908. maskset.p = begin; // needed for alternated position after overtype selection
  909. const clearpos = isSelection.call(inputmask, pos) ? begin : validatedPos;
  910. for (i = lvp; i >= clearpos; i--) {
  911. maskset.validPositions.splice(i, 1);
  912. if (validTest === undefined) delete maskset.tests[i + 1];
  913. }
  914. let j = validatedPos,
  915. posMatch = j,
  916. t,
  917. canMatch,
  918. test;
  919. if (validTest) {
  920. maskset.validPositions[validatedPos] = $.extend(true, {}, validTest);
  921. posMatch++;
  922. j++;
  923. }
  924. if (positionsClone[end] == undefined && maskset.jitOffset[end]) {
  925. end += maskset.jitOffset[end] + 1;
  926. }
  927. for (i = validTest ? end : end - 1; i <= lvp; i++) {
  928. if (
  929. (t = positionsClone[i]) !== undefined &&
  930. t.generatedInput !== true &&
  931. (i >= end ||
  932. (i >= begin &&
  933. IsEnclosedStatic(i, positionsClone, {
  934. begin,
  935. end
  936. })))
  937. ) {
  938. while (
  939. ((test = getTest.call(inputmask, posMatch)), test.match.def !== "")
  940. ) {
  941. // loop needed to match further positions
  942. if (
  943. (canMatch = positionCanMatchDefinition.call(
  944. inputmask,
  945. posMatch,
  946. t,
  947. opts
  948. )) !== false ||
  949. t.match.def === "+"
  950. ) {
  951. // validated match //we still need some hackery for the + validator (numeric alias)
  952. if (t.match.def === "+") getBuffer.call(inputmask, true);
  953. const result = isValid.call(
  954. inputmask,
  955. posMatch,
  956. t.input,
  957. t.match.def !== "+",
  958. /* t.match.def !== "+" */ true
  959. );
  960. valid = result !== false;
  961. j = (result.pos || posMatch) + 1;
  962. if (!valid && canMatch) break;
  963. } else {
  964. valid = false;
  965. }
  966. if (valid) {
  967. if (validTest === undefined && t.match.static && i === pos.begin)
  968. offset++;
  969. break;
  970. }
  971. if (
  972. (!valid && getBuffer.call(inputmask), posMatch > maskset.maskLength)
  973. ) {
  974. break;
  975. }
  976. posMatch++;
  977. }
  978. if (getTest.call(inputmask, posMatch).match.def == "") {
  979. valid = false;
  980. }
  981. // restore position
  982. posMatch = j;
  983. }
  984. if (!valid) break;
  985. }
  986. if (!valid) {
  987. maskset.validPositions = $.extend(true, [], positionsClone);
  988. resetMaskSet.call(inputmask, true);
  989. return false;
  990. }
  991. } else if (
  992. validTest &&
  993. getTest.call(inputmask, validatedPos).match.cd === validTest.match.cd
  994. ) {
  995. maskset.validPositions[validatedPos] = $.extend(true, {}, validTest);
  996. }
  997. resetMaskSet.call(inputmask, true);
  998. return offset;
  999. }