From 0a426519f8dceb6b432147e1141b8981de4f6c9d Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 4 Jan 2021 20:51:16 +0000 Subject: [PATCH] implement account creation --- html/admin.html | 2 +- html/form-base.html | 1 - html/form.html | 25 ++++--- ts/form.ts | 178 ++++++++++++++++++++++++++++++++++++++++++++ ts/typings/d.ts | 1 - 5 files changed, 194 insertions(+), 13 deletions(-) create mode 100644 ts/form.ts diff --git a/html/admin.html b/html/admin.html index bd34a09..ac082db 100644 --- a/html/admin.html +++ b/html/admin.html @@ -178,7 +178,7 @@
-
+
diff --git a/html/form-base.html b/html/form-base.html index d95a708..432d6a4 100644 --- a/html/form-base.html +++ b/html/form-base.html @@ -5,6 +5,5 @@ window.invalidPassword = "{{ .lang.reEnterPasswordInvalid }}"; window.URLBase = "{{ .urlBase }}"; - {{ end }} diff --git a/html/form.html b/html/form.html index eb2271d..88c5685 100644 --- a/html/form.html +++ b/html/form.html @@ -13,6 +13,7 @@ {{ .lang.successContinueButton }}
+
@@ -21,17 +22,24 @@
-
- - + + - + - + + +
@@ -39,11 +47,8 @@ {{ .lang.passwordRequirementsHeader }}
    {{ range $key, $value := .requirements }} -
  • - -
  • -
  • - +
  • +
  • {{ end }}
diff --git a/ts/form.ts b/ts/form.ts new file mode 100644 index 0000000..0cfb596 --- /dev/null +++ b/ts/form.ts @@ -0,0 +1,178 @@ +import { Modal } from "./modules/modal.js"; +import { _post, toggleLoader } from "./modules/common.js"; + +interface formWindow extends Window { + validationStrings: pwValStrings; + invalidPassword: string; + modal: Modal; +} + +interface pwValString { + singular: string; + plural: string; +} + +interface pwValStrings { + length: pwValString; + uppercase: pwValString; + lowercase: pwValString; + number: pwValString; + special: pwValString; + [ type: string ]: pwValString; +} + +window.modal = new Modal(document.getElementById("modal-success")); +declare var window: formWindow; + +var defaultPwValStrings: pwValStrings = { + length: { + singular: "Must have at least {n} character", + plural: "Must have a 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; +let usernameField = document.getElementById("create-username") as HTMLInputElement; +const emailField = document.getElementById("create-email") as HTMLInputElement; +if (!window.usernameEnabled) { usernameField.parentElement.remove(); usernameField = emailField; } +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); + +interface respDTO { + [ type: string ]: boolean; +} + +interface sendDTO { + code: string; + email: string; + username: string; + password: string; +} + +const create = (event: SubmitEvent) => { + event.preventDefault(); + toggleLoader(submitSpan); + let send: sendDTO = { + code: window.location.href.split('/').pop(), + username: usernameField.value, + email: emailField.value, + password: passwordField.value + }; + _post("/newUser", send, (req: XMLHttpRequest) => { + if (req.readyState == 4) { + let vals = JSON.parse(req.response) as respDTO; + let valid = true; + for (let type in vals) { + if (requirements[type]) { requirements[type].valid = vals[type]; } + if (!vals[type]) { valid = false; } + } + toggleLoader(submitSpan); + if (req.status == 200 && valid) { + window.modal.show(); + } else { + submitSpan.classList.add("~critical"); + submitSpan.classList.remove("~urge"); + setTimeout(() => { + submitSpan.classList.add("~urge"); + submitSpan.classList.remove("~critical"); + }, 1000); + } + } + }); +}; + +form.onsubmit = create; + +class Requirement { + private _name: string; + private _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 = ``; + } else { + this._valid.classList.add("~critical"); + this._valid.classList.remove("~positive"); + this._valid.innerHTML = ``; + } + } + + 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; + } +} + +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); +} + +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); + } else { console.log(category); } + } +} diff --git a/ts/typings/d.ts b/ts/typings/d.ts index f09054c..013ea9b 100644 --- a/ts/typings/d.ts +++ b/ts/typings/d.ts @@ -59,7 +59,6 @@ declare interface Modals { settingsRestart: Modal; settingsRefresh: Modal; ombiDefaults?: Modal; - newAccountSuccess?: Modal; profiles: Modal; addProfile: Modal; }