validation-tests.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. import Inputmask from "./inputmask";
  2. import { getLastValidPosition, seekNext } from "./positioning";
  3. export {
  4. determineTestTemplate,
  5. getDecisionTaker,
  6. getMaskTemplate,
  7. getPlaceholder,
  8. getTest,
  9. getTests,
  10. getTestTemplate,
  11. isSubsetOf
  12. };
  13. function getLocator(tst, align) {
  14. // need to align the locators to be correct
  15. let locator = (
  16. tst.alternation != undefined ? tst.mloc[getDecisionTaker(tst)] : tst.locator
  17. ).join("");
  18. if (locator !== "") {
  19. locator = locator.split(":")[0]; // strip off alternation marker
  20. while (locator.length < align) locator += "0";
  21. }
  22. return locator;
  23. }
  24. function getDecisionTaker(tst) {
  25. let decisionTaker = tst.locator[tst.alternation];
  26. if (typeof decisionTaker === "string" && decisionTaker.length > 0) {
  27. // no decision taken ~ take first one as decider
  28. decisionTaker = decisionTaker.split(",")[0];
  29. }
  30. return decisionTaker !== undefined ? decisionTaker.toString() : "";
  31. }
  32. // tobe put on prototype?
  33. function getPlaceholder(pos, test, returnPL) {
  34. const inputmask = this,
  35. opts = this.opts,
  36. maskset = this.maskset;
  37. test = test || getTest.call(inputmask, pos).match;
  38. if (test.placeholder !== undefined || returnPL === true) {
  39. if (
  40. test.placeholder !== "" &&
  41. test.static === true &&
  42. test.generated !== true
  43. ) {
  44. // static and not dynamically generated ~ does not occur in regex mask ~ numeric alias def is not a valid entry
  45. const lvp = getLastValidPosition.call(inputmask, pos),
  46. nextPos = seekNext.call(inputmask, lvp);
  47. return (returnPL ? pos <= nextPos : pos < nextPos)
  48. ? opts.staticDefinitionSymbol && test.static
  49. ? test.nativeDef
  50. : test.def
  51. : typeof test.placeholder === "function"
  52. ? test.placeholder(opts)
  53. : test.placeholder;
  54. } else {
  55. return typeof test.placeholder === "function"
  56. ? test.placeholder(opts)
  57. : test.placeholder;
  58. }
  59. } else if (test.static === true) {
  60. if (pos > -1 && maskset.validPositions[pos] === undefined) {
  61. let tests = getTests.call(inputmask, pos),
  62. staticAlternations = [],
  63. prevTest;
  64. if (
  65. typeof opts.placeholder === "string" &&
  66. tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0)
  67. ) {
  68. for (let i = 0; i < tests.length; i++) {
  69. if (
  70. tests[i].match.def !== "" &&
  71. tests[i].match.optionality !== true &&
  72. tests[i].match.optionalQuantifier !== true &&
  73. (tests[i].match.static === true ||
  74. prevTest === undefined ||
  75. tests[i].match.fn.test(
  76. prevTest.match.def,
  77. maskset,
  78. pos,
  79. true,
  80. opts
  81. ) !== false)
  82. ) {
  83. staticAlternations.push(tests[i]);
  84. if (tests[i].match.static === true) prevTest = tests[i];
  85. if (staticAlternations.length > 1) {
  86. if (/[0-9a-bA-Z]/.test(staticAlternations[0].match.def)) {
  87. return opts.placeholder.charAt(pos % opts.placeholder.length);
  88. }
  89. }
  90. }
  91. }
  92. }
  93. }
  94. return test.def;
  95. }
  96. return typeof opts.placeholder === "object"
  97. ? test.def
  98. : opts.placeholder.charAt(pos % opts.placeholder.length);
  99. }
  100. // tobe put on prototype?
  101. function getMaskTemplate(
  102. baseOnInput,
  103. minimalPos,
  104. includeMode,
  105. noJit,
  106. clearOptionalTail
  107. ) {
  108. // includeMode true => input, undefined => placeholder, false => mask
  109. const inputmask = this,
  110. opts = this.opts,
  111. maskset = this.maskset,
  112. greedy = opts.greedy;
  113. if (clearOptionalTail && opts.greedy) {
  114. opts.greedy = false;
  115. inputmask.maskset.tests = {};
  116. }
  117. minimalPos = minimalPos || 0;
  118. let maskTemplate = [],
  119. ndxIntlzr,
  120. pos = 0,
  121. test,
  122. testPos,
  123. jitRenderStatic;
  124. do {
  125. if (baseOnInput === true && maskset.validPositions[pos]) {
  126. testPos =
  127. clearOptionalTail &&
  128. maskset.validPositions[pos].match.optionality &&
  129. maskset.validPositions[pos + 1] === undefined &&
  130. (maskset.validPositions[pos].generatedInput === true ||
  131. (maskset.validPositions[pos].input ==
  132. opts.skipOptionalPartCharacter &&
  133. pos > 0))
  134. ? determineTestTemplate.call(
  135. inputmask,
  136. pos,
  137. getTests.call(inputmask, pos, ndxIntlzr, pos - 1)
  138. )
  139. : maskset.validPositions[pos];
  140. test = testPos.match;
  141. ndxIntlzr = testPos.locator.slice();
  142. maskTemplate.push(
  143. includeMode === true
  144. ? testPos.input
  145. : includeMode === false
  146. ? test.nativeDef
  147. : getPlaceholder.call(inputmask, pos, test)
  148. );
  149. } else {
  150. testPos = getTestTemplate.call(inputmask, pos, ndxIntlzr, pos - 1);
  151. test = testPos.match;
  152. ndxIntlzr = testPos.locator.slice();
  153. const jitMasking =
  154. noJit === true
  155. ? false
  156. : opts.jitMasking !== false
  157. ? opts.jitMasking
  158. : test.jit;
  159. // check for groupSeparator is a hack for the numerics as we don't want the render of the groupSeparator beforehand
  160. jitRenderStatic =
  161. (jitRenderStatic ||
  162. maskset.validPositions[
  163. pos - 1
  164. ]) /* && getTest.call(inputmask, pos + 1).match.def == "" */ &&
  165. test.static &&
  166. test.def !== opts.groupSeparator &&
  167. test.fn === null;
  168. if (
  169. jitRenderStatic ||
  170. jitMasking === false ||
  171. jitMasking === undefined /* || pos < lvp */ ||
  172. (typeof jitMasking === "number" &&
  173. isFinite(jitMasking) &&
  174. jitMasking > pos)
  175. ) {
  176. maskTemplate.push(
  177. includeMode === false
  178. ? test.nativeDef
  179. : getPlaceholder.call(inputmask, maskTemplate.length, test)
  180. );
  181. } else {
  182. jitRenderStatic = false;
  183. }
  184. }
  185. pos++;
  186. } while (test.static !== true || test.def !== "" || minimalPos > pos);
  187. if (maskTemplate[maskTemplate.length - 1] === "") {
  188. maskTemplate.pop(); // drop the last one which is empty
  189. }
  190. if (
  191. includeMode !== false || // do not alter the masklength when just retrieving the maskdefinition
  192. maskset.maskLength === undefined
  193. ) {
  194. // just make sure the maskLength gets initialized in all cases (needed for isValid)
  195. maskset.maskLength = pos - 1;
  196. }
  197. opts.greedy = greedy;
  198. return maskTemplate;
  199. }
  200. // tobe put on prototype?
  201. function getTestTemplate(pos, ndxIntlzr, tstPs) {
  202. const inputmask = this,
  203. maskset = this.maskset;
  204. return (
  205. maskset.validPositions[pos] ||
  206. determineTestTemplate.call(
  207. inputmask,
  208. pos,
  209. getTests.call(
  210. inputmask,
  211. pos,
  212. ndxIntlzr ? ndxIntlzr.slice() : ndxIntlzr,
  213. tstPs
  214. )
  215. )
  216. );
  217. }
  218. // tobe put on prototype?
  219. function determineTestTemplate(pos, tests) {
  220. let inputmask = this,
  221. opts = this.opts,
  222. lenghtOffset = 0,
  223. optionalityLevel = determineOptionalityLevel(pos, tests);
  224. pos = pos > 0 ? pos - 1 : 0;
  225. let altTest = getTest.call(inputmask, pos),
  226. targetLocator = getLocator(altTest),
  227. tstLocator,
  228. closest,
  229. bestMatch;
  230. if (
  231. opts.greedy &&
  232. tests.length > 1 &&
  233. tests[tests.length - 1].match.def === ""
  234. )
  235. lenghtOffset = 1;
  236. // console.log(" optionality = " + optionalityLevel);
  237. // console.log(" - " + JSON.stringify(tests));
  238. for (let ndx = 0; ndx < tests.length - lenghtOffset; ndx++) {
  239. // find best matching
  240. const tst = tests[ndx];
  241. tstLocator = getLocator(tst, targetLocator.length);
  242. const distance = Math.abs(tstLocator - targetLocator);
  243. if (
  244. tst.unMatchedAlternationStopped !== true ||
  245. tests.filter((tst) => tst.unMatchedAlternationStopped !== true).length <=
  246. 1
  247. ) {
  248. // only skip when there are choices outside the alternation
  249. if (
  250. closest === undefined ||
  251. (tstLocator !== "" && distance < closest) ||
  252. (bestMatch &&
  253. !opts.greedy &&
  254. bestMatch.match.optionality &&
  255. bestMatch.match.optionality - optionalityLevel > 0 &&
  256. bestMatch.match.newBlockMarker === "master" &&
  257. (!tst.match.optionality ||
  258. tst.match.optionality - optionalityLevel < 1 ||
  259. !tst.match.newBlockMarker)) ||
  260. (bestMatch &&
  261. !opts.greedy &&
  262. bestMatch.match.optionalQuantifier &&
  263. !tst.match.optionalQuantifier)
  264. ) {
  265. closest = distance;
  266. bestMatch = tst;
  267. }
  268. }
  269. }
  270. return bestMatch;
  271. }
  272. function determineOptionalityLevel(pos, tests) {
  273. let optionalityLevel = 0,
  274. differentOptionalLevels = false;
  275. tests.forEach((test) => {
  276. if (test.match.optionality) {
  277. if (optionalityLevel !== 0 && optionalityLevel !== test.match.optionality)
  278. differentOptionalLevels = true;
  279. if (optionalityLevel === 0 || optionalityLevel > test.match.optionality) {
  280. optionalityLevel = test.match.optionality;
  281. }
  282. }
  283. });
  284. if (optionalityLevel) {
  285. if (pos == 0) optionalityLevel = 0;
  286. else if (tests.length == 1) optionalityLevel = 0;
  287. else if (!differentOptionalLevels) optionalityLevel = 0;
  288. }
  289. return optionalityLevel;
  290. }
  291. // tobe put on prototype?
  292. function getTest(pos, tests) {
  293. const inputmask = this,
  294. maskset = this.maskset;
  295. if (maskset.validPositions[pos]) {
  296. return maskset.validPositions[pos];
  297. }
  298. return (tests || getTests.call(inputmask, pos))[0];
  299. }
  300. function isSubsetOf(source, target, opts) {
  301. function expand(pattern) {
  302. let expanded = [],
  303. start = -1,
  304. end;
  305. for (let i = 0, l = pattern.length; i < l; i++) {
  306. if (pattern.charAt(i) === "-") {
  307. end = pattern.charCodeAt(i + 1);
  308. while (++start < end) expanded.push(String.fromCharCode(start));
  309. } else {
  310. start = pattern.charCodeAt(i);
  311. expanded.push(pattern.charAt(i));
  312. }
  313. }
  314. return expanded.join("");
  315. }
  316. if (source.match.def === target.match.nativeDef) return true;
  317. if (
  318. (opts.regex ||
  319. (source.match.fn instanceof RegExp &&
  320. target.match.fn instanceof RegExp)) &&
  321. source.match.static !== true &&
  322. target.match.static !== true
  323. ) {
  324. // is regex a subset
  325. if (target.match.fn.source === ".") return true;
  326. return (
  327. expand(target.match.fn.source.replace(/[[\]/]/g, "")).indexOf(
  328. expand(source.match.fn.source.replace(/[[\]/]/g, ""))
  329. ) !== -1
  330. );
  331. }
  332. return false;
  333. }
  334. // tobe put on prototype?
  335. function getTests(pos, ndxIntlzr, tstPs) {
  336. let inputmask = this,
  337. $ = this.dependencyLib,
  338. maskset = this.maskset,
  339. opts = this.opts,
  340. el = this.el,
  341. maskTokens = maskset.maskToken,
  342. testPos = ndxIntlzr ? tstPs : 0,
  343. ndxInitializer = ndxIntlzr ? ndxIntlzr.slice() : [0],
  344. matches = [],
  345. insertStop = false,
  346. latestMatch,
  347. cacheDependency = ndxIntlzr ? ndxIntlzr.join("") : "",
  348. unMatchedAlternation = false;
  349. function resolveTestFromToken(
  350. maskToken,
  351. ndxInitializer,
  352. loopNdx,
  353. quantifierRecurse
  354. ) {
  355. // ndxInitializer contains a set of indexes to speedup searches in the mtokens
  356. function handleMatch(match, loopNdx, quantifierRecurse) {
  357. function isFirstMatch(latestMatch, tokenGroup) {
  358. let firstMatch = tokenGroup.matches.indexOf(latestMatch) === 0;
  359. if (!firstMatch) {
  360. tokenGroup.matches.every(function (match, ndx) {
  361. if (match.isQuantifier === true) {
  362. firstMatch = isFirstMatch(
  363. latestMatch,
  364. tokenGroup.matches[ndx - 1]
  365. );
  366. } else if (Object.prototype.hasOwnProperty.call(match, "matches"))
  367. firstMatch = isFirstMatch(latestMatch, match);
  368. if (firstMatch) return false;
  369. return true;
  370. });
  371. }
  372. return firstMatch;
  373. }
  374. function resolveNdxInitializer(pos, alternateNdx, targetAlternation) {
  375. let bestMatch, indexPos;
  376. if (maskset.tests[pos] || maskset.validPositions[pos]) {
  377. (maskset.validPositions[pos]
  378. ? [maskset.validPositions[pos]]
  379. : maskset.tests[pos]
  380. ).every(function (lmnt, ndx) {
  381. if (lmnt.mloc[alternateNdx]) {
  382. bestMatch = lmnt;
  383. return false; // break
  384. }
  385. const alternation =
  386. targetAlternation !== undefined
  387. ? targetAlternation
  388. : lmnt.alternation,
  389. ndxPos =
  390. lmnt.locator[alternation] !== undefined
  391. ? lmnt.locator[alternation].toString().indexOf(alternateNdx)
  392. : -1;
  393. if (
  394. (indexPos === undefined || ndxPos < indexPos) &&
  395. ndxPos !== -1
  396. ) {
  397. bestMatch = lmnt;
  398. indexPos = ndxPos;
  399. }
  400. return true;
  401. });
  402. }
  403. if (bestMatch) {
  404. const bestMatchAltIndex = bestMatch.locator[bestMatch.alternation],
  405. locator =
  406. bestMatch.mloc[alternateNdx] ||
  407. bestMatch.mloc[bestMatchAltIndex] ||
  408. bestMatch.locator;
  409. if (locator[locator.length - 1].toString().indexOf(":") !== -1) {
  410. // eslint-disable-next-line no-unused-vars
  411. const alternation = locator.pop();
  412. // targetAlternation = parseInt(alternation.substring(1));
  413. }
  414. return locator.slice(
  415. (targetAlternation !== undefined
  416. ? targetAlternation
  417. : bestMatch.alternation) + 1
  418. );
  419. } else {
  420. return targetAlternation !== undefined
  421. ? resolveNdxInitializer(pos, alternateNdx)
  422. : undefined;
  423. }
  424. }
  425. function staticCanMatchDefinition(source, target) {
  426. return source.match.static === true && target.match.static !== true
  427. ? target.match.fn.test(
  428. source.match.def,
  429. maskset,
  430. pos,
  431. false,
  432. opts,
  433. false
  434. )
  435. : false;
  436. }
  437. // mergelocators for retrieving the correct locator match when merging
  438. function setMergeLocators(targetMatch, altMatch) {
  439. function mergeLoc(altNdx) {
  440. targetMatch.mloc = targetMatch.mloc || {};
  441. let locNdx = targetMatch.locator[altNdx];
  442. if (locNdx === undefined) {
  443. targetMatch.alternation = undefined;
  444. } else {
  445. if (typeof locNdx === "string") locNdx = locNdx.split(",")[0];
  446. if (targetMatch.mloc[locNdx] === undefined) {
  447. targetMatch.mloc[locNdx] = targetMatch.locator.slice();
  448. targetMatch.mloc[locNdx].push(`:${targetMatch.alternation}`); // add alternation index
  449. }
  450. if (altMatch !== undefined) {
  451. const offset = 0;
  452. for (let ndx in altMatch.mloc) {
  453. if (typeof ndx === "string") ndx = parseInt(ndx.split(",")[0]);
  454. // do {
  455. // if (targetMatch.mloc[ndx + offset] === undefined) {
  456. targetMatch.mloc[ndx + offset] = altMatch.mloc[ndx];
  457. // break;
  458. // }
  459. // } while (targetMatch.mloc[ndx + offset++] !== undefined);
  460. }
  461. targetMatch.locator[altNdx] = Object.keys(targetMatch.mloc).join(
  462. ","
  463. );
  464. }
  465. if (targetMatch.alternation > altNdx) {
  466. // if the alternation index is higher than the current one resolve it to the alternation
  467. targetMatch.alternation = altNdx;
  468. }
  469. return true;
  470. }
  471. return false;
  472. }
  473. let alternationNdx = targetMatch.alternation,
  474. shouldMerge =
  475. altMatch === undefined ||
  476. (alternationNdx <= altMatch.alternation &&
  477. targetMatch.locator[alternationNdx]
  478. .toString()
  479. .indexOf(altMatch.locator[alternationNdx]) === -1);
  480. if (!shouldMerge && alternationNdx > altMatch.alternation) {
  481. for (let i = 0; i < alternationNdx; i++) {
  482. if (targetMatch.locator[i] !== altMatch.locator[i]) {
  483. alternationNdx = i;
  484. shouldMerge = true;
  485. break;
  486. }
  487. }
  488. }
  489. if (shouldMerge) {
  490. return mergeLoc(alternationNdx);
  491. }
  492. return false;
  493. }
  494. function isSameLevel(targetMatch, altMatch) {
  495. if (targetMatch.locator.length !== altMatch.locator.length) {
  496. return false;
  497. }
  498. for (
  499. let locNdx = targetMatch.alternation + 1;
  500. locNdx < targetMatch.locator.length;
  501. locNdx++
  502. ) {
  503. if (targetMatch.locator[locNdx] !== altMatch.locator[locNdx]) {
  504. return false;
  505. }
  506. }
  507. return true;
  508. }
  509. function handleGroup() {
  510. match = handleMatch(
  511. maskToken.matches[maskToken.matches.indexOf(match) + 1],
  512. loopNdx,
  513. quantifierRecurse
  514. );
  515. if (match) return true;
  516. }
  517. function handleOptional() {
  518. const optionalToken = match,
  519. mtchsNdx = matches.length;
  520. match = resolveTestFromToken(
  521. match,
  522. ndxInitializer,
  523. loopNdx,
  524. quantifierRecurse
  525. );
  526. if (matches.length > 0) {
  527. // check on matches.length instead of match to handle quantifier in a recursive call
  528. // mark optionality in matches
  529. matches.forEach(function (mtch, ndx) {
  530. if (ndx >= mtchsNdx) {
  531. mtch.match.optionality = mtch.match.optionality
  532. ? mtch.match.optionality + 1
  533. : 1;
  534. }
  535. });
  536. latestMatch = matches[matches.length - 1].match;
  537. if (
  538. quantifierRecurse === undefined &&
  539. isFirstMatch(latestMatch, optionalToken)
  540. ) {
  541. // prevent loop see #698
  542. insertStop = true; // insert a stop
  543. testPos = pos; // match the position after the group
  544. } else {
  545. return match; // make the loop continue when it is deliberately by a quantifier
  546. }
  547. }
  548. }
  549. function handleAlternator() {
  550. function isUnmatchedAlternation(alternateToken) {
  551. let matchesLength = alternateToken.matches[0].matches
  552. ? alternateToken.matches[0].matches.length
  553. : 1,
  554. matchesNewLength;
  555. for (let alndx = 0; alndx < alternateToken.matches.length; alndx++) {
  556. matchesNewLength = alternateToken.matches[alndx].matches
  557. ? alternateToken.matches[alndx].matches.length
  558. : 1;
  559. if (matchesLength !== matchesNewLength) {
  560. break;
  561. }
  562. }
  563. return matchesLength !== matchesNewLength;
  564. }
  565. inputmask.hasAlternator = true;
  566. let alternateToken = match,
  567. malternateMatches = [],
  568. maltMatches,
  569. currentMatches = matches.slice(),
  570. loopNdxCnt = loopNdx.length,
  571. altIndex = ndxInitializer.length > 0 ? ndxInitializer.shift() : -1;
  572. if (altIndex === -1 || typeof altIndex === "string") {
  573. let currentPos = testPos,
  574. ndxInitializerClone = ndxInitializer.slice(),
  575. altIndexArr = [],
  576. amndx;
  577. if (typeof altIndex === "string") {
  578. altIndexArr = altIndex.split(",");
  579. } else {
  580. for (amndx = 0; amndx < alternateToken.matches.length; amndx++) {
  581. altIndexArr.push(amndx.toString());
  582. }
  583. }
  584. if (maskset.excludes[pos] !== undefined) {
  585. const altIndexArrClone = altIndexArr.slice();
  586. for (let i = 0, exl = maskset.excludes[pos].length; i < exl; i++) {
  587. const excludeSet = maskset.excludes[pos][i].toString().split(":");
  588. if (loopNdx.length == excludeSet[1]) {
  589. altIndexArr.splice(altIndexArr.indexOf(excludeSet[0]), 1);
  590. }
  591. }
  592. if (altIndexArr.length === 0) {
  593. // fully alternated => reset
  594. delete maskset.excludes[pos];
  595. altIndexArr = altIndexArrClone;
  596. }
  597. }
  598. if (
  599. opts.keepStatic === true ||
  600. (isFinite(parseInt(opts.keepStatic)) &&
  601. currentPos >= opts.keepStatic)
  602. )
  603. altIndexArr = altIndexArr.slice(0, 1);
  604. for (let ndx = 0; ndx < altIndexArr.length; ndx++) {
  605. amndx = parseInt(altIndexArr[ndx]);
  606. matches = [];
  607. // set the correct ndxInitializer
  608. ndxInitializer =
  609. typeof altIndex === "string"
  610. ? resolveNdxInitializer(testPos, amndx, loopNdxCnt) ||
  611. ndxInitializerClone.slice()
  612. : ndxInitializerClone.slice();
  613. // console.log("ndxInit", ndxInitializer);
  614. const tokenMatch = alternateToken.matches[amndx];
  615. if (
  616. tokenMatch &&
  617. handleMatch(
  618. tokenMatch,
  619. [amndx].concat(loopNdx),
  620. quantifierRecurse
  621. )
  622. ) {
  623. match = true;
  624. } else {
  625. if (ndx === 0) {
  626. unMatchedAlternation = isUnmatchedAlternation(alternateToken);
  627. }
  628. if (
  629. tokenMatch &&
  630. tokenMatch.matches &&
  631. tokenMatch.matches.length >
  632. alternateToken.matches[0].matches.length
  633. ) {
  634. break;
  635. }
  636. }
  637. maltMatches = matches.slice();
  638. testPos = currentPos;
  639. matches = [];
  640. // fuzzy merge matches
  641. for (let ndx1 = 0; ndx1 < maltMatches.length; ndx1++) {
  642. let altMatch = maltMatches[ndx1],
  643. dropMatch = false;
  644. altMatch.alternation = altMatch.alternation || loopNdxCnt;
  645. setMergeLocators(altMatch);
  646. for (let ndx2 = 0; ndx2 < malternateMatches.length; ndx2++) {
  647. const altMatch2 = malternateMatches[ndx2];
  648. if (
  649. typeof altIndex !== "string" ||
  650. (altMatch.alternation !== undefined &&
  651. altIndexArr.includes(
  652. altMatch.locator[altMatch.alternation].toString()
  653. ))
  654. ) {
  655. if (altMatch.match.nativeDef === altMatch2.match.nativeDef) {
  656. dropMatch = true;
  657. setMergeLocators(altMatch2, altMatch);
  658. break;
  659. } else if (isSubsetOf(altMatch, altMatch2, opts)) {
  660. if (setMergeLocators(altMatch, altMatch2)) {
  661. dropMatch = true;
  662. malternateMatches.splice(
  663. malternateMatches.indexOf(altMatch2),
  664. 0,
  665. altMatch
  666. );
  667. }
  668. break;
  669. } else if (isSubsetOf(altMatch2, altMatch, opts)) {
  670. setMergeLocators(altMatch2, altMatch);
  671. break;
  672. } else if (staticCanMatchDefinition(altMatch, altMatch2)) {
  673. if (
  674. !isSameLevel(altMatch, altMatch2) &&
  675. el.inputmask.userOptions.keepStatic === undefined
  676. ) {
  677. opts.keepStatic = true;
  678. } else if (setMergeLocators(altMatch, altMatch2)) {
  679. // insert match above general match
  680. dropMatch = true;
  681. malternateMatches.splice(
  682. malternateMatches.indexOf(altMatch2),
  683. 0,
  684. altMatch
  685. );
  686. }
  687. break;
  688. } else if (staticCanMatchDefinition(altMatch2, altMatch)) {
  689. setMergeLocators(altMatch2, altMatch);
  690. break;
  691. }
  692. }
  693. }
  694. if (!dropMatch) {
  695. malternateMatches.push(altMatch);
  696. }
  697. }
  698. }
  699. matches = currentMatches.concat(malternateMatches);
  700. testPos = pos;
  701. insertStop = matches.length > 0 && unMatchedAlternation; // insert a stopelemnt when there is an alternate - needed for non-greedy option
  702. match = malternateMatches.length > 0 && !unMatchedAlternation; // set correct match state
  703. if (unMatchedAlternation && insertStop && !match) {
  704. // mark matches with unMatchedAlternationStopped
  705. matches.forEach(function (mtch, ndx) {
  706. mtch.unMatchedAlternationStopped = true;
  707. });
  708. }
  709. // cloneback
  710. ndxInitializer = ndxInitializerClone.slice();
  711. } else {
  712. match = handleMatch(
  713. alternateToken.matches[altIndex] || maskToken.matches[altIndex],
  714. [altIndex].concat(loopNdx),
  715. quantifierRecurse
  716. );
  717. }
  718. if (match) return true;
  719. }
  720. function handleQuantifier() {
  721. let qt = match,
  722. breakloop = false;
  723. for (
  724. var qndx = ndxInitializer.length > 0 ? ndxInitializer.shift() : 0;
  725. qndx < (isNaN(qt.quantifier.max) ? qndx + 1 : qt.quantifier.max) &&
  726. testPos <= pos;
  727. qndx++
  728. ) {
  729. var tokenGroup = maskToken.matches[maskToken.matches.indexOf(qt) - 1];
  730. match = handleMatch(tokenGroup, [qndx].concat(loopNdx), tokenGroup); // set the tokenGroup as quantifierRecurse marker
  731. if (match) {
  732. matches.forEach(function (mtch, ndx) {
  733. if (IsMatchOf(tokenGroup, mtch.match)) latestMatch = mtch.match;
  734. else latestMatch = matches[matches.length - 1].match;
  735. // mark optionality
  736. // TODO FIX RECURSIVE QUANTIFIERS
  737. latestMatch.optionalQuantifier = qndx >= qt.quantifier.min;
  738. // console.log(pos + " " + qt.quantifier.min + " " + latestMatch.optionalQuantifier);
  739. // qndx + 1 as the index starts from 0
  740. latestMatch.jit =
  741. (qndx + 1) * (tokenGroup.matches.indexOf(latestMatch) + 1) >
  742. qt.quantifier.jit;
  743. if (
  744. latestMatch.optionalQuantifier &&
  745. isFirstMatch(latestMatch, tokenGroup)
  746. ) {
  747. insertStop = true;
  748. testPos = pos; // match the position after the group
  749. if (
  750. opts.greedy &&
  751. maskset.validPositions[pos - 1] == undefined &&
  752. qndx > qt.quantifier.min &&
  753. ["*", "+"].indexOf(qt.quantifier.max) != -1
  754. ) {
  755. matches.pop();
  756. cacheDependency = undefined;
  757. }
  758. breakloop = true; // stop quantifierloop && search for next possible match
  759. match = false; // mark match to false to make sure the loop in optionals continues
  760. }
  761. if (
  762. !breakloop &&
  763. latestMatch.jit /* && !latestMatch.optionalQuantifier */
  764. ) {
  765. // always set jitOffset, isvalid checks when to apply
  766. maskset.jitOffset[pos] =
  767. tokenGroup.matches.length -
  768. tokenGroup.matches.indexOf(latestMatch);
  769. }
  770. });
  771. if (breakloop) break; // search for next possible match
  772. return true;
  773. }
  774. }
  775. }
  776. if (testPos > pos + opts._maxTestPos) {
  777. throw new Error(
  778. `Inputmask: There is probably an error in your mask definition or in the code. Create an issue on github with an example of the mask you are using. ${maskset.mask}`
  779. );
  780. }
  781. if (testPos === pos && match.matches === undefined) {
  782. matches.push({
  783. match,
  784. locator: loopNdx.reverse(),
  785. cd: cacheDependency,
  786. mloc: {}
  787. });
  788. if (
  789. match.optionality &&
  790. quantifierRecurse === undefined &&
  791. ((opts.definitions &&
  792. opts.definitions[match.nativeDef] &&
  793. opts.definitions[match.nativeDef].optional) ||
  794. (Inputmask.prototype.definitions[match.nativeDef] &&
  795. Inputmask.prototype.definitions[match.nativeDef].optional))
  796. ) {
  797. // prevent loop see #698
  798. insertStop = true; // insert a stop
  799. testPos = pos; // match the position after the group
  800. } else {
  801. return true;
  802. }
  803. } else if (match.matches !== undefined) {
  804. if (match.isGroup && quantifierRecurse !== match) {
  805. // when a group pass along to the quantifier
  806. return handleGroup();
  807. } else if (match.isOptional) {
  808. return handleOptional();
  809. } else if (match.isAlternator) {
  810. return handleAlternator();
  811. } else if (
  812. match.isQuantifier &&
  813. quantifierRecurse !==
  814. maskToken.matches[maskToken.matches.indexOf(match) - 1]
  815. ) {
  816. return handleQuantifier();
  817. } else {
  818. match = resolveTestFromToken(
  819. match,
  820. ndxInitializer,
  821. loopNdx,
  822. quantifierRecurse
  823. );
  824. if (match) return true;
  825. }
  826. } else {
  827. testPos++;
  828. }
  829. }
  830. // the offset is set in the quantifierloop when git masking is used
  831. for (
  832. let tndx = ndxInitializer.length > 0 ? ndxInitializer.shift() : 0;
  833. tndx < maskToken.matches.length;
  834. tndx++
  835. ) {
  836. if (maskToken.matches[tndx].isQuantifier !== true) {
  837. const match = handleMatch(
  838. maskToken.matches[tndx],
  839. [tndx].concat(loopNdx),
  840. quantifierRecurse
  841. );
  842. if (match && testPos === pos) {
  843. return match;
  844. } else if (testPos > pos) {
  845. break;
  846. }
  847. }
  848. }
  849. }
  850. function IsMatchOf(tokenGroup, match) {
  851. let isMatch = tokenGroup.matches.indexOf(match) != -1;
  852. if (!isMatch) {
  853. tokenGroup.matches.forEach((mtch, ndx) => {
  854. if (mtch.matches !== undefined && !isMatch) {
  855. isMatch = IsMatchOf(mtch, match);
  856. }
  857. });
  858. }
  859. return isMatch;
  860. }
  861. function mergeLocators(pos, tests) {
  862. let locator = [],
  863. alternation;
  864. if (!Array.isArray(tests)) tests = [tests];
  865. if (tests.length > 0) {
  866. if (tests[0].alternation === undefined || opts.keepStatic === true) {
  867. locator = determineTestTemplate
  868. .call(inputmask, pos, tests.slice())
  869. .locator.slice();
  870. if (locator.length === 0) locator = tests[0].locator.slice();
  871. } else {
  872. tests.forEach(function (tst) {
  873. if (tst.def !== "") {
  874. if (locator.length === 0) {
  875. alternation = tst.alternation;
  876. locator = tst.locator.slice();
  877. } else {
  878. if (
  879. tst.locator[alternation] &&
  880. locator[alternation]
  881. .toString()
  882. .indexOf(tst.locator[alternation]) === -1
  883. ) {
  884. locator[alternation] += "," + tst.locator[alternation];
  885. }
  886. }
  887. }
  888. });
  889. }
  890. }
  891. return locator;
  892. }
  893. if (pos > -1) {
  894. if (ndxIntlzr === undefined) {
  895. // determine index initializer
  896. let previousPos = pos - 1,
  897. test;
  898. while (
  899. (test =
  900. maskset.validPositions[previousPos] || maskset.tests[previousPos]) ===
  901. undefined &&
  902. previousPos > -1
  903. ) {
  904. previousPos--;
  905. }
  906. if (test !== undefined && previousPos > -1) {
  907. ndxInitializer = mergeLocators(previousPos, test);
  908. cacheDependency = ndxInitializer.join("");
  909. testPos = previousPos;
  910. }
  911. }
  912. if (maskset.tests[pos] && maskset.tests[pos][0].cd === cacheDependency) {
  913. // cacheDependency is set on all tests, just check on the first
  914. return maskset.tests[pos];
  915. }
  916. for (
  917. let mtndx = ndxInitializer.shift();
  918. mtndx < maskTokens.length;
  919. mtndx++
  920. ) {
  921. const match = resolveTestFromToken(maskTokens[mtndx], ndxInitializer, [
  922. mtndx
  923. ]);
  924. if ((match && testPos === pos) || testPos > pos) {
  925. break;
  926. }
  927. }
  928. }
  929. if (matches.length === 0 || insertStop) {
  930. matches.push({
  931. match: {
  932. fn: null,
  933. static: true,
  934. optionality: false,
  935. casing: null,
  936. def: "",
  937. placeholder: ""
  938. },
  939. // mark when there are unmatched alternations ex: mask: "(a|aa)"
  940. // this will result in the least distance to select the correct test result in determineTestTemplate
  941. locator:
  942. unMatchedAlternation &&
  943. matches.filter((tst) => tst.unMatchedAlternationStopped !== true)
  944. .length === 0
  945. ? [0]
  946. : [],
  947. mloc: {},
  948. cd: cacheDependency
  949. });
  950. }
  951. let result;
  952. if (ndxIntlzr !== undefined && maskset.tests[pos]) {
  953. // prioritize full tests for caching
  954. result = $.extend(true, [], matches);
  955. } else {
  956. // console.log("stored " + pos + " - " + JSON.stringify(matches));
  957. maskset.tests[pos] = $.extend(true, [], matches); // set a clone to prevent overwriting some props
  958. result = maskset.tests[pos];
  959. }
  960. // console.log(pos + " - " + JSON.stringify(matches));
  961. // cleanup optionality marking
  962. matches.forEach((t) => {
  963. t.match.optionality = t.match.defOptionality || false;
  964. });
  965. return result;
  966. }