form: module-ize password validator

This commit is contained in:
Harvey Tindall 2021-06-05 22:17:10 +01:00
parent 885d2ebf0f
commit 0014db44f0
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
2 changed files with 156 additions and 144 deletions

View File

@ -2,9 +2,9 @@ import { Modal } from "./modules/modal.js";
import { notificationBox, whichAnimationEvent } from "./modules/common.js";
import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js";
import { loadLangSelector } from "./modules/lang.js";
import { initValidator } from "./modules/validator.js";
interface formWindow extends Window {
validationStrings: pwValStrings;
invalidPassword: string;
successModal: Modal;
telegramModal: Modal;
@ -31,20 +31,6 @@ interface formWindow extends Window {
userExpiryMessage: string;
}
interface pwValString {
singular: string;
plural: string;
}
interface pwValStrings {
length: pwValString;
uppercase: pwValString;
lowercase: pwValString;
number: pwValString;
special: pwValString;
[ type: string ]: pwValString;
}
loadLangSelector("form");
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement);
@ -235,29 +221,6 @@ if (window.userExpiryEnabled) {
calculateTime();
}
var defaultPwValStrings: pwValStrings = {
length: {
singular: "Must have at least {n} character",
plural: "Must have at least {n} characters"
},
uppercase: {
singular: "Must have at least {n} uppercase character",
plural: "Must have at least {n} uppercase characters"
},
lowercase: {
singular: "Must have at least {n} lowercase character",
plural: "Must have at least {n} lowercase characters"
},
number: {
singular: "Must have at least {n} number",
plural: "Must have at least {n} numbers"
},
special: {
singular: "Must have at least {n} special character",
plural: "Must have at least {n} special characters"
}
}
const form = document.getElementById("form-create") as HTMLFormElement;
const submitButton = form.querySelector("input[type=submit]") as HTMLInputElement;
const submitSpan = form.querySelector("span.submit") as HTMLSpanElement;
@ -267,19 +230,7 @@ if (!window.usernameEnabled) { usernameField.parentElement.remove(); usernameFie
const passwordField = document.getElementById("create-password") as HTMLInputElement;
const rePasswordField = document.getElementById("create-reenter-password") as HTMLInputElement;
const checkPasswords = () => {
if (passwordField.value != rePasswordField.value) {
rePasswordField.setCustomValidity(window.invalidPassword);
submitButton.disabled = true;
submitSpan.setAttribute("disabled", "");
} else {
rePasswordField.setCustomValidity("");
submitButton.disabled = false;
submitSpan.removeAttribute("disabled");
}
};
rePasswordField.addEventListener("keyup", checkPasswords);
passwordField.addEventListener("keyup", checkPasswords);
var requirements = initValidator(passwordField, rePasswordField, submitButton, submitSpan)
interface respDTO {
response: boolean;
@ -371,96 +322,3 @@ const create = (event: SubmitEvent) => {
};
form.onsubmit = create;
class Requirement {
private _name: string;
protected _minCount: number;
private _content: HTMLSpanElement;
private _valid: HTMLSpanElement;
private _li: HTMLLIElement;
get valid(): boolean { return this._valid.classList.contains("~positive"); }
set valid(state: boolean) {
if (state) {
this._valid.classList.add("~positive");
this._valid.classList.remove("~critical");
this._valid.innerHTML = `<i class="icon ri-check-line" title="valid"></i>`;
} else {
this._valid.classList.add("~critical");
this._valid.classList.remove("~positive");
this._valid.innerHTML = `<i class="icon ri-close-line" title="invalid"></i>`;
}
}
constructor(name: string, el: HTMLLIElement) {
this._name = name;
this._li = el;
this._content = this._li.querySelector("span.requirement-content") as HTMLSpanElement;
this._valid = this._li.querySelector("span.requirement-valid") as HTMLSpanElement;
this.valid = false;
this._minCount = +this._li.getAttribute("min");
let text = "";
if (this._minCount == 1) {
text = window.validationStrings[this._name].singular.replace("{n}", "1");
} else {
text = window.validationStrings[this._name].plural.replace("{n}", ""+this._minCount);
}
this._content.textContent = text;
}
validate = (count: number) => { this.valid = (count >= this._minCount); }
}
// Incredible code right here
const isInt = (s: string): boolean => { return (s in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); }
const testStrings = (f: pwValString): boolean => {
const testString = (s: string): boolean => {
if (s == "" || !s.includes("{n}")) { return false; }
return true;
}
return testString(f.singular) && testString(f.plural);
}
interface Validation { [name: string]: number }
const validate = (s: string): Validation => {
let v: Validation = {};
for (let criteria of ["length", "lowercase", "uppercase", "number", "special"]) { v[criteria] = 0; }
v["length"] = s.length;
for (let c of s) {
if (isInt(c)) { v["number"]++; }
else {
const upper = c.toUpperCase();
if (upper == c.toLowerCase()) { v["special"]++; }
else {
if (upper == c) { v["uppercase"]++; }
else if (upper != c) { v["lowercase"]++; }
}
}
}
return v
}
passwordField.addEventListener("keyup", () => {
const v = validate(passwordField.value);
for (let criteria in requirements) {
requirements[criteria].validate(v[criteria]);
}
});
var requirements: { [category: string]: Requirement} = {};
if (!window.validationStrings) {
window.validationStrings = defaultPwValStrings;
} else {
for (let category in window.validationStrings) {
if (!testStrings(window.validationStrings[category])) {
window.validationStrings[category] = defaultPwValStrings[category];
}
const el = document.getElementById("requirement-" + category);
if (el) {
requirements[category] = new Requirement(category, el as HTMLLIElement);
}
}
}

154
ts/modules/validator.ts Normal file
View File

@ -0,0 +1,154 @@
interface valWindow extends Window {
validationStrings: pwValStrings;
invalidPassword: string;
}
interface pwValString {
singular: string;
plural: string;
}
interface pwValStrings {
length: pwValString;
uppercase: pwValString;
lowercase: pwValString;
number: pwValString;
special: pwValString;
[ type: string ]: pwValString;
}
declare var window: valWindow;
class Requirement {
private _name: string;
protected _minCount: number;
private _content: HTMLSpanElement;
private _valid: HTMLSpanElement;
private _li: HTMLLIElement;
get valid(): boolean { return this._valid.classList.contains("~positive"); }
set valid(state: boolean) {
if (state) {
this._valid.classList.add("~positive");
this._valid.classList.remove("~critical");
this._valid.innerHTML = `<i class="icon ri-check-line" title="valid"></i>`;
} else {
this._valid.classList.add("~critical");
this._valid.classList.remove("~positive");
this._valid.innerHTML = `<i class="icon ri-close-line" title="invalid"></i>`;
}
}
constructor(name: string, el: HTMLLIElement) {
this._name = name;
this._li = el;
this._content = this._li.querySelector("span.requirement-content") as HTMLSpanElement;
this._valid = this._li.querySelector("span.requirement-valid") as HTMLSpanElement;
this.valid = false;
this._minCount = +this._li.getAttribute("min");
let text = "";
if (this._minCount == 1) {
text = window.validationStrings[this._name].singular.replace("{n}", "1");
} else {
text = window.validationStrings[this._name].plural.replace("{n}", ""+this._minCount);
}
this._content.textContent = text;
}
validate = (count: number) => { this.valid = (count >= this._minCount); }
}
export function initValidator(passwordField: HTMLInputElement, rePasswordField: HTMLInputElement, submitButton: HTMLInputElement, submitSpan: HTMLSpanElement): { [category: string]: Requirement } {
var defaultPwValStrings: pwValStrings = {
length: {
singular: "Must have at least {n} character",
plural: "Must have at least {n} characters"
},
uppercase: {
singular: "Must have at least {n} uppercase character",
plural: "Must have at least {n} uppercase characters"
},
lowercase: {
singular: "Must have at least {n} lowercase character",
plural: "Must have at least {n} lowercase characters"
},
number: {
singular: "Must have at least {n} number",
plural: "Must have at least {n} numbers"
},
special: {
singular: "Must have at least {n} special character",
plural: "Must have at least {n} special characters"
}
}
const checkPasswords = () => {
if (passwordField.value != rePasswordField.value) {
rePasswordField.setCustomValidity(window.invalidPassword);
submitButton.disabled = true;
submitSpan.setAttribute("disabled", "");
} else {
rePasswordField.setCustomValidity("");
submitButton.disabled = false;
submitSpan.removeAttribute("disabled");
}
};
rePasswordField.addEventListener("keyup", checkPasswords);
passwordField.addEventListener("keyup", checkPasswords);
// Incredible code right here
const isInt = (s: string): boolean => { return (s in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); }
const testStrings = (f: pwValString): boolean => {
const testString = (s: string): boolean => {
if (s == "" || !s.includes("{n}")) { return false; }
return true;
}
return testString(f.singular) && testString(f.plural);
}
interface Validation { [name: string]: number }
const validate = (s: string): Validation => {
let v: Validation = {};
for (let criteria of ["length", "lowercase", "uppercase", "number", "special"]) { v[criteria] = 0; }
v["length"] = s.length;
for (let c of s) {
if (isInt(c)) { v["number"]++; }
else {
const upper = c.toUpperCase();
if (upper == c.toLowerCase()) { v["special"]++; }
else {
if (upper == c) { v["uppercase"]++; }
else if (upper != c) { v["lowercase"]++; }
}
}
}
return v
}
passwordField.addEventListener("keyup", () => {
const v = validate(passwordField.value);
for (let criteria in requirements) {
requirements[criteria].validate(v[criteria]);
}
});
var requirements: { [category: string]: Requirement } = {};
if (!window.validationStrings) {
window.validationStrings = defaultPwValStrings;
} else {
for (let category in window.validationStrings) {
if (!testStrings(window.validationStrings[category])) {
window.validationStrings[category] = defaultPwValStrings[category];
}
const el = document.getElementById("requirement-" + category);
if (el) {
requirements[category] = new Requirement(category, el as HTMLLIElement);
}
}
}
return requirements
}