Skip to content

Commit

Permalink
work for #4842
Browse files Browse the repository at this point in the history
  • Loading branch information
OlgaLarina committed Feb 26, 2024
1 parent 7550457 commit 040f885
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/mask/input_element_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ITextMaskInputArgs } from "./mask_utils";

export class InputElementAdapter {
constructor(private inputMaskInstance: InputMaskBase, private inputElement: HTMLInputElement, value: string = "") {
this.inputElement.value = inputMaskInstance.getMaskedValue(value);
this.inputElement.value = inputMaskInstance.formatString(value);
this.addInputEventListener();
}

Expand Down
1 change: 1 addition & 0 deletions src/mask/mask_base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class InputMaskBase extends Base implements IInputMaskType {

public getUnmaskedValue(src: string): string { return src; }
public getMaskedValue(src: string): string { return src; }
public formatString(src: string): string { return this.getMaskedValue(src); }
}

Serializer.addClass(
Expand Down
114 changes: 73 additions & 41 deletions src/mask/mask_datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,31 @@ interface IDateTimeComposition {
max?: Date;
}

function getMaxCountLexem(currentLexemType: string, count: number): number {
switch(currentLexemType) {
case("day"):
case("month"): {
return 2;
}
case("year"): {
return count;
}
default: {
return 1;
}
}
}
export function getDateTimeLexems(mask: string): Array<IDateTimeMaskLexem> {
const result: Array<IDateTimeMaskLexem> = [];
let prevLexemType: string;

const createOrUpdateLexem = (currentLexemType: "month" | "day" | "year" | "separator", currentChar: string) => {
if(!!prevLexemType && prevLexemType === currentLexemType) {
result[result.length - 1].count++;
const maxCount = getMaxCountLexem(currentLexemType, result[result.length - 1].count);
result[result.length - 1].maxCount = maxCount;
} else {
const maxCount = (currentLexemType === "month" || currentLexemType === "day") ? 2 : (currentLexemType === "year") ? 4 : 1;
const maxCount = getMaxCountLexem(currentLexemType, 1);
result.push({ type: currentLexemType, value: currentChar, count: 1, data: "", maxCount: maxCount });
}
};
Expand Down Expand Up @@ -58,14 +74,13 @@ export function getDateTimeLexems(mask: string): Array<IDateTimeMaskLexem> {
}

export class InputMaskDateTime extends InputMaskPattern {
private currentValue: Array<{ lexem: IDateTimeMaskLexem, data?: string }> = [];
private lexems: Array<IDateTimeMaskLexem> = [];

protected updateLiterals(): void {
this.lexems = getDateTimeLexems(this.mask || "");
}

private getOnlyNumbers(input: string): string {
private leaveOnlyNumbers(input: string): string {
let result = "";
for(let index = 0; index < input.length; index++) {
if(input[index].match(numberDefinition)) {
Expand All @@ -91,7 +106,9 @@ export class InputMaskDateTime extends InputMaskPattern {
break;
}
case("year"): {
lexem.data = date.getFullYear().toString();
let year = date.getFullYear();
if(lexem.count == 2) year = year % 100;
lexem.data = year.toString();
break;
}
default: {
Expand All @@ -105,12 +122,20 @@ export class InputMaskDateTime extends InputMaskPattern {
private getISO_8601Format(dateTime: IDateTimeComposition): string {
if(dateTime.year === undefined || dateTime.month === undefined || dateTime.day === undefined) return "";

const year = this.getPlaceholder(4, dateTime.year.toString(), "0", true) + dateTime.year;
const month = this.getPlaceholder(2, dateTime.month.toString(), "0", true) + dateTime.month;
const day = this.getPlaceholder(2, dateTime.day.toString(), "0", true) + dateTime.day;
const year = this.getPlaceholder(4, dateTime.year.toString(), "0") + dateTime.year;
const month = this.getPlaceholder(2, dateTime.month.toString(), "0") + dateTime.month;
const day = this.getPlaceholder(2, dateTime.day.toString(), "0") + dateTime.day;
return [year, month, day].join("-");
}

private getCorrectData(lexem: IDateTimeMaskLexem): string {
if(lexem.type !== "year" || lexem.count > 2) return lexem.data;

const year = parseInt(lexem.data);
const result = (year > 68 ? "19": "20") + lexem.data;
return result;
}

private isDateValid(dateTime: IDateTimeComposition): boolean {
const year = dateTime.year !== undefined ? dateTime.year : 2000;
const month = dateTime.month !== undefined ? dateTime.month : 1;
Expand All @@ -127,9 +152,7 @@ export class InputMaskDateTime extends InputMaskPattern {
date >= min && date <= max;
}

private getPlaceholder(lexemLength: number, str: string, char: string, matchWholeMask: boolean) {
if(!matchWholeMask) return "";

private getPlaceholder(lexemLength: number, str: string, char: string) {
const paddingsLength = lexemLength - (str || "").length;
const paddings = paddingsLength > 0 ? char.repeat(paddingsLength) : "";
return paddings;
Expand Down Expand Up @@ -165,13 +188,15 @@ export class InputMaskDateTime extends InputMaskPattern {
private getCorrectDataFormat(lexem: IDateTimeMaskLexem, isCompleted: boolean, matchWholeMask: boolean): string {
let data = lexem.data || "";
if(isCompleted) {
const zeroPaddings = this.getPlaceholder(lexem.count, data, "0", true);
const zeroPaddings = this.getPlaceholder(lexem.count, data, "0");
data = zeroPaddings + data;
} else {
if(((lexem.type === "day" && parseInt(data[0]) === 0) || (lexem.type === "month" && parseInt(data[0]) === 0)) && lexem.count < lexem.maxCount) {
data = data.slice(1, data.length);
}
data += this.getPlaceholder(lexem.count, data, lexem.value, matchWholeMask);
if(matchWholeMask) {
data += this.getPlaceholder(lexem.count, data, lexem.value);
}
}

if(!data && matchWholeMask) {
Expand All @@ -183,22 +208,29 @@ export class InputMaskDateTime extends InputMaskPattern {
private getFormatedString(matchWholeMask: boolean): string {
const tempDateTime: IDateTimeComposition = { day: undefined, month: undefined, year: undefined };
let result = "";
let prevSeparator = "";
let prevIsCompleted = false;

for(let index = 0; index < this.lexems.length; index++) {
const lexem = this.lexems[index];
if(lexem.type === "day" || lexem.type === "month" || lexem.type === "year") {
const isCompleted = this.isEntryComplete(lexem, tempDateTime);
let data = this.getCorrectDataFormat(lexem, isCompleted, matchWholeMask);
if(!!data) {
result += data;
// if(data.length < lexem.count) {
if(!isCompleted && !matchWholeMask) {
break;
switch(lexem.type) {
case "day":
case "month":
case "year":
if(lexem.data === undefined && !matchWholeMask) {
result += (prevIsCompleted ? prevSeparator : "");
return result;
} else {
const isCompleted = this.isEntryComplete(lexem, tempDateTime);
const data = this.getCorrectDataFormat(lexem, isCompleted, matchWholeMask);
result += (prevSeparator + data);
prevIsCompleted = isCompleted;
}
} else {
break;
}
} else if (lexem.type === "separator") {
result += lexem.value;

case "separator":
prevSeparator = lexem.value;
break;
}
}

Expand All @@ -210,7 +242,7 @@ export class InputMaskDateTime extends InputMaskPattern {

if (numberParts.length > 0) {
numberParts.forEach((part, index) => {
const _data = this.getOnlyNumbers(part);
const _data = this.leaveOnlyNumbers(part);
numberLexems[index].data = _data.slice(0, numberLexems[index].maxCount);
});
}
Expand Down Expand Up @@ -258,30 +290,26 @@ export class InputMaskDateTime extends InputMaskPattern {
const inputParts: Array<string> = [];
const nonSeparatorLexems = this.lexems.filter(l => l.type !== "separator");
let curPart = "";
let foundSeparator = false;
for(let i = 0; i < input.length; i++) {
// if(input[i].match(numberDefinition) && curPart.length < nonSeparatorLexems[inputParts.length].maxCount) {
// curPart += input[i];
// }
if(!(input[i].match(numberDefinition) || input[i] == nonSeparatorLexems[inputParts.length].value)) {
if(!input[i].match(numberDefinition) && input[i] !== nonSeparatorLexems[inputParts.length].value) {
foundSeparator = true;
if(curPart != "") {
inputParts.push(curPart);
curPart = "";
}
} else {
foundSeparator = false;
curPart += input[i];
// if(curPart.length == nonSeparatorLexems[inputParts.length].maxCount) {
// if(curPart != "") {
// inputParts.push(curPart);
// curPart = "";
// }
// }
}
if(inputParts.length >= nonSeparatorLexems.length) break;
if(inputParts.length >= nonSeparatorLexems.length) {
foundSeparator = false;
break;
}
}

if(curPart != "") {
if(curPart != "" || foundSeparator) {
inputParts.push(curPart);
curPart = "";
}
return inputParts;
}
Expand All @@ -295,17 +323,21 @@ export class InputMaskDateTime extends InputMaskPattern {
this.lexems.forEach(lexem => {
let str = lexem.data;
if(!str || str.length < lexem.count) return undefined;
tempDateTime[lexem.type] = parseInt(str);
tempDateTime[lexem.type] = parseInt(this.getCorrectData(lexem));
});

return this.getISO_8601Format(tempDateTime);
}
public getMaskedValue(src: string): string {
const input = (src === undefined || src === null) ? "" : src;
return this._getMaskedValue(input);
}
public formatString(src: string) : string {
if(this.dataToSave === "unmasked") {
return this.getMaskedStrFromISO(input);
return this.getMaskedStrFromISO(src);
} else {
return this.getMaskedValue(src);
}
return this._getMaskedValue(input);
}

public processInput(args: ITextMaskInputArgs): IMaskedValue {
Expand Down
3 changes: 1 addition & 2 deletions src/mask/mask_pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export function getMaskedValueByPattern(src: string, pattern: string | Array<IMa
let strIndex = 0;
const literals: Array<IMaskLiteral> = (typeof pattern === "string") ? getLiterals(pattern) : pattern;

maskEnumerator:
for(let maskIndex = 0; maskIndex < literals.length; maskIndex++) {
switch(literals[maskIndex].type) {
case "regex" :
Expand All @@ -56,7 +55,7 @@ export function getMaskedValueByPattern(src: string, pattern: string | Array<IMa
} else if(matchWholeMask) {
result += settings.placeholderChar;
} else {
break maskEnumerator;
return result;
}

strIndex++;
Expand Down
1 change: 1 addition & 0 deletions src/mask/mask_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ export interface IInputMaskType {
getMaskedValue(src: string): string;
getUnmaskedValue(src: string): any;
processInput(args: ITextMaskInputArgs): IMaskedValue;
formatString(src: string): string;
isEmpty(): boolean;
}
2 changes: 1 addition & 1 deletion src/question_text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export class QuestionTextModel extends QuestionTextBase {
return this.maskSettings;
}
public get inputValue(): string {
return !!this.maskInstance ? this.maskInstance.getMaskedValue(this.value) : this.value;
return !!this.maskInstance ? this.maskInstance.formatString(this.value) : this.value;
}
public set inputValue(val: string) {
let value;
Expand Down
65 changes: 45 additions & 20 deletions tests/mask/mask_datetime_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,27 @@ QUnit.test("_getMaskedValue matchWholeMask is false m/d/yyyy", function(assert)
assert.equal(maskInstance._getMaskedValue("2", false), "2/");
assert.equal(maskInstance._getMaskedValue("12", false), "12/");
assert.equal(maskInstance._getMaskedValue("5/0", false), "5/");
/// !!!
// assert.equal(maskInstance._getMaskedValue("1/0", false), "1/");
assert.equal(maskInstance._getMaskedValue("1/0", false), "1/");
assert.equal(maskInstance._getMaskedValue("10/0", false), "10/");
assert.equal(maskInstance._getMaskedValue("3/1", false), "3/1");
assert.equal(maskInstance._getMaskedValue("3/17", false), "3/17/");
assert.equal(maskInstance._getMaskedValue("3/4", false), "3/4/");
assert.equal(maskInstance._getMaskedValue("10/4", false), "10/4/");
});

QUnit.test("get masked value from ISO", function(assert) {
QUnit.test("get formatString value from ISO mm/dd/yyyy", function(assert) {
const maskInstance = new InputMaskDateTime();
maskInstance.mask = "mm/dd/yyyy";
assert.equal(maskInstance.getMaskedValue("2024-12-05"), "12/05/2024");
assert.equal(maskInstance.getMaskedValue("2024-13-05"), "mm/dd/yyyy");
assert.equal(maskInstance.formatString("2024-12-05"), "12/05/2024");
assert.equal(maskInstance.formatString("2024-13-05"), "mm/dd/yyyy");
});

QUnit.test("get formatString value from ISO m/d/yy", function(assert) {
const maskInstance = new InputMaskDateTime();
maskInstance.mask = "m/d/yy";
assert.equal(maskInstance.formatString("2024-12-05"), "12/5/24");
assert.equal(maskInstance.formatString("988-01-05"), "1/5/88");
assert.equal(maskInstance.formatString("2024-13-05"), "m/d/yy");
});

QUnit.test("get masked date if text with dots mm/dd/yyyy", function(assert) {
Expand Down Expand Up @@ -236,6 +243,17 @@ QUnit.test("get masked valid date text m/d/yyyy", function(assert) {
assert.equal(maskInstance.getUnmaskedValue("1/3/1987"), "1987-01-03");
});

QUnit.test("_getMaskedValue matchWholeMask m/d/yy", function(assert) {
const maskInstance = new InputMaskDateTime();
maskInstance.mask = "m/d/yy";

assert.equal(maskInstance.getUnmaskedValue("8/5/91"), "1991-08-05");
assert.equal(maskInstance.getUnmaskedValue("1/3/69"), "1969-01-03");
assert.equal(maskInstance.getUnmaskedValue("12/30/68"), "2068-12-30");
assert.equal(maskInstance.getUnmaskedValue("12/30/01"), "2001-12-30");
assert.equal(maskInstance.getUnmaskedValue("12/30/00"), "2000-12-30");
});

QUnit.test("dateTime processInput serial input: insert characters", function(assert) {
const maskInstance = new InputMaskDateTime();
maskInstance.mask = "mm/dd/yyyy";
Expand Down Expand Up @@ -334,31 +352,38 @@ QUnit.test("dateTime processInput: insert characters", function(assert) {

QUnit.test("dateTime processInput: insert characters", function(assert) {
const maskInstance = new InputMaskDateTime();
maskInstance.mask = "m/d/yyyy";
let result = maskInstance.processInput({ insertedCharacters: "4", selectionStart: 0, selectionEnd: 0, prevValue: "m/d/yyyy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/d/yyyy", "type 4");
maskInstance.mask = "m/d/yy";
let result = maskInstance.processInput({ insertedCharacters: "4", selectionStart: 0, selectionEnd: 0, prevValue: "m/d/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/d/yy", "type 4");
assert.equal(result.cursorPosition, 2, "type 4");

result = maskInstance.processInput({ insertedCharacters: "5", selectionStart: 2, selectionEnd: 2, prevValue: "4/d/yyyy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/yyyy", "type 5");
result = maskInstance.processInput({ insertedCharacters: "5", selectionStart: 2, selectionEnd: 2, prevValue: "4/d/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/yy", "type 5");
assert.equal(result.cursorPosition, 4, "type 5");

result = maskInstance.processInput({ insertedCharacters: "1", selectionStart: 4, selectionEnd: 4, prevValue: "4/5/yyyy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/1yyy", "type 1");
result = maskInstance.processInput({ insertedCharacters: "1", selectionStart: 4, selectionEnd: 4, prevValue: "4/5/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/1y", "type 1");
assert.equal(result.cursorPosition, 5, "type 1");

result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 0, selectionEnd: 0, prevValue: "m/d/yyyy", inputDirection: "leftToRight" });
assert.equal(result.text, "m/d/yyyy", "try type 0 into month");
result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 0, selectionEnd: 0, prevValue: "m/d/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "m/d/yy", "try type 0 into month");
assert.equal(result.cursorPosition, 0, "try type 0 into month");

/// !!!
// result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 2, selectionEnd: 2, prevValue: "1/d/yyyy", inputDirection: "leftToRight" });
// assert.equal(result.text, "1/d/yyyy", "try type 0 into day");
// assert.equal(result.cursorPosition, 2, "try type 0 into day");
result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 2, selectionEnd: 2, prevValue: "1/d/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "1/d/yy", "try type 0 into day");
assert.equal(result.cursorPosition, 2, "try type 0 into day");

result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 3, selectionEnd: 3, prevValue: "10/d/yyyy", inputDirection: "leftToRight" });
assert.equal(result.text, "10/d/yyyy", "try type 0 into day");
result = maskInstance.processInput({ insertedCharacters: "0", selectionStart: 3, selectionEnd: 3, prevValue: "10/d/yy", inputDirection: "leftToRight" });
assert.equal(result.text, "10/d/yy", "try type 0 into day");
assert.equal(result.cursorPosition, 3, "try type 0 into day");

result = maskInstance.processInput({ insertedCharacters: "3", selectionStart: 5, selectionEnd: 5, prevValue: "4/5/1y", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/13", "type 3");
assert.equal(result.cursorPosition, 6, "type 3");

result = maskInstance.processInput({ insertedCharacters: "8", selectionStart: 6, selectionEnd: 6, prevValue: "4/5/13", inputDirection: "leftToRight" });
assert.equal(result.text, "4/5/13", "try type 8");
assert.equal(result.cursorPosition, 6, "try type 8");
});

QUnit.test("dateTime processInput: delete characters by backspace", function(assert) {
Expand Down

0 comments on commit 040f885

Please sign in to comment.