Skip to content

Commit

Permalink
work for #4842
Browse files Browse the repository at this point in the history
  • Loading branch information
OlgaLarina committed Dec 26, 2023
1 parent 1d6b06c commit 0b0e33f
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 164 deletions.
71 changes: 71 additions & 0 deletions src/mask/lexical_analyzer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { settings } from "./mask_utils";

export type LexemTokenType = "literal" | "expression" | "or";
export interface ILexemToken {
data: any;
type: LexemTokenType;
isConst?: boolean;
quantifier?: "+" | "*";
}

export class LexicalAnalyzer {
getLexems(str: string): Array<ILexemToken> {
const result: Array<ILexemToken> = [];

let prevChartIsEscaped = false;
let prevTocken: ILexemToken;
let currentTocken: ILexemToken;
let bracketCounter = 0;
let subString = "";

for(let index = 0; index < str.length; index++) {
const currentChar = str[index];
currentTocken = null;

if(bracketCounter !== 0) {
if(currentChar === ")") {
bracketCounter--;
if(bracketCounter === 0) {
currentTocken = <ILexemToken>{ data: subString, type: "expression" };
subString = "";
} else {
subString += currentChar;
}
} else {
subString += currentChar;
}
} else {
if(prevChartIsEscaped) {
prevChartIsEscaped = false;
currentTocken = <ILexemToken>{ data: currentChar, isConst: true };
} else {
switch (currentChar) {
case settings.escapedChar:
prevChartIsEscaped = true;
break;
case "|":
currentTocken = <ILexemToken>{ data: currentChar, type: "or" };
break;
case "(":
bracketCounter++;
break;
case "*":
prevTocken.quantifier = "*";
break;
case "+":
prevTocken.quantifier = "+";
break;
default:
currentTocken = <ILexemToken>{ data: currentChar, type: "literal" };
}
}
}

if(!!currentTocken) {
result.push(currentTocken);
}
prevTocken = currentTocken;
}
return result;
}
}
33 changes: 29 additions & 4 deletions src/mask/mask_pattern.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import { InputMaskBase } from "./mask";
import { IMaskedValue, settings, syntacticAnalysisMask } from "./mask_utils";
import { IMaskedValue, settings } from "./mask_utils";

export interface IMaskLiteral {
type: "const" | "regex";
value: any;
}

export function getLiterals(mask: string): Array<IMaskLiteral> {
const result: Array<IMaskLiteral> = [];
let prevChartIsEscaped = false;
const definitionsKeys = Object.keys(settings.definitions);

for(let index = 0; index < mask.length; index++) {
const currentChar = mask[index];
if(currentChar === settings.escapedChar) {
prevChartIsEscaped = true;
} else if(prevChartIsEscaped) {
prevChartIsEscaped = false;
result.push({ type: "const", value: currentChar });
} else {
result.push({ type: definitionsKeys.indexOf(currentChar) !== -1 ? "regex" : "const", value: currentChar });
}
}

return result;
}

export function getMaskedValueByPattern(str: string, pattern: string, matchWholeMask = true): string {
let result = "";
let strIndex = 0;

const parsedMask = syntacticAnalysisMask(pattern);
const parsedMask = getLiterals(pattern);
for(let maskIndex = 0; maskIndex < parsedMask.length; maskIndex++) {
if(parsedMask[maskIndex].type === "regex") {
const currentDefinition = settings.definitions[parsedMask[maskIndex].value];
Expand All @@ -31,7 +56,7 @@ export function getUnmaskedValueByPattern(str: string, pattern: string, matchWho
let result = "";
if(!str) return result;

const parsedMask = syntacticAnalysisMask(pattern);
const parsedMask = getLiterals(pattern);
for(let index = 0; index < parsedMask.length; index++) {
if(parsedMask[index].type === "regex") {
const currentDefinition = settings.definitions[parsedMask[index].value];
Expand Down Expand Up @@ -76,7 +101,7 @@ export class InputMaskPattern extends InputMaskBase {
return processValueWithPattern(this.input.value, mask, this._prevSelectionStart, this.input.selectionStart);
}

protected updateMaskedString(mask: string, option?: any): void {
public updateMaskedString(mask: string, option?: any): void {
if(!!this.input) {
const result = this.processMaskedValue(mask);
this.input.value = result.text;
Expand Down
75 changes: 2 additions & 73 deletions src/mask/mask_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,77 +16,6 @@ export var settings = {
definitions: <{ [key: string]: RegExp }>{
"9": /[0-9]/,
"a": /[a-zA-Z]/,
"*": /[a-zA-Z0-9]/
"#": /[a-zA-Z0-9]/
}
};

interface IMaskLiteral {
type: "const" | "regex";
repeat?: boolean;
value: any;
}

export function syntacticAnalysisMask(mask: string): Array<IMaskLiteral> {
const result: Array<IMaskLiteral> = [];
let prevChartIsEscaped = false;
let prevChart;
const definitionsKeys = Object.keys(settings.definitions);

for(let index = 0; index < mask.length; index++) {
const currentChar = mask[index];
if(currentChar === settings.escapedChar) {
prevChartIsEscaped = true;
} else if(prevChartIsEscaped) {
prevChartIsEscaped = false;
result.push({ type: "const", value: currentChar });
} else if(currentChar === "+") {
result[result.length - 1].repeat = true;
} else {
result.push({ type: definitionsKeys.indexOf(currentChar) !== -1 ? "regex" : "const", value: currentChar });
}
prevChart = currentChar;
}

return result;
}

// export function getMaskedValueByPatternOld(str: string, pattern: string, matchWholeMask = true): string {
// let result = "";
// let strIndex = 0;
// for(let maskIndex = 0; maskIndex < pattern.length; maskIndex++) {
// const currentDefinition = settings.definitions[pattern[maskIndex]];
// if(currentDefinition) {
// if(strIndex < str.length && str[strIndex].match(currentDefinition)) {
// result += str[strIndex];
// } else if(matchWholeMask) {
// result += settings.placeholderChar;
// } else {
// break;
// }
// strIndex++;
// } else {
// result += pattern[maskIndex];
// }
// }
// return result;
// }

// export function getUnmaskedValueByPatternOld(str: string, pattern: string, matchWholeMask: boolean): string {
// let result = "";
// if(!str) return result;

// for(let index = 0; index < pattern.length; index++) {
// const currentDefinition = settings.definitions[pattern[index]];
// if(currentDefinition) {
// if(!!str[index] && str[index].match(currentDefinition)) {
// result += str[index];
// } else if(matchWholeMask) {
// result = "";
// break;
// } else {
// break;
// }
// }
// }
// return result;
// }
};
93 changes: 75 additions & 18 deletions src/mask/number_mask.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InputMaskBase } from "./mask";
import { IMaskedValue, settings, syntacticAnalysisMask } from "./mask_utils";
import { IMaskLiteral } from "./mask_pattern";
import { IMaskedValue, settings } from "./mask_utils";

interface INumberMaskOption {
mask?: string;
Expand All @@ -14,41 +15,97 @@ interface INumericalСomposition {
fractionalPart?: number;
}

export function parseNumber(str: any): INumericalСomposition {
interface INumberMaskLiteral extends IMaskLiteral {
repeat?: boolean;
}

export function getNumberMaskLiterals(mask: string): Array<INumberMaskLiteral> {
const result: Array<INumberMaskLiteral> = [];
let prevChartIsEscaped = false;
let prevChart;
const definitionsKeys = Object.keys(settings.definitions);

for(let index = 0; index < mask.length; index++) {
const currentChar = mask[index];
if(currentChar === settings.escapedChar) {
prevChartIsEscaped = true;
} else if(prevChartIsEscaped) {
prevChartIsEscaped = false;
result.push({ type: "const", value: currentChar });
} else if(currentChar === "+") {
result[result.length - 1].repeat = true;
} else {
result.push({ type: definitionsKeys.indexOf(currentChar) !== -1 ? "regex" : "const", value: currentChar });
}
prevChart = currentChar;
}

return result;
}

export function parseNumber(str: any, decimalSeparator = "."): INumericalСomposition {
const result: INumericalСomposition = { integralPart: 0, fractionalPart: 0 };
const input = str.toString();

const parts = input.trim().split(".");
const parts = input.trim().split(decimalSeparator);
if(parts.length >= 2) {
result.integralPart = parseInt(parts[0].trim());
result.fractionalPart = parseInt(parts[1].trim());
result.integralPart = parseInt(parts[0].trim() || 0);
result.fractionalPart = parseInt(parts[1].trim() || 0);
} else if(parts.length == 1) {
result.integralPart = parseInt(parts[0].trim());
result.integralPart = parseInt(parts[0].trim() || 0);
} else {
result.integralPart = parseInt(input.trim());
result.integralPart = parseInt(input.trim() || 0);
}

return result;
}

export function getNumberMaskedValue(str: string | number, mask: string, option?: INumberMaskOption) {
const decimalSeparator = option?.decimal || settings.numberOptions.decimal;
const parsedMask = syntacticAnalysisMask(mask);
export function splitString(str: string, reverse = false, n = 3): Array<string> {
let arr = [];

const input = str.toString();
const parsedNumber = parseNumber(input);
if(reverse) {
for (let i = str.length - n; i > -n; i -= n) {
arr.push(str.substring(i, i + n));
}
arr = arr.reverse();
} else {
for (let i = 0; i < str.length; i += n) {
arr.push(str.substring(i, i + n));
}
}

let result = "";
let maskIndex = 0;
return arr;
}

for(let index = 0; index < parsedNumber.integralPart.toString().length; index++) {
export function getNumberMaskedValue(str: string | number, mask: string, option?: INumberMaskOption): string {
const decimalSeparator = option?.decimal || settings.numberOptions.decimal;
const thousandsSeparator = option?.thousands || settings.numberOptions.thousands;
const precision = option?.precision || settings.numberOptions.precision;
const parsedNumber = parseNumber(str);

const integralPart = parsedNumber.integralPart ? splitString(parsedNumber.integralPart.toString(), true).join(thousandsSeparator) : "0";
let fractionalPart = parsedNumber.fractionalPart ? splitString(parsedNumber.fractionalPart.toString()).join(thousandsSeparator) : "";
if(fractionalPart === "") {
return integralPart;
} else {
fractionalPart = fractionalPart.substring(0, precision);
return [integralPart, fractionalPart].join(decimalSeparator);
}
return result;
}

export function getNumberUnmaskedValue(str: string, mask: string, option?: INumberMaskOption) {
return str;
export function getNumberUnmaskedValue(str: string, mask: string, option?: INumberMaskOption): number {
const decimalSeparator = option?.decimal || settings.numberOptions.decimal;
const thousandsSeparator = option?.thousands || settings.numberOptions.thousands;
const precision = option?.precision || settings.numberOptions.precision;

const number = parseNumber(str.replace(thousandsSeparator, ""), decimalSeparator);
const integralPart = number.integralPart ? number.integralPart : 0;
let fractionalPart = number.fractionalPart ? number.fractionalPart : undefined;
if(fractionalPart) {
fractionalPart = parseInt(fractionalPart.toString().substring(0, precision));
}

return parseFloat([integralPart, fractionalPart].join(decimalSeparator));
}

export class InputMaskNumber extends InputMaskBase {
Expand Down
Loading

0 comments on commit 0b0e33f

Please sign in to comment.