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