validation-tests.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. export {
  2. determineTestTemplate,
  3. getDecisionTaker,
  4. getMaskTemplate,
  5. getPlaceholder,
  6. getTest,
  7. getTests,
  8. getTestTemplate,
  9. isSubsetOf
  10. };
  11. import Inputmask from "./inputmask";
  12. function getLocator(tst, align) { //need to align the locators to be correct
  13. var locator = (tst.alternation != undefined ? tst.mloc[getDecisionTaker(tst)] : tst.locator).join("");
  14. if (locator !== "") while (locator.length < align) locator += "0";
  15. return locator;
  16. }
  17. function getDecisionTaker(tst) {
  18. var decisionTaker = tst.locator[tst.alternation];
  19. if (typeof decisionTaker == "string" && decisionTaker.length > 0) { //no decision taken ~ take first one as decider
  20. decisionTaker = decisionTaker.split(",")[0];
  21. }
  22. return decisionTaker !== undefined ? decisionTaker.toString() : "";
  23. }
  24. //tobe put on prototype?
  25. function getPlaceholder(pos, test, returnPL) {
  26. const inputmask = this,
  27. opts = this.opts,
  28. maskset = this.maskset;
  29. test = test || getTest.call(inputmask, pos).match;
  30. if (test.placeholder !== undefined || returnPL === true) {
  31. return typeof test.placeholder === "function" ? test.placeholder(opts) : test.placeholder;
  32. } else if (test.static === true) {
  33. if (pos > -1 && maskset.validPositions[pos] === undefined) {
  34. var tests = getTests.call(inputmask, pos),
  35. staticAlternations = [],
  36. prevTest;
  37. if (tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0)) {
  38. for (var i = 0; i < tests.length; i++) {
  39. if (tests[i].match.def !== "" && tests[i].match.optionality !== true && tests[i].match.optionalQuantifier !== true &&
  40. (tests[i].match.static === true || (prevTest === undefined || tests[i].match.fn.test(prevTest.match.def, maskset, pos, true, opts) !== false))) {
  41. staticAlternations.push(tests[i]);
  42. if (tests[i].match.static === true) prevTest = tests[i];
  43. if (staticAlternations.length > 1) {
  44. if (/[0-9a-bA-Z]/.test(staticAlternations[0].match.def)) {
  45. return opts.placeholder.charAt(pos % opts.placeholder.length);
  46. }
  47. }
  48. }
  49. }
  50. }
  51. }
  52. return test.def;
  53. }
  54. return opts.placeholder.charAt(pos % opts.placeholder.length);
  55. }
  56. //tobe put on prototype?
  57. function getMaskTemplate(baseOnInput, minimalPos, includeMode, noJit, clearOptionalTail) {
  58. //includeMode true => input, undefined => placeholder, false => mask
  59. var inputmask = this,
  60. opts = this.opts,
  61. maskset = this.maskset;
  62. var greedy = opts.greedy;
  63. if (clearOptionalTail && opts.greedy) {
  64. opts.greedy = false;
  65. inputmask.maskset.tests = {};
  66. }
  67. minimalPos = minimalPos || 0;
  68. var maskTemplate = [],
  69. ndxIntlzr, pos = 0,
  70. test, testPos, jitRenderStatic;
  71. do {
  72. if (baseOnInput === true && maskset.validPositions[pos]) {
  73. testPos = (clearOptionalTail && maskset.validPositions[pos].match.optionality
  74. && maskset.validPositions[pos + 1] === undefined
  75. && (maskset.validPositions[pos].generatedInput === true || (maskset.validPositions[pos].input == opts.skipOptionalPartCharacter && pos > 0)))
  76. ? determineTestTemplate.call(inputmask, pos, getTests.call(inputmask, pos, ndxIntlzr, pos - 1))
  77. : maskset.validPositions[pos];
  78. test = testPos.match;
  79. ndxIntlzr = testPos.locator.slice();
  80. maskTemplate.push(includeMode === true ? testPos.input : includeMode === false ? test.nativeDef : getPlaceholder.call(inputmask, pos, test));
  81. } else {
  82. testPos = getTestTemplate.call(inputmask, pos, ndxIntlzr, pos - 1);
  83. test = testPos.match;
  84. ndxIntlzr = testPos.locator.slice();
  85. var jitMasking = noJit === true ? false : (opts.jitMasking !== false ? opts.jitMasking : test.jit);
  86. //check for groupSeparator is a hack for the numerics as we don't want the render of the groupSeparator beforehand
  87. jitRenderStatic = ((jitRenderStatic && test.static && test.def !== opts.groupSeparator && test.fn === null) || (maskset.validPositions[pos - 1] && test.static && test.def !== opts.groupSeparator && test.fn === null)) && maskset.tests[pos] && maskset.tests[pos].length === 1;
  88. if (jitRenderStatic || jitMasking === false || jitMasking === undefined /*|| pos < lvp*/ || (typeof jitMasking === "number" && isFinite(jitMasking) && jitMasking > pos)) {
  89. maskTemplate.push(includeMode === false ? test.nativeDef : getPlaceholder.call(inputmask, maskTemplate.length, test));
  90. } else {
  91. jitRenderStatic = false;
  92. }
  93. }
  94. pos++;
  95. } while ((test.static !== true || test.def !== "") || minimalPos > pos);
  96. if (maskTemplate[maskTemplate.length - 1] === "") {
  97. maskTemplate.pop(); //drop the last one which is empty
  98. }
  99. if (includeMode !== false || //do not alter the masklength when just retrieving the maskdefinition
  100. maskset.maskLength === undefined) //just make sure the maskLength gets initialized in all cases (needed for isValid)
  101. {
  102. maskset.maskLength = pos - 1;
  103. }
  104. opts.greedy = greedy;
  105. return maskTemplate;
  106. }
  107. //tobe put on prototype?
  108. function getTestTemplate(pos, ndxIntlzr, tstPs) {
  109. var inputmask = this,
  110. maskset = this.maskset;
  111. return maskset.validPositions[pos] || determineTestTemplate.call(inputmask, pos, getTests.call(inputmask, pos, ndxIntlzr ? ndxIntlzr.slice() : ndxIntlzr, tstPs));
  112. }
  113. //tobe put on prototype?
  114. function determineTestTemplate(pos, tests) {
  115. var inputmask = this,
  116. opts = this.opts,
  117. lenghtOffset = 0;
  118. var optionalityLevel = determineOptionalityLevel(pos, tests);
  119. pos = pos > 0 ? pos - 1 : 0;
  120. var altTest = getTest.call(inputmask, pos), targetLocator = getLocator(altTest), tstLocator, closest, bestMatch;
  121. if (opts.greedy && tests.length > 1 && tests[tests.length - 1].match.def === "")
  122. lenghtOffset = 1;
  123. // console.log(" optionality = " + optionalityLevel);
  124. // console.log(" - " + JSON.stringify(tests));
  125. for (var ndx = 0; ndx < tests.length - lenghtOffset; ndx++) { //find best matching
  126. var tst = tests[ndx];
  127. tstLocator = getLocator(tst, targetLocator.length);
  128. var distance = Math.abs(tstLocator - targetLocator);
  129. if (closest === undefined
  130. || (tstLocator !== "" && distance < closest)
  131. || (bestMatch && !opts.greedy &&
  132. (bestMatch.match.optionality && bestMatch.match.optionality - optionalityLevel > 0) &&
  133. bestMatch.match.newBlockMarker === "master" &&
  134. ((!tst.match.optionality || tst.match.optionality - optionalityLevel < 1) || !tst.match.newBlockMarker))
  135. || (bestMatch && !opts.greedy && bestMatch.match.optionalQuantifier && !tst.match.optionalQuantifier)) {
  136. closest = distance;
  137. bestMatch = tst;
  138. }
  139. }
  140. return bestMatch;
  141. }
  142. function determineOptionalityLevel(pos, tests) {
  143. let optionalityLevel = 0, differentOptionalLevels = false;
  144. tests.forEach(test => {
  145. if (test.match.optionality) {
  146. if (optionalityLevel !== 0 && optionalityLevel !== test.match.optionality)
  147. differentOptionalLevels = true;
  148. if (optionalityLevel === 0 || optionalityLevel > test.match.optionality) {
  149. optionalityLevel = test.match.optionality;
  150. }
  151. }
  152. });
  153. if (optionalityLevel) {
  154. if (pos == 0) optionalityLevel = 0;
  155. else if (tests.length == 1) optionalityLevel = 0;
  156. else if (!differentOptionalLevels) optionalityLevel = 0;
  157. }
  158. return optionalityLevel;
  159. }
  160. //tobe put on prototype?
  161. function getTest(pos, tests) {
  162. var inputmask = this,
  163. maskset = this.maskset;
  164. if (maskset.validPositions[pos]) {
  165. return maskset.validPositions[pos];
  166. }
  167. return (tests || getTests.call(inputmask, pos))[0];
  168. }
  169. function isSubsetOf(source, target, opts) {
  170. function expand(pattern) {
  171. var expanded = [], start = -1, end;
  172. for (var i = 0, l = pattern.length; i < l; i++) {
  173. if (pattern.charAt(i) === "-") {
  174. end = pattern.charCodeAt(i + 1);
  175. while (++start < end) expanded.push(String.fromCharCode(start));
  176. } else {
  177. start = pattern.charCodeAt(i);
  178. expanded.push(pattern.charAt(i));
  179. }
  180. }
  181. return expanded.join("");
  182. }
  183. if (source.match.def === target.match.nativeDef) return true;
  184. if ((opts.regex || (source.match.fn instanceof RegExp && target.match.fn instanceof RegExp)) && source.match.static !== true && target.match.static !== true) { //is regex a subset
  185. return expand(target.match.fn.toString().replace(/[[\]/]/g, "")).indexOf(expand(source.match.fn.toString().replace(/[[\]/]/g, ""))) !== -1;
  186. }
  187. return false;
  188. }
  189. //tobe put on prototype?
  190. function getTests(pos, ndxIntlzr, tstPs) {
  191. var inputmask = this,
  192. $ = this.dependencyLib,
  193. maskset = this.maskset,
  194. opts = this.opts,
  195. el = this.el,
  196. maskTokens = maskset.maskToken,
  197. testPos = ndxIntlzr ? tstPs : 0,
  198. ndxInitializer = ndxIntlzr ? ndxIntlzr.slice() : [0],
  199. matches = [],
  200. insertStop = false,
  201. latestMatch,
  202. cacheDependency = ndxIntlzr ? ndxIntlzr.join("") : "";
  203. function resolveTestFromToken(maskToken, ndxInitializer, loopNdx, quantifierRecurse) { //ndxInitializer contains a set of indexes to speedup searches in the mtokens
  204. function handleMatch(match, loopNdx, quantifierRecurse) {
  205. function isFirstMatch(latestMatch, tokenGroup) {
  206. var firstMatch = tokenGroup.matches.indexOf(latestMatch) === 0;
  207. if (!firstMatch) {
  208. tokenGroup.matches.every(function (match, ndx) {
  209. if (match.isQuantifier === true) {
  210. firstMatch = isFirstMatch(latestMatch, tokenGroup.matches[ndx - 1]);
  211. } else if (Object.prototype.hasOwnProperty.call(match, "matches")) firstMatch = isFirstMatch(latestMatch, match);
  212. if (firstMatch) return false;
  213. return true;
  214. });
  215. }
  216. return firstMatch;
  217. }
  218. function resolveNdxInitializer(pos, alternateNdx, targetAlternation) {
  219. var bestMatch, indexPos;
  220. if (maskset.tests[pos] || maskset.validPositions[pos]) {
  221. (maskset.tests[pos] || [maskset.validPositions[pos]]).every(function (lmnt, ndx) {
  222. if (lmnt.mloc[alternateNdx]) {
  223. bestMatch = lmnt;
  224. return false; //break
  225. }
  226. var alternation = targetAlternation !== undefined ? targetAlternation : lmnt.alternation,
  227. ndxPos = lmnt.locator[alternation] !== undefined ? lmnt.locator[alternation].toString().indexOf(alternateNdx) : -1;
  228. if ((indexPos === undefined || ndxPos < indexPos) && ndxPos !== -1) {
  229. bestMatch = lmnt;
  230. indexPos = ndxPos;
  231. }
  232. return true;
  233. });
  234. }
  235. if (bestMatch) {
  236. var bestMatchAltIndex = bestMatch.locator[bestMatch.alternation];
  237. var locator = bestMatch.mloc[alternateNdx] || bestMatch.mloc[bestMatchAltIndex] || bestMatch.locator;
  238. return locator.slice((targetAlternation !== undefined ? targetAlternation : bestMatch.alternation) + 1);
  239. } else {
  240. return targetAlternation !== undefined ? resolveNdxInitializer(pos, alternateNdx) : undefined;
  241. }
  242. }
  243. function staticCanMatchDefinition(source, target) {
  244. return source.match.static === true && target.match.static !== true ? target.match.fn.test(source.match.def, maskset, pos, false, opts, false) : false;
  245. }
  246. //mergelocators for retrieving the correct locator match when merging
  247. function setMergeLocators(targetMatch, altMatch) {
  248. var alternationNdx = targetMatch.alternation,
  249. shouldMerge = altMatch === undefined || (alternationNdx === altMatch.alternation &&
  250. targetMatch.locator[alternationNdx].toString().indexOf(altMatch.locator[alternationNdx]) === -1);
  251. if (!shouldMerge && alternationNdx > altMatch.alternation) {
  252. for (var i = altMatch.alternation; i < alternationNdx; i++) {
  253. if (targetMatch.locator[i] !== altMatch.locator[i]) {
  254. alternationNdx = i;
  255. shouldMerge = true;
  256. break;
  257. }
  258. }
  259. }
  260. if (shouldMerge) {
  261. targetMatch.mloc = targetMatch.mloc || {};
  262. var locNdx = targetMatch.locator[alternationNdx];
  263. if (locNdx === undefined) {
  264. targetMatch.alternation = undefined;
  265. } else {
  266. if (typeof locNdx === "string") locNdx = locNdx.split(",")[0];
  267. if (targetMatch.mloc[locNdx] === undefined) targetMatch.mloc[locNdx] = targetMatch.locator.slice();
  268. if (altMatch !== undefined) {
  269. for (var ndx in altMatch.mloc) {
  270. if (typeof ndx === "string") ndx = ndx.split(",")[0];
  271. if (targetMatch.mloc[ndx] === undefined) targetMatch.mloc[ndx] = altMatch.mloc[ndx];
  272. }
  273. targetMatch.locator[alternationNdx] = Object.keys(targetMatch.mloc).join(",");
  274. }
  275. return true;
  276. }
  277. }
  278. return false;
  279. }
  280. function isSameLevel(targetMatch, altMatch) {
  281. if (targetMatch.locator.length !== altMatch.locator.length) {
  282. return false;
  283. }
  284. for (let locNdx = targetMatch.alternation + 1; locNdx < targetMatch.locator.length; locNdx++) {
  285. if (targetMatch.locator[locNdx] !== altMatch.locator[locNdx]) {
  286. return false;
  287. }
  288. }
  289. return true;
  290. }
  291. function handleGroup() {
  292. match = handleMatch(maskToken.matches[maskToken.matches.indexOf(match) + 1], loopNdx, quantifierRecurse);
  293. if (match) return true;
  294. }
  295. function handleOptional() {
  296. var optionalToken = match, mtchsNdx = matches.length;
  297. match = resolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
  298. if (matches.length > 0) { //check on matches.length instead of match to handle quantifier in a recursive call
  299. //mark optionality in matches
  300. matches.forEach(function (mtch, ndx) {
  301. if (ndx >= mtchsNdx) {
  302. mtch.match.optionality = mtch.match.optionality ? mtch.match.optionality + 1 : 1;
  303. }
  304. });
  305. latestMatch = matches[matches.length - 1].match;
  306. if (quantifierRecurse === undefined && isFirstMatch(latestMatch, optionalToken)) { //prevent loop see #698
  307. insertStop = true; //insert a stop
  308. testPos = pos; //match the position after the group
  309. } else {
  310. return match; //make the loop continue when it is deliberately by a quantifier
  311. }
  312. }
  313. }
  314. function handleAlternator() {
  315. inputmask.hasAlternator = true;
  316. var alternateToken = match,
  317. malternateMatches = [],
  318. maltMatches,
  319. currentMatches = matches.slice(),
  320. loopNdxCnt = loopNdx.length,
  321. unMatchedAlternation = false;
  322. var altIndex = ndxInitializer.length > 0 ? ndxInitializer.shift() : -1;
  323. if (altIndex === -1 || typeof altIndex === "string") {
  324. var currentPos = testPos,
  325. ndxInitializerClone = ndxInitializer.slice(),
  326. altIndexArr = [],
  327. amndx;
  328. if (typeof altIndex == "string") {
  329. altIndexArr = altIndex.split(",");
  330. } else {
  331. for (amndx = 0; amndx < alternateToken.matches.length; amndx++) {
  332. altIndexArr.push(amndx.toString());
  333. }
  334. }
  335. if (maskset.excludes[pos] !== undefined) {
  336. var altIndexArrClone = altIndexArr.slice();
  337. for (var i = 0, exl = maskset.excludes[pos].length; i < exl; i++) {
  338. var excludeSet = maskset.excludes[pos][i].toString().split(":");
  339. if (loopNdx.length == excludeSet[1]) {
  340. altIndexArr.splice(altIndexArr.indexOf(excludeSet[0]), 1);
  341. }
  342. }
  343. if (altIndexArr.length === 0) { //fully alternated => reset
  344. delete maskset.excludes[pos];
  345. altIndexArr = altIndexArrClone;
  346. }
  347. }
  348. if (opts.keepStatic === true || (isFinite(parseInt(opts.keepStatic)) && currentPos >= opts.keepStatic)) altIndexArr = altIndexArr.slice(0, 1);
  349. for (var ndx = 0; ndx < altIndexArr.length; ndx++) {
  350. amndx = parseInt(altIndexArr[ndx]);
  351. matches = [];
  352. //set the correct ndxInitializer
  353. ndxInitializer = typeof altIndex === "string" ? resolveNdxInitializer(testPos, amndx, loopNdxCnt) || ndxInitializerClone.slice() : ndxInitializerClone.slice();
  354. var tokenMatch = alternateToken.matches[amndx];
  355. if (tokenMatch && handleMatch(tokenMatch, [amndx].concat(loopNdx), quantifierRecurse)) {
  356. match = true;
  357. } else {
  358. if (ndx === 0) {
  359. unMatchedAlternation = true;
  360. }
  361. if (tokenMatch && tokenMatch.matches && tokenMatch.matches.length > alternateToken.matches[0].matches.length) {
  362. break;
  363. }
  364. }
  365. maltMatches = matches.slice();
  366. testPos = currentPos;
  367. matches = [];
  368. //fuzzy merge matches
  369. for (var ndx1 = 0; ndx1 < maltMatches.length; ndx1++) {
  370. var altMatch = maltMatches[ndx1],
  371. dropMatch = false;
  372. altMatch.match.jit = altMatch.match.jit || unMatchedAlternation; //mark jit when there are unmatched alternations ex: mask: "(a|aa)"
  373. altMatch.alternation = altMatch.alternation || loopNdxCnt;
  374. setMergeLocators(altMatch);
  375. for (var ndx2 = 0; ndx2 < malternateMatches.length; ndx2++) {
  376. var altMatch2 = malternateMatches[ndx2];
  377. if (typeof altIndex !== "string" || (altMatch.alternation !== undefined && altIndexArr.includes(altMatch.locator[altMatch.alternation].toString()))) {
  378. if (altMatch.match.nativeDef === altMatch2.match.nativeDef) {
  379. dropMatch = true;
  380. setMergeLocators(altMatch2, altMatch);
  381. break;
  382. } else if (isSubsetOf(altMatch, altMatch2, opts)) {
  383. if (setMergeLocators(altMatch, altMatch2)) {
  384. dropMatch = true;
  385. malternateMatches.splice(malternateMatches.indexOf(altMatch2), 0, altMatch);
  386. }
  387. break;
  388. } else if (isSubsetOf(altMatch2, altMatch, opts)) {
  389. setMergeLocators(altMatch2, altMatch);
  390. break;
  391. } else if (staticCanMatchDefinition(altMatch, altMatch2)) {
  392. if (!isSameLevel(altMatch, altMatch2) && el.inputmask.userOptions.keepStatic === undefined) {
  393. opts.keepStatic = true;
  394. } else if (setMergeLocators(altMatch, altMatch2)) {
  395. //insert match above general match
  396. dropMatch = true;
  397. malternateMatches.splice(malternateMatches.indexOf(altMatch2), 0, altMatch);
  398. }
  399. break;
  400. }
  401. }
  402. }
  403. if (!dropMatch) {
  404. malternateMatches.push(altMatch);
  405. }
  406. }
  407. }
  408. matches = currentMatches.concat(malternateMatches);
  409. testPos = pos;
  410. insertStop = matches.length > 0; //insert a stopelemnt when there is an alternate - needed for non-greedy option
  411. match = malternateMatches.length > 0; //set correct match state
  412. //cloneback
  413. ndxInitializer = ndxInitializerClone.slice();
  414. } else {
  415. match = handleMatch(alternateToken.matches[altIndex] || maskToken.matches[altIndex], [altIndex].concat(loopNdx), quantifierRecurse);
  416. }
  417. if (match) return true;
  418. }
  419. function handleQuantifier() {
  420. var qt = match, breakloop = false;
  421. for (var qndx = (ndxInitializer.length > 0) ? ndxInitializer.shift() : 0; (qndx < (isNaN(qt.quantifier.max) ? qndx + 1 : qt.quantifier.max)) && testPos <= pos; qndx++) {
  422. var tokenGroup = maskToken.matches[maskToken.matches.indexOf(qt) - 1];
  423. match = handleMatch(tokenGroup, [qndx].concat(loopNdx), tokenGroup); //set the tokenGroup as quantifierRecurse marker
  424. if (match) {
  425. matches.forEach(function (mtch, ndx) {
  426. if (IsMatchOf(tokenGroup, mtch.match))
  427. latestMatch = mtch.match;
  428. else latestMatch = matches[matches.length - 1].match;
  429. //mark optionality
  430. //TODO FIX RECURSIVE QUANTIFIERS
  431. latestMatch.optionalQuantifier = qndx >= qt.quantifier.min;
  432. // console.log(pos + " " + qt.quantifier.min + " " + latestMatch.optionalQuantifier);
  433. //qndx + 1 as the index starts from 0
  434. latestMatch.jit = (qndx + 1) * (tokenGroup.matches.indexOf(latestMatch) + 1) > qt.quantifier.jit;
  435. if (latestMatch.optionalQuantifier && isFirstMatch(latestMatch, tokenGroup)) {
  436. insertStop = true;
  437. testPos = pos; //match the position after the group
  438. if (opts.greedy && maskset.validPositions[pos - 1] == undefined && qndx > qt.quantifier.min && ["*", "+"].indexOf(qt.quantifier.max) != -1) {
  439. matches.pop();
  440. cacheDependency = undefined;
  441. }
  442. breakloop = true; //stop quantifierloop && search for next possible match
  443. match = false; //mark match to false to make sure the loop in optionals continues
  444. }
  445. if (!breakloop && latestMatch.jit /*&& !latestMatch.optionalQuantifier*/) {
  446. //always set jitOffset, isvalid checks when to apply
  447. maskset.jitOffset[pos] = tokenGroup.matches.length - tokenGroup.matches.indexOf(latestMatch);
  448. }
  449. });
  450. if (breakloop) break; // search for next possible match
  451. return true;
  452. }
  453. }
  454. }
  455. if (testPos > (pos + opts._maxTestPos)) {
  456. throw "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;
  457. }
  458. if (testPos === pos && match.matches === undefined) {
  459. matches.push({
  460. "match": match,
  461. "locator": loopNdx.reverse(),
  462. "cd": cacheDependency,
  463. "mloc": {}
  464. });
  465. if (match.optionality && quantifierRecurse === undefined &&
  466. ((opts.definitions && opts.definitions[match.nativeDef] && opts.definitions[match.nativeDef].optional) ||
  467. (Inputmask.prototype.definitions[match.nativeDef] && Inputmask.prototype.definitions[match.nativeDef].optional))) { //prevent loop see #698
  468. insertStop = true; //insert a stop
  469. testPos = pos; //match the position after the group
  470. } else {
  471. return true;
  472. }
  473. } else if (match.matches !== undefined) {
  474. if (match.isGroup && quantifierRecurse !== match) { //when a group pass along to the quantifier
  475. return handleGroup();
  476. } else if (match.isOptional) {
  477. return handleOptional();
  478. } else if (match.isAlternator) {
  479. return handleAlternator();
  480. } else if (match.isQuantifier && quantifierRecurse !== maskToken.matches[maskToken.matches.indexOf(match) - 1]) {
  481. return handleQuantifier();
  482. } else {
  483. match = resolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
  484. if (match) return true;
  485. }
  486. } else {
  487. testPos++;
  488. }
  489. }
  490. //the offset is set in the quantifierloop when git masking is used
  491. for (var tndx = (ndxInitializer.length > 0 ? ndxInitializer.shift() : 0); tndx < maskToken.matches.length; tndx++) {
  492. if (maskToken.matches[tndx].isQuantifier !== true) {
  493. var match = handleMatch(maskToken.matches[tndx], [tndx].concat(loopNdx), quantifierRecurse);
  494. if (match && testPos === pos) {
  495. return match;
  496. } else if (testPos > pos) {
  497. break;
  498. }
  499. }
  500. }
  501. }
  502. function IsMatchOf(tokenGroup, match) {
  503. let isMatch = tokenGroup.matches.indexOf(match) != -1;
  504. if (!isMatch) {
  505. tokenGroup.matches.forEach((mtch, ndx) => {
  506. if (mtch.matches !== undefined && !isMatch) {
  507. isMatch = IsMatchOf(mtch, match);
  508. }
  509. });
  510. }
  511. return isMatch;
  512. }
  513. function mergeLocators(pos, tests) {
  514. let locator = [], alternation;
  515. if (!Array.isArray(tests)) tests = [tests];
  516. if (tests.length > 0) {
  517. if (tests[0].alternation === undefined || opts.keepStatic === true) {
  518. locator = determineTestTemplate.call(inputmask, pos, tests.slice()).locator.slice();
  519. if (locator.length === 0) locator = tests[0].locator.slice();
  520. } else {
  521. tests.forEach(function (tst) {
  522. if (tst.def !== "") {
  523. if (locator.length === 0) {
  524. alternation = tst.alternation;
  525. locator = tst.locator.slice();
  526. } else {
  527. if (tst.locator[alternation] && locator[alternation].toString().indexOf(tst.locator[alternation]) === -1) {
  528. locator[alternation] += "," + tst.locator[alternation];
  529. }
  530. }
  531. }
  532. });
  533. }
  534. }
  535. return locator;
  536. }
  537. if (pos > -1) {
  538. if (ndxIntlzr === undefined) { //determine index initializer
  539. var previousPos = pos - 1,
  540. test;
  541. while ((test = maskset.validPositions[previousPos] || maskset.tests[previousPos]) === undefined && previousPos > -1) {
  542. previousPos--;
  543. }
  544. if (test !== undefined && previousPos > -1) {
  545. ndxInitializer = mergeLocators(previousPos, test);
  546. cacheDependency = ndxInitializer.join("");
  547. testPos = previousPos;
  548. }
  549. }
  550. if (maskset.tests[pos] && maskset.tests[pos][0].cd === cacheDependency) { //cacheDependency is set on all tests, just check on the first
  551. return maskset.tests[pos];
  552. }
  553. for (var mtndx = ndxInitializer.shift(); mtndx < maskTokens.length; mtndx++) {
  554. var match = resolveTestFromToken(maskTokens[mtndx], ndxInitializer, [mtndx]);
  555. if ((match && testPos === pos) || testPos > pos) {
  556. break;
  557. }
  558. }
  559. }
  560. if (matches.length === 0 || insertStop) {
  561. matches.push({
  562. match: {
  563. fn: null,
  564. static: true,
  565. optionality: false,
  566. casing: null,
  567. def: "",
  568. placeholder: ""
  569. },
  570. locator: [],
  571. mloc: {},
  572. cd: cacheDependency
  573. });
  574. }
  575. var result;
  576. if (ndxIntlzr !== undefined && maskset.tests[pos]) { //prioritize full tests for caching
  577. result = $.extend(true, [], matches);
  578. } else {
  579. maskset.tests[pos] = $.extend(true, [], matches); //set a clone to prevent overwriting some props
  580. result = maskset.tests[pos];
  581. }
  582. // console.log(pos + " - " + JSON.stringify(matches));
  583. //cleanup optionality marking
  584. matches.forEach(t => {
  585. t.match.optionality = t.match.defOptionality || false;
  586. });
  587. return result;
  588. }