import Inputmask from "./inputmask"; import { getLastValidPosition, seekNext } from "./positioning"; export { determineTestTemplate, getDecisionTaker, getMaskTemplate, getPlaceholder, getTest, getTests, getTestTemplate, isSubsetOf }; function getLocator(tst, align) { // need to align the locators to be correct let locator = ( tst.alternation != undefined ? tst.mloc[getDecisionTaker(tst)] : tst.locator ).join(""); if (locator !== "") { locator = locator.split(":")[0]; // strip off alternation marker while (locator.length < align) locator += "0"; } return locator; } function getDecisionTaker(tst) { let decisionTaker = tst.locator[tst.alternation]; if (typeof decisionTaker === "string" && decisionTaker.length > 0) { // no decision taken ~ take first one as decider decisionTaker = decisionTaker.split(",")[0]; } return decisionTaker !== undefined ? decisionTaker.toString() : ""; } // tobe put on prototype? function getPlaceholder(pos, test, returnPL) { const inputmask = this, opts = this.opts, maskset = this.maskset; test = test || getTest.call(inputmask, pos).match; if (test.placeholder !== undefined || returnPL === true) { if ( test.placeholder !== "" && test.static === true && test.generated !== true ) { // static and not dynamically generated ~ does not occur in regex mask ~ numeric alias def is not a valid entry const lvp = getLastValidPosition.call(inputmask, pos), nextPos = seekNext.call(inputmask, lvp); return (returnPL ? pos <= nextPos : pos < nextPos) ? opts.staticDefinitionSymbol && test.static ? test.nativeDef : test.def : typeof test.placeholder === "function" ? test.placeholder(opts) : test.placeholder; } else { return typeof test.placeholder === "function" ? test.placeholder(opts) : test.placeholder; } } else if (test.static === true) { if (pos > -1 && maskset.validPositions[pos] === undefined) { let tests = getTests.call(inputmask, pos), staticAlternations = [], prevTest; if ( typeof opts.placeholder === "string" && tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0) ) { for (let i = 0; i < tests.length; i++) { if ( tests[i].match.def !== "" && tests[i].match.optionality !== true && tests[i].match.optionalQuantifier !== true && (tests[i].match.static === true || prevTest === undefined || tests[i].match.fn.test( prevTest.match.def, maskset, pos, true, opts ) !== false) ) { staticAlternations.push(tests[i]); if (tests[i].match.static === true) prevTest = tests[i]; if (staticAlternations.length > 1) { if (/[0-9a-bA-Z]/.test(staticAlternations[0].match.def)) { return opts.placeholder.charAt(pos % opts.placeholder.length); } } } } } } return test.def; } return typeof opts.placeholder === "object" ? test.def : opts.placeholder.charAt(pos % opts.placeholder.length); } // tobe put on prototype? function getMaskTemplate( baseOnInput, minimalPos, includeMode, noJit, clearOptionalTail ) { // includeMode true => input, undefined => placeholder, false => mask const inputmask = this, opts = this.opts, maskset = this.maskset, greedy = opts.greedy; if (clearOptionalTail && opts.greedy) { opts.greedy = false; inputmask.maskset.tests = {}; } minimalPos = minimalPos || 0; let maskTemplate = [], ndxIntlzr, pos = 0, test, testPos, jitRenderStatic; do { if (baseOnInput === true && maskset.validPositions[pos]) { testPos = clearOptionalTail && maskset.validPositions[pos].match.optionality && maskset.validPositions[pos + 1] === undefined && (maskset.validPositions[pos].generatedInput === true || (maskset.validPositions[pos].input == opts.skipOptionalPartCharacter && pos > 0)) ? determineTestTemplate.call( inputmask, pos, getTests.call(inputmask, pos, ndxIntlzr, pos - 1) ) : maskset.validPositions[pos]; test = testPos.match; ndxIntlzr = testPos.locator.slice(); maskTemplate.push( includeMode === true ? testPos.input : includeMode === false ? test.nativeDef : getPlaceholder.call(inputmask, pos, test) ); } else { testPos = getTestTemplate.call(inputmask, pos, ndxIntlzr, pos - 1); test = testPos.match; ndxIntlzr = testPos.locator.slice(); const jitMasking = noJit === true ? false : opts.jitMasking !== false ? opts.jitMasking : test.jit; // check for groupSeparator is a hack for the numerics as we don't want the render of the groupSeparator beforehand jitRenderStatic = (jitRenderStatic || maskset.validPositions[ pos - 1 ]) /* && getTest.call(inputmask, pos + 1).match.def == "" */ && test.static && test.def !== opts.groupSeparator && test.fn === null; if ( jitRenderStatic || jitMasking === false || jitMasking === undefined /* || pos < lvp */ || (typeof jitMasking === "number" && isFinite(jitMasking) && jitMasking > pos) ) { maskTemplate.push( includeMode === false ? test.nativeDef : getPlaceholder.call(inputmask, maskTemplate.length, test) ); } else { jitRenderStatic = false; } } pos++; } while (test.static !== true || test.def !== "" || minimalPos > pos); if (maskTemplate[maskTemplate.length - 1] === "") { maskTemplate.pop(); // drop the last one which is empty } if ( includeMode !== false || // do not alter the masklength when just retrieving the maskdefinition maskset.maskLength === undefined ) { // just make sure the maskLength gets initialized in all cases (needed for isValid) maskset.maskLength = pos - 1; } opts.greedy = greedy; return maskTemplate; } // tobe put on prototype? function getTestTemplate(pos, ndxIntlzr, tstPs) { const inputmask = this, maskset = this.maskset; return ( maskset.validPositions[pos] || determineTestTemplate.call( inputmask, pos, getTests.call( inputmask, pos, ndxIntlzr ? ndxIntlzr.slice() : ndxIntlzr, tstPs ) ) ); } // tobe put on prototype? function determineTestTemplate(pos, tests) { let inputmask = this, opts = this.opts, lenghtOffset = 0, optionalityLevel = determineOptionalityLevel(pos, tests); pos = pos > 0 ? pos - 1 : 0; let altTest = getTest.call(inputmask, pos), targetLocator = getLocator(altTest), tstLocator, closest, bestMatch; if ( opts.greedy && tests.length > 1 && tests[tests.length - 1].match.def === "" ) lenghtOffset = 1; // console.log(" optionality = " + optionalityLevel); // console.log(" - " + JSON.stringify(tests)); for (let ndx = 0; ndx < tests.length - lenghtOffset; ndx++) { // find best matching const tst = tests[ndx]; tstLocator = getLocator(tst, targetLocator.length); const distance = Math.abs(tstLocator - targetLocator); if ( tst.unMatchedAlternationStopped !== true || tests.filter((tst) => tst.unMatchedAlternationStopped !== true).length <= 1 ) { // only skip when there are choices outside the alternation if ( closest === undefined || (tstLocator !== "" && distance < closest) || (bestMatch && !opts.greedy && bestMatch.match.optionality && bestMatch.match.optionality - optionalityLevel > 0 && bestMatch.match.newBlockMarker === "master" && (!tst.match.optionality || tst.match.optionality - optionalityLevel < 1 || !tst.match.newBlockMarker)) || (bestMatch && !opts.greedy && bestMatch.match.optionalQuantifier && !tst.match.optionalQuantifier) ) { closest = distance; bestMatch = tst; } } } return bestMatch; } function determineOptionalityLevel(pos, tests) { let optionalityLevel = 0, differentOptionalLevels = false; tests.forEach((test) => { if (test.match.optionality) { if (optionalityLevel !== 0 && optionalityLevel !== test.match.optionality) differentOptionalLevels = true; if (optionalityLevel === 0 || optionalityLevel > test.match.optionality) { optionalityLevel = test.match.optionality; } } }); if (optionalityLevel) { if (pos == 0) optionalityLevel = 0; else if (tests.length == 1) optionalityLevel = 0; else if (!differentOptionalLevels) optionalityLevel = 0; } return optionalityLevel; } // tobe put on prototype? function getTest(pos, tests) { const inputmask = this, maskset = this.maskset; if (maskset.validPositions[pos]) { return maskset.validPositions[pos]; } return (tests || getTests.call(inputmask, pos))[0]; } function isSubsetOf(source, target, opts) { function expand(pattern) { let expanded = [], start = -1, end; for (let i = 0, l = pattern.length; i < l; i++) { if (pattern.charAt(i) === "-") { end = pattern.charCodeAt(i + 1); while (++start < end) expanded.push(String.fromCharCode(start)); } else { start = pattern.charCodeAt(i); expanded.push(pattern.charAt(i)); } } return expanded.join(""); } if (source.match.def === target.match.nativeDef) return true; 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 if (target.match.fn.source === ".") return true; return ( expand(target.match.fn.source.replace(/[[\]/]/g, "")).indexOf( expand(source.match.fn.source.replace(/[[\]/]/g, "")) ) !== -1 ); } return false; } // tobe put on prototype? function getTests(pos, ndxIntlzr, tstPs) { let inputmask = this, $ = this.dependencyLib, maskset = this.maskset, opts = this.opts, el = this.el, maskTokens = maskset.maskToken, testPos = ndxIntlzr ? tstPs : 0, ndxInitializer = ndxIntlzr ? ndxIntlzr.slice() : [0], matches = [], insertStop = false, latestMatch, cacheDependency = ndxIntlzr ? ndxIntlzr.join("") : "", unMatchedAlternation = false; function resolveTestFromToken( maskToken, ndxInitializer, loopNdx, quantifierRecurse ) { // ndxInitializer contains a set of indexes to speedup searches in the mtokens function handleMatch(match, loopNdx, quantifierRecurse) { function isFirstMatch(latestMatch, tokenGroup) { let firstMatch = tokenGroup.matches.indexOf(latestMatch) === 0; if (!firstMatch) { tokenGroup.matches.every(function (match, ndx) { if (match.isQuantifier === true) { firstMatch = isFirstMatch( latestMatch, tokenGroup.matches[ndx - 1] ); } else if (Object.prototype.hasOwnProperty.call(match, "matches")) firstMatch = isFirstMatch(latestMatch, match); if (firstMatch) return false; return true; }); } return firstMatch; } function resolveNdxInitializer(pos, alternateNdx, targetAlternation) { let bestMatch, indexPos; if (maskset.tests[pos] || maskset.validPositions[pos]) { (maskset.validPositions[pos] ? [maskset.validPositions[pos]] : maskset.tests[pos] ).every(function (lmnt, ndx) { if (lmnt.mloc[alternateNdx]) { bestMatch = lmnt; return false; // break } const alternation = targetAlternation !== undefined ? targetAlternation : lmnt.alternation, ndxPos = lmnt.locator[alternation] !== undefined ? lmnt.locator[alternation].toString().indexOf(alternateNdx) : -1; if ( (indexPos === undefined || ndxPos < indexPos) && ndxPos !== -1 ) { bestMatch = lmnt; indexPos = ndxPos; } return true; }); } if (bestMatch) { const bestMatchAltIndex = bestMatch.locator[bestMatch.alternation], locator = bestMatch.mloc[alternateNdx] || bestMatch.mloc[bestMatchAltIndex] || bestMatch.locator; if (locator[locator.length - 1].toString().indexOf(":") !== -1) { // eslint-disable-next-line no-unused-vars const alternation = locator.pop(); // targetAlternation = parseInt(alternation.substring(1)); } return locator.slice( (targetAlternation !== undefined ? targetAlternation : bestMatch.alternation) + 1 ); } else { return targetAlternation !== undefined ? resolveNdxInitializer(pos, alternateNdx) : undefined; } } function staticCanMatchDefinition(source, target) { return source.match.static === true && target.match.static !== true ? target.match.fn.test( source.match.def, maskset, pos, false, opts, false ) : false; } // mergelocators for retrieving the correct locator match when merging function setMergeLocators(targetMatch, altMatch) { function mergeLoc(altNdx) { targetMatch.mloc = targetMatch.mloc || {}; let locNdx = targetMatch.locator[altNdx]; if (locNdx === undefined) { targetMatch.alternation = undefined; } else { if (typeof locNdx === "string") locNdx = locNdx.split(",")[0]; if (targetMatch.mloc[locNdx] === undefined) { targetMatch.mloc[locNdx] = targetMatch.locator.slice(); targetMatch.mloc[locNdx].push(`:${targetMatch.alternation}`); // add alternation index } if (altMatch !== undefined) { const offset = 0; for (let ndx in altMatch.mloc) { if (typeof ndx === "string") ndx = parseInt(ndx.split(",")[0]); // do { // if (targetMatch.mloc[ndx + offset] === undefined) { targetMatch.mloc[ndx + offset] = altMatch.mloc[ndx]; // break; // } // } while (targetMatch.mloc[ndx + offset++] !== undefined); } targetMatch.locator[altNdx] = Object.keys(targetMatch.mloc).join( "," ); } if (targetMatch.alternation > altNdx) { // if the alternation index is higher than the current one resolve it to the alternation targetMatch.alternation = altNdx; } return true; } return false; } let alternationNdx = targetMatch.alternation, shouldMerge = altMatch === undefined || (alternationNdx <= altMatch.alternation && targetMatch.locator[alternationNdx] .toString() .indexOf(altMatch.locator[alternationNdx]) === -1); if (!shouldMerge && alternationNdx > altMatch.alternation) { for (let i = 0; i < alternationNdx; i++) { if (targetMatch.locator[i] !== altMatch.locator[i]) { alternationNdx = i; shouldMerge = true; break; } } } if (shouldMerge) { return mergeLoc(alternationNdx); } return false; } function isSameLevel(targetMatch, altMatch) { if (targetMatch.locator.length !== altMatch.locator.length) { return false; } for ( let locNdx = targetMatch.alternation + 1; locNdx < targetMatch.locator.length; locNdx++ ) { if (targetMatch.locator[locNdx] !== altMatch.locator[locNdx]) { return false; } } return true; } function handleGroup() { match = handleMatch( maskToken.matches[maskToken.matches.indexOf(match) + 1], loopNdx, quantifierRecurse ); if (match) return true; } function handleOptional() { const optionalToken = match, mtchsNdx = matches.length; match = resolveTestFromToken( match, ndxInitializer, loopNdx, quantifierRecurse ); if (matches.length > 0) { // check on matches.length instead of match to handle quantifier in a recursive call // mark optionality in matches matches.forEach(function (mtch, ndx) { if (ndx >= mtchsNdx) { mtch.match.optionality = mtch.match.optionality ? mtch.match.optionality + 1 : 1; } }); latestMatch = matches[matches.length - 1].match; if ( quantifierRecurse === undefined && isFirstMatch(latestMatch, optionalToken) ) { // prevent loop see #698 insertStop = true; // insert a stop testPos = pos; // match the position after the group } else { return match; // make the loop continue when it is deliberately by a quantifier } } } function handleAlternator() { function isUnmatchedAlternation(alternateToken) { let matchesLength = alternateToken.matches[0].matches ? alternateToken.matches[0].matches.length : 1, matchesNewLength; for (let alndx = 0; alndx < alternateToken.matches.length; alndx++) { matchesNewLength = alternateToken.matches[alndx].matches ? alternateToken.matches[alndx].matches.length : 1; if (matchesLength !== matchesNewLength) { break; } } return matchesLength !== matchesNewLength; } inputmask.hasAlternator = true; let alternateToken = match, malternateMatches = [], maltMatches, currentMatches = matches.slice(), loopNdxCnt = loopNdx.length, altIndex = ndxInitializer.length > 0 ? ndxInitializer.shift() : -1; if (altIndex === -1 || typeof altIndex === "string") { let currentPos = testPos, ndxInitializerClone = ndxInitializer.slice(), altIndexArr = [], amndx; if (typeof altIndex === "string") { altIndexArr = altIndex.split(","); } else { for (amndx = 0; amndx < alternateToken.matches.length; amndx++) { altIndexArr.push(amndx.toString()); } } if (maskset.excludes[pos] !== undefined) { const altIndexArrClone = altIndexArr.slice(); for (let i = 0, exl = maskset.excludes[pos].length; i < exl; i++) { const excludeSet = maskset.excludes[pos][i].toString().split(":"); if (loopNdx.length == excludeSet[1]) { altIndexArr.splice(altIndexArr.indexOf(excludeSet[0]), 1); } } if (altIndexArr.length === 0) { // fully alternated => reset delete maskset.excludes[pos]; altIndexArr = altIndexArrClone; } } if ( opts.keepStatic === true || (isFinite(parseInt(opts.keepStatic)) && currentPos >= opts.keepStatic) ) altIndexArr = altIndexArr.slice(0, 1); for (let ndx = 0; ndx < altIndexArr.length; ndx++) { amndx = parseInt(altIndexArr[ndx]); matches = []; // set the correct ndxInitializer ndxInitializer = typeof altIndex === "string" ? resolveNdxInitializer(testPos, amndx, loopNdxCnt) || ndxInitializerClone.slice() : ndxInitializerClone.slice(); // console.log("ndxInit", ndxInitializer); const tokenMatch = alternateToken.matches[amndx]; if ( tokenMatch && handleMatch( tokenMatch, [amndx].concat(loopNdx), quantifierRecurse ) ) { match = true; } else { if (ndx === 0) { unMatchedAlternation = isUnmatchedAlternation(alternateToken); } if ( tokenMatch && tokenMatch.matches && tokenMatch.matches.length > alternateToken.matches[0].matches.length ) { break; } } maltMatches = matches.slice(); testPos = currentPos; matches = []; // fuzzy merge matches for (let ndx1 = 0; ndx1 < maltMatches.length; ndx1++) { let altMatch = maltMatches[ndx1], dropMatch = false; altMatch.alternation = altMatch.alternation || loopNdxCnt; setMergeLocators(altMatch); for (let ndx2 = 0; ndx2 < malternateMatches.length; ndx2++) { const altMatch2 = malternateMatches[ndx2]; if ( typeof altIndex !== "string" || (altMatch.alternation !== undefined && altIndexArr.includes( altMatch.locator[altMatch.alternation].toString() )) ) { if (altMatch.match.nativeDef === altMatch2.match.nativeDef) { dropMatch = true; setMergeLocators(altMatch2, altMatch); break; } else if (isSubsetOf(altMatch, altMatch2, opts)) { if (setMergeLocators(altMatch, altMatch2)) { dropMatch = true; malternateMatches.splice( malternateMatches.indexOf(altMatch2), 0, altMatch ); } break; } else if (isSubsetOf(altMatch2, altMatch, opts)) { setMergeLocators(altMatch2, altMatch); break; } else if (staticCanMatchDefinition(altMatch, altMatch2)) { if ( !isSameLevel(altMatch, altMatch2) && el.inputmask.userOptions.keepStatic === undefined ) { opts.keepStatic = true; } else if (setMergeLocators(altMatch, altMatch2)) { // insert match above general match dropMatch = true; malternateMatches.splice( malternateMatches.indexOf(altMatch2), 0, altMatch ); } break; } else if (staticCanMatchDefinition(altMatch2, altMatch)) { setMergeLocators(altMatch2, altMatch); break; } } } if (!dropMatch) { malternateMatches.push(altMatch); } } } matches = currentMatches.concat(malternateMatches); testPos = pos; insertStop = matches.length > 0 && unMatchedAlternation; // insert a stopelemnt when there is an alternate - needed for non-greedy option match = malternateMatches.length > 0 && !unMatchedAlternation; // set correct match state if (unMatchedAlternation && insertStop && !match) { // mark matches with unMatchedAlternationStopped matches.forEach(function (mtch, ndx) { mtch.unMatchedAlternationStopped = true; }); } // cloneback ndxInitializer = ndxInitializerClone.slice(); } else { match = handleMatch( alternateToken.matches[altIndex] || maskToken.matches[altIndex], [altIndex].concat(loopNdx), quantifierRecurse ); } if (match) return true; } function handleQuantifier() { let qt = match, breakloop = false; for ( var qndx = ndxInitializer.length > 0 ? ndxInitializer.shift() : 0; qndx < (isNaN(qt.quantifier.max) ? qndx + 1 : qt.quantifier.max) && testPos <= pos; qndx++ ) { var tokenGroup = maskToken.matches[maskToken.matches.indexOf(qt) - 1]; match = handleMatch(tokenGroup, [qndx].concat(loopNdx), tokenGroup); // set the tokenGroup as quantifierRecurse marker if (match) { matches.forEach(function (mtch, ndx) { if (IsMatchOf(tokenGroup, mtch.match)) latestMatch = mtch.match; else latestMatch = matches[matches.length - 1].match; // mark optionality // TODO FIX RECURSIVE QUANTIFIERS latestMatch.optionalQuantifier = qndx >= qt.quantifier.min; // console.log(pos + " " + qt.quantifier.min + " " + latestMatch.optionalQuantifier); // qndx + 1 as the index starts from 0 latestMatch.jit = (qndx + 1) * (tokenGroup.matches.indexOf(latestMatch) + 1) > qt.quantifier.jit; if ( latestMatch.optionalQuantifier && isFirstMatch(latestMatch, tokenGroup) ) { insertStop = true; testPos = pos; // match the position after the group if ( opts.greedy && maskset.validPositions[pos - 1] == undefined && qndx > qt.quantifier.min && ["*", "+"].indexOf(qt.quantifier.max) != -1 ) { matches.pop(); cacheDependency = undefined; } breakloop = true; // stop quantifierloop && search for next possible match match = false; // mark match to false to make sure the loop in optionals continues } if ( !breakloop && latestMatch.jit /* && !latestMatch.optionalQuantifier */ ) { // always set jitOffset, isvalid checks when to apply maskset.jitOffset[pos] = tokenGroup.matches.length - tokenGroup.matches.indexOf(latestMatch); } }); if (breakloop) break; // search for next possible match return true; } } } if (testPos > pos + opts._maxTestPos) { throw new Error( `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}` ); } if (testPos === pos && match.matches === undefined) { matches.push({ match, locator: loopNdx.reverse(), cd: cacheDependency, mloc: {} }); if ( match.optionality && quantifierRecurse === undefined && ((opts.definitions && opts.definitions[match.nativeDef] && opts.definitions[match.nativeDef].optional) || (Inputmask.prototype.definitions[match.nativeDef] && Inputmask.prototype.definitions[match.nativeDef].optional)) ) { // prevent loop see #698 insertStop = true; // insert a stop testPos = pos; // match the position after the group } else { return true; } } else if (match.matches !== undefined) { if (match.isGroup && quantifierRecurse !== match) { // when a group pass along to the quantifier return handleGroup(); } else if (match.isOptional) { return handleOptional(); } else if (match.isAlternator) { return handleAlternator(); } else if ( match.isQuantifier && quantifierRecurse !== maskToken.matches[maskToken.matches.indexOf(match) - 1] ) { return handleQuantifier(); } else { match = resolveTestFromToken( match, ndxInitializer, loopNdx, quantifierRecurse ); if (match) return true; } } else { testPos++; } } // the offset is set in the quantifierloop when git masking is used for ( let tndx = ndxInitializer.length > 0 ? ndxInitializer.shift() : 0; tndx < maskToken.matches.length; tndx++ ) { if (maskToken.matches[tndx].isQuantifier !== true) { const match = handleMatch( maskToken.matches[tndx], [tndx].concat(loopNdx), quantifierRecurse ); if (match && testPos === pos) { return match; } else if (testPos > pos) { break; } } } } function IsMatchOf(tokenGroup, match) { let isMatch = tokenGroup.matches.indexOf(match) != -1; if (!isMatch) { tokenGroup.matches.forEach((mtch, ndx) => { if (mtch.matches !== undefined && !isMatch) { isMatch = IsMatchOf(mtch, match); } }); } return isMatch; } function mergeLocators(pos, tests) { let locator = [], alternation; if (!Array.isArray(tests)) tests = [tests]; if (tests.length > 0) { if (tests[0].alternation === undefined || opts.keepStatic === true) { locator = determineTestTemplate .call(inputmask, pos, tests.slice()) .locator.slice(); if (locator.length === 0) locator = tests[0].locator.slice(); } else { tests.forEach(function (tst) { if (tst.def !== "") { if (locator.length === 0) { alternation = tst.alternation; locator = tst.locator.slice(); } else { if ( tst.locator[alternation] && locator[alternation] .toString() .indexOf(tst.locator[alternation]) === -1 ) { locator[alternation] += "," + tst.locator[alternation]; } } } }); } } return locator; } if (pos > -1) { if (ndxIntlzr === undefined) { // determine index initializer let previousPos = pos - 1, test; while ( (test = maskset.validPositions[previousPos] || maskset.tests[previousPos]) === undefined && previousPos > -1 ) { previousPos--; } if (test !== undefined && previousPos > -1) { ndxInitializer = mergeLocators(previousPos, test); cacheDependency = ndxInitializer.join(""); testPos = previousPos; } } if (maskset.tests[pos] && maskset.tests[pos][0].cd === cacheDependency) { // cacheDependency is set on all tests, just check on the first return maskset.tests[pos]; } for ( let mtndx = ndxInitializer.shift(); mtndx < maskTokens.length; mtndx++ ) { const match = resolveTestFromToken(maskTokens[mtndx], ndxInitializer, [ mtndx ]); if ((match && testPos === pos) || testPos > pos) { break; } } } if (matches.length === 0 || insertStop) { matches.push({ match: { fn: null, static: true, optionality: false, casing: null, def: "", placeholder: "" }, // mark when there are unmatched alternations ex: mask: "(a|aa)" // this will result in the least distance to select the correct test result in determineTestTemplate locator: unMatchedAlternation && matches.filter((tst) => tst.unMatchedAlternationStopped !== true) .length === 0 ? [0] : [], mloc: {}, cd: cacheDependency }); } let result; if (ndxIntlzr !== undefined && maskset.tests[pos]) { // prioritize full tests for caching result = $.extend(true, [], matches); } else { // console.log("stored " + pos + " - " + JSON.stringify(matches)); maskset.tests[pos] = $.extend(true, [], matches); // set a clone to prevent overwriting some props result = maskset.tests[pos]; } // console.log(pos + " - " + JSON.stringify(matches)); // cleanup optionality marking matches.forEach((t) => { t.match.optionality = t.match.defOptionality || false; }); return result; }