Select Git revision
tutorial-dom-form-handling.html
login.mjs 71.89 KiB
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*/
import { instanceSymbol } from "../../constants.mjs";
import { addAttributeToken } from "../../dom/attributes.mjs";
import {
ATTRIBUTE_ERRORMESSAGE,
ATTRIBUTE_ROLE,
} from "../../dom/constants.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
assembleMethodSymbol,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { findTargetElementFromEvent, fireEvent } from "../../dom/events.mjs";
import { isFunction } from "../../types/is.mjs";
import { LoginStyleSheet } from "./stylesheet/login.mjs";
import { fireCustomEvent } from "../../dom/events.mjs";
import "./form.mjs";
import "./field-set.mjs";
import "./password.mjs";
import "./message-state-button.mjs";
import "./digits.mjs";
import "../layout/collapse.mjs";
import "../datatable/datasource/rest.mjs";
import { InvalidStyleSheet } from "./stylesheet/invalid.mjs";
import { getLocaleOfDocument } from "../../dom/locale.mjs";
import { getWindow } from "../../dom/util.mjs";
export { Login };
/**
* @private
* @type {symbol}
*/
const loginElementSymbol = Symbol("loginElement");
/**
* @private
* @type {symbol}
*/
const forgotPasswordLinkSymbol = Symbol("forgotPasswordLink");
/**
* @private
* @type {symbol}
*/
const forgotPasswordCollapseSymbol = Symbol("forgotPasswordCollapse");
/**
* @private
* @type {symbol}
*/
const loginCollapseSymbol = Symbol("loginCollapse");
/**
* @private
* @type {symbol}
*/
const loginLinkSymbol = Symbol("loginLink");
/**
* @private
* @type {symbol}
*/
const secondFactorCollapseSymbol = Symbol("secondFactorCollapse");
/**
* @private
* @type {symbol}
*/
const resetLoginProcessLinksSymbol = Symbol("resetLoginProcessLink");
/**
* @private
* @type {symbol}
*/
const loggedInCollapseSymbol = Symbol("loggedInCollapse");
/**
* @private
* @type {symbol}
*/
const loginButtonSymbol = Symbol("loginButton");
/**
* @private
* @type {symbol}
*/
const requestLinkButtonSymbol = Symbol("requestLinkButton");
/**
* @private
* @type {symbol}
*/
const digitsButtonSymbol = Symbol("digitsButton");
/**
* @private
* @type {symbol}
*/
const secondFactorButtonSymbol = Symbol("secondFactorButton");
/**
* @private
* @type {symbol}
*/
const digitsCollapseSymbol = Symbol("digitsCollapse");
/**
* A Login
*
* @fragments /fragments/components/form/login/
*
* @example /examples/components/form/login-simple
*
* @since 3.113.0
* @copyright schukai GmbH
* @summary A beautiful Login that can make your life easier and also looks good. It supports a lot of features.
*
* @fires login-success
* @fires redirect-to-first-success-url
*/
class Login extends CustomElement {
/**
* This method is called by the `instanceof` operator.
* @returns {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/components/form/login@@instance");
}
/**
*
* @return {Components.Form.Login
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
setTimeout(() => {
initControlReferences.call(this);
initEventHandler.call(this);
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 100);
}, 0);
return this;
}
/**
* To set the options via the HTML Tag, the attribute `data-monster-options` must be used.
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
*
* The individual configuration values can be found in the table.
*
* @property {Object} templates Template definitions
* @property {string} templates.main The main HTML template used for rendering the login form
* @property {Object} labels Label definitions used for localization and form messages
* @property {string} labels.username Label for the username or email field
* @property {string} labels.password Label for the password field
* @property {string} labels.login Label for the login button
* @property {string} labels.forgotPasswordLink Text for the "forgot password" link
* @property {string} labels.mailAddress Label for the email input in password reset flow
* @property {string} labels.requestLink Label for the button that sends a password reset code
* @property {string} labels.digits Label for the digits input field
* @property {string} labels.loginLink Label for the back-to-login link
* @property {string} labels.secondFactor Label for the second factor authentication input
* @property {string} labels.sendDigits Label for the button that submits the digits input
* @property {string} labels.sendSecondFactorDigits Label for the button that submits the second factor code
* @property {string} labels.resetLoginProcess Label for the link to return to the login form
* @property {string} labels.messageEmptyUserName Message shown when username is empty
* @property {string} labels.messageEmptyPassword Message shown when password is empty
* @property {string} labels.messageEmptyBoth Message shown when both username and password are empty
* @property {string} labels.messageEmptyEmail Message shown when email field is empty
* @property {string} labels.messageInvalidEmail Message shown when an invalid email address is entered
* @property {string} labels.digitsEmpty Message shown when digits field is empty
* @property {string} labels.digitsInvalid Message shown when digits input is invalid
* @property {string} labels.messageLoginFailed Message shown on failed login attempt
* @property {string} labels.messageForbidden Message shown on successful login with insufficient permissions
* @property {string} labels.messageSomethingWentWrong Fallback error message
* @property {string} labels.messageThisFormIsNotConfigured Message shown if no backend URL is configured
* @property {string} labels.messagePasswordResetDisabled Message shown if password reset is disabled due to 2FA
* @property {Object} classes Class definitions for visual styling
* @property {string} classes.usernameInvalid CSS class applied when username is invalid
* @property {string} classes.passwordInvalid CSS class applied when password is invalid
* @property {string} classes.emailInvalid CSS class applied when email input is invalid
* @property {string} classes.button CSS class applied to all form buttons
* @property {boolean} disabled If true, disables interaction with the component
* @property {Object} features Feature flags to toggle optional behavior
* @property {boolean} features.forgotPassword Enables the forgot password flow
* @property {boolean} features.redirectToFirstSuccessUrl If true, redirects to the first success URL after login
* @property {Object} actions Action definitions for custom event handling
* @property {Function} actions.click Callback function for generic click actions within the login component
* @property {Object} callbacks Optional callback hooks for modifying internal behavior
* @property {Function} callbacks.username A function that receives and can transform the entered username before submission
* @property {Function} callbacks.forgotPassword A function that receives and can transform the entered email before submission
* @property {number} digits Number of digits required for second factor or password reset code input
* @property {Object[]} successUrls List of URLs shown after successful login (e.g., home or logout)
* @property {string} successUrls.label Label for the success URL (displayed)
* @property {string} successUrls.url Actual target URL
* @property {number} timeoutForMessage Duration in milliseconds to show error messages
* @property {number} timeoutForSuccess Duration in milliseconds before triggering the post-login success state
* @property {Object} accessKeys Keyboard access keys for accessibility and shortcuts
* @property {string} accessKeys.loginLink Access key for switching to login form
* @property {string} accessKeys.username Access key for focusing the username field
* @property {string} accessKeys.password Access key for focusing the password field
* @property {Object} fetch Definitions for backend requests to support login workflows
* @property {Object} placeholder Placeholder text for input fields
* @property {string} placeholder.username Placeholder for the username field
* @property {string} placeholder.password Placeholder for the password field
* @property {string} placeholder.email Placeholder for the email field
* @property {Object} fetch.login Fetch config for login request
* @property {string} fetch.login.url Endpoint to post login credentials to
* @property {string} fetch.login.method HTTP method for login (e.g., "POST")
* @property {string} fetch.login.mode Fetch mode (e.g., "same-origin")
* @property {Object} fetch.login.headers HTTP headers to be sent with the login request
* @property {string} fetch.login.headers.accept Accept header value
* @property {string} fetch.login.headers.Content-Type Content-Type header value
* @property {string} fetch.login.credentials Credential mode (e.g., "same-origin")
* @property {Object} fetch.forgotPassword Fetch config for password reset code request
* @property {string} fetch.forgotPassword.url Endpoint to request a reset link/code
* @property {string} fetch.forgotPassword.method HTTP method
* @property {string} fetch.forgotPassword.mode Fetch mode
* @property {Object} fetch.forgotPassword.headers Headers
* @property {string} fetch.forgotPassword.headers.accept Accept header
* @property {string} fetch.forgotPassword.headers.Content-Type Content-Type header
* @property {string} fetch.forgotPassword.credentials Credential mode
* @property {Object} fetch.digits Fetch config for submitting password reset code
* @property {string} fetch.digits.url Endpoint for validating the code
* @property {string} fetch.digits.method HTTP method
* @property {string} fetch.digits.mode Fetch mode
* @property {Object} fetch.digits.headers Headers
* @property {string} fetch.digits.headers.accept Accept header
* @property {string} fetch.digits.headers.Content-Type Content-Type header
* @property {string} fetch.digits.credentials Credential mode
* @property {Object} fetch.secondFactor Fetch config for submitting second factor code
* @property {string} fetch.secondFactor.url Endpoint to validate second factor code
* @property {string} fetch.secondFactor.method HTTP method
* @property {string} fetch.secondFactor.mode Fetch mode
* @property {Object} fetch.secondFactor.headers Headers
* @property {string} fetch.secondFactor.headers.accept Accept header
* @property {string} fetch.secondFactor.headers.Content-Type Content-Type header
* @property {string} fetch.secondFactor.credentials Credential mode
*/
get defaults() {
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
labels: getTranslations(),
classes: {
usernameInvalid: "",
passwordInvalid: "",
emailInvalid: "",
button: "monster-button-outline-primary",
},
disabled: false,
features: {
forgotPassword: true,
redirectToFirstSuccessUrl: false,
},
actions: {},
callbacks : {
username : null,
forgotPassword : null,
},
digits: 6,
successUrls: [
{
label: "Home",
url: "/",
},
{
label: "Logout",
url: "/logout",
},
],
timeoutForMessage: 3500,
timeoutForSuccess: 1000,
accessKeys: {
loginLink: "x",
username: "u",
password: "p",
},
placeholder: {
username: "",
password: "",
email: "",
},
fetch: {
login: {
url: "",
method: "POST",
mode: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
credentials: "same-origin",
},
forgotPassword: {
url: "",
method: "POST",
mode: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
credentials: "same-origin",
},
digits: {
url: "",
method: "POST",
mode: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
credentials: "same-origin",
},
secondFactor: {
url: "",
method: "POST",
mode: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
credentials: "same-origin",
},
},
});
}
/**
* Opens the login collapse and focuses the username control.
*
* @returns {Login}
*/
openLogin() {
this[loginCollapseSymbol].open();
this.shadowRoot.querySelector("input[name='username']").focus();
return this;
}
/**
* Opens the forgot password collapse. If the feature `forgotPassword` is not enabled, an error will be thrown.
*
* @returns {Login}
* @throws {Error} If the feature is not enabled
*/
openForgotPassword() {
if (!this.getOption("features.forgotPassword")) {
throw new Error("Forgot Password is not enabled");
}
this[forgotPasswordCollapseSymbol].open();
return this;
}
/**
* Opens the second factor collapse and focuses the second factor control.
* @returns {Login}
*/
openSecondFactor() {
this[secondFactorCollapseSymbol].open();
this.shadowRoot.getElementById("secondFactorButton").focus();
return this;
}
/**
* Opens the digits collapse and focuses the digits control.
* @returns {Login}
*/
openDigits() {
this[digitsCollapseSymbol].open();
this.shadowRoot.getElementById("digitsControl").focus();
return this;
}
/**
* Opens the logged in collapse. If the feature `redirectToFirstSuccessUrl` is enabled, the user will be redirected to the first success URL.
*
* @returns {Login}
*/
openLoggedIn() {
fireEvent(this, "login-success");
if (this.getOption("features.redirectToFirstSuccessUrl")) {
setTimeout(() => {
fireEvent(this, "redirect-to-first-success-url");
const successUrl = this.getOption("successUrls");
if (successUrl.length > 0) {
const success = successUrl[0].url;
if (success) {
getWindow().location.href = success;
}
}
}, this.getOption("timeoutForSuccess"));
return;
}
this[loggedInCollapseSymbol].open();
return this;
}
/**
* @return {string}
*/
static getTag() {
return "monster-login";
}
/**
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [LoginStyleSheet, InvalidStyleSheet];
}
}
function getTranslations() {
const locale = getLocaleOfDocument();
switch (locale.language) {
case "de":
return {
username: "Benutzername oder E-Mail",
password: "Passwort",
login: "Anmelden",
forgotPasswordLink: "Passwort vergessen?",
mailAddress: "E-Mail-Adresse",
requestLink: "Code anfordern",
digits: "Code",
loginLink: "Anmelden",
secondFactor: "Zweiter Faktor Code",
sendDigits: "Code senden",
sendSecondFactorDigits: "Code senden",
resetLoginProcess: "Zurück zum Login",
messageEmptyUserName: "Bitte geben Sie Ihren Benutzernamen ein",
messageEmptyPassword: "Bitte geben Sie Ihr Passwort ein",
messageEmptyBoth:
"Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein",
messageEmptyEmail: "Bitte geben Sie Ihre E-Mail-Adresse ein",
messageInvalidEmail: "Bitte geben Sie eine gültige E-Mail-Adresse ein",
digitsEmpty: "Bitte geben Sie Ihren Code ein",
digitsInvalid: "Bitte geben Sie einen gültigen Code ein",
messageLoginFailed:
"Anmeldung fehlgeschlagen, bitte überprüfen Sie Ihre Eingaben.",
messageForbidden:
"Die Anmeldung war erfolgreich, aber Sie haben keine Berechtigung. Sie können sich <b>nicht</b> anmelden.<br>Sie können einen anderen Benutzer auswählen oder sich an den Administrator wenden.",
messageSomethingWentWrong:
"Etwas ist schief gelaufen, bitte versuchen Sie es später erneut",
messageThisFormIsNotConfigured:
"Dieses Formular ist nicht konfiguriert.",
messagePasswordResetDisabled:
"Sie können keinen Code anfordern, da die<br>Zwei-Faktor-Authentifizierung bei Ihrem Konto aktiviert ist.<br>Bitte kontaktieren Sie den Administrator.",
};
case "es":
return {
username: "Nombre de usuario o correo electrónico",
password: "Contraseña",
login: "Iniciar sesión",
forgotPasswordLink: "¿Olvidaste tu contraseña?",
mailAddress: "Dirección de correo electrónico",
requestLink: "Solicitar enlace",
digits: "Código",
loginLink: "Iniciar sesión",
secondFactor: "Código de segundo factor",
sendDigits: "Enviar código",
sendSecondFactorDigits: "Enviar código",
resetLoginProcess: "Volver al inicio de sesión",
messageEmptyUserName: "Por favor ingrese su nombre de usuario",
messageEmptyPassword: "Por favor ingrese su contraseña",
messageEmptyBoth: "Por favor ingrese su nombre de usuario y contraseña",
messageEmptyEmail:
"Por favor ingrese su dirección de correo electrónico",
messageInvalidEmail:
"Por favor ingrese una dirección de correo electrónico válida",
digitsEmpty: "Por favor ingrese su código",
digitsInvalid: "Por favor ingrese un código válido",
messageLoginFailed:
"Error al iniciar sesión, por favor verifique sus datos.",
messageForbidden:
"El inicio de sesión fue exitoso, pero no tienes permisos. <b>No puedes</b> iniciar sesión.<br>Puedes seleccionar otro usuario o contactar al administrador.",
messageSomethingWentWrong:
"Algo salió mal, por favor intenta de nuevo más tarde.",
messageThisFormIsNotConfigured: "Este formulario no está configurado.",
messagePasswordResetDisabled:
"Esta función no funciona porque la autenticación de dos factores está activada en su cuenta. Por favor, póngase en contacto con el administrador.",
};
case "zh":
return {
username: "用户名或电子邮箱",
password: "密码",
login: "登录",
forgotPasswordLink: "忘记密码?",
mailAddress: "电子邮件地址",
requestLink: "请求链接",
digits: "验证码",
loginLink: "登录",
secondFactor: "二次验证码",
sendDigits: "发送验证码",
sendSecondFactorDigits: "发送验证码",
resetLoginProcess: "返回登录",
messageEmptyUserName: "请输入用户名",
messageEmptyPassword: "请输入密码",
messageEmptyBoth: "请输入用户名和密码",
messageEmptyEmail: "请输入电子邮件地址",
messageInvalidEmail: "请输入有效的电子邮件地址",
digitsEmpty: "请输入验证码",
digitsInvalid: "请输入有效的验证码",
messageLoginFailed: "登录失败,请检查您的输入。",
messageForbidden:
"登录成功,但您没有权限。您<b>不能</b>登录。<br>您可以选择其他用户或联系管理员。",
messageSomethingWentWrong: "出了点问题,请稍后再试。",
messageThisFormIsNotConfigured: "此表单尚未配置。",
messagePasswordResetDisabled:
"此功能无法使用,因为您的帐户启用了双因素身份验证。请联系管理员。",
};
case "hi":
return {
username: "उपयगकर नम य ई-मल",
password: "पसवर",
login: "लग इन कर",
forgotPasswordLink: "पसवर भल गए?",
mailAddress: "ई-मल पत",
requestLink: "लक क अनरध कर",
digits: "कड",
loginLink: "लग इन कर",
secondFactor: "दसर फकर कड",
sendDigits: "कड भज",
sendSecondFactorDigits: "कड भज",
resetLoginProcess: "लगन पर वपस जए",
messageEmptyUserName: "कपय अपन उपयगकर नम दर कर",
messageEmptyPassword: "कपय अपन पसवर दर कर",
messageEmptyBoth: "कपय अपन उपयगकर नम और पसवर दर कर",
messageEmptyEmail: "कपय अपन ई-मल पत दर कर",
messageInvalidEmail: "कपय एक वध ई-मल पत दर कर",
digitsEmpty: "कपय अपन कड दर कर",
digitsInvalid: "कपय एक वध कड दर कर",
messageLoginFailed: "लगन वफल हआ, कपय अपन जनकर क जच कर।",
messageForbidden:
"लगन सफल रह लकन आपक पस अनमत नह ह। आप <b>लगन नह</b> कर सकत।<br>आप दसर उपयगकर चन सकत ह य पशसक स सपर कर सकत ह।",
messageSomethingWentWrong: "कछ गलत हआ, कपय बद म पन पयस कर।",
messageThisFormIsNotConfigured: "यह फर कनगर नह ह।",
messagePasswordResetDisabled:
"यह सवध कम नह करत कक आपक खत म द घटक पमणकरण सकम ह। कपय पशसक स सपर कर।",
};
case "bn":
return {
username: "ইউজরনম ব ই-মইল",
password: "পসওযর",
login: "লগইন করন",
forgotPasswordLink: "পসওযর ভল গছন?",
mailAddress: "ই-মইল ঠকন",
requestLink: "লক অনরধ করন",
digits: "কড",
loginLink: "লগইন করন",
secondFactor: "দতয ফকর কড",
sendDigits: "কড পঠন",
sendSecondFactorDigits: "কড পঠন",
resetLoginProcess: "লগইন ফর যন",
messageEmptyUserName: "দয কর আপনর ইউজরনম লখন",
messageEmptyPassword: "দয কর আপনর পসওযর লখন",
messageEmptyBoth: "দয কর আপনর ইউজরনম এব পসওযর লখন",
messageEmptyEmail: "দয কর আপনর ই-মইল ঠকন লখন",
messageInvalidEmail: "দয কর একট বধ ই-মইল ঠকন লখন",
digitsEmpty: "দয কর আপনর কড লখন",
digitsInvalid: "দয কর একট বধ কড লখন",
messageLoginFailed: "লগইন বর হযছ, দয কর আপনর ইনপট পরক করন।",
messageForbidden:
"লগইন সফল হযছ, কন আপনর অনমত নই। আপন <b>লগইন করত পরবন ন</b>।<br>আপন অন ইউজর নরচন করত পরন অথব অযডমনসটরর সথ যগযগ করত পরন।",
messageSomethingWentWrong: "কছ ভল হযছ, দয কর পর আবর চষ করন।",
messageThisFormIsNotConfigured: "এই ফরট কনফগর কর হযন।",
messagePasswordResetDisabled:
"এই ফশনট করকর নয করণ আপনর অযকউন দট ফকর পমণকরণ সকয কর আছ। দয কর পশসকর সথ যগযগ করন।",
};
case "pt": // Portuguese
return {
username: "Nome de usuário ou e-mail",
password: "Senha",
login: "Entrar",
forgotPasswordLink: "Esqueceu a senha?",
mailAddress: "Endereço de e-mail",
requestLink: "Solicitar link",
digits: "Código",
loginLink: "Entrar",
secondFactor: "Código do segundo fator",
sendDigits: "Enviar código",
sendSecondFactorDigits: "Enviar código",
resetLoginProcess: "Voltar ao login",
messageEmptyUserName: "Por favor, insira seu nome de usuário",
messageEmptyPassword: "Por favor, insira sua senha",
messageEmptyBoth: "Por favor, insira seu nome de usuário e senha",
messageEmptyEmail: "Por favor, insira seu endereço de e-mail",
messageInvalidEmail: "Por favor, insira um endereço de e-mail válido",
digitsEmpty: "Por favor, insira seu código",
digitsInvalid: "Por favor, insira um código válido",
messageLoginFailed: "Falha no login, verifique suas informações.",
messageForbidden:
"Login foi bem-sucedido, mas você não tem permissão. Você <b>não pode</b> entrar.<br>Você pode escolher outro usuário ou entrar em contato com o administrador.",
messageSomethingWentWrong:
"Algo deu errado, tente novamente mais tarde.",
messageThisFormIsNotConfigured: "Este formulário não está configurado.",
messagePasswordResetDisabled:
"Esta função não funciona porque a autenticação de dois fatores<br>está ativada em sua conta. Por favor, entre em contato com o administrador.",
};
case "ru": // Russian
return {
username: "Имя пользователя или электронная почта",
password: "Пароль",
login: "Войти",
forgotPasswordLink: "Забыли пароль?",
mailAddress: "Адрес электронной почты",
requestLink: "Запросить ссылку",
digits: "Код",
loginLink: "Войти",
secondFactor: "Код второго фактора",
sendDigits: "Отправить код",
sendSecondFactorDigits: "Отправить код",
resetLoginProcess: "Вернуться к входу",
messageEmptyUserName: "Пожалуйста, введите имя пользователя",
messageEmptyPassword: "Пожалуйста, введите пароль",
messageEmptyBoth: "Пожалуйста, введите имя пользователя и пароль",
messageEmptyEmail: "Пожалуйста, введите адрес электронной почты",
messageInvalidEmail:
"Пожалуйста, введите действительный адрес электронной почты",
digitsEmpty: "Пожалуйста, введите код",
digitsInvalid: "Пожалуйста, введите действительный код",
messageLoginFailed: "Ошибка входа, проверьте введенные данные.",
messageForbidden:
"Вход выполнен, но у вас нет прав. Вы <b>не можете</b> войти.<br>Вы можете выбрать другого пользователя или связаться с администратором.",
messageSomethingWentWrong: "Что-то пошло не так, попробуйте позже.",
messageThisFormIsNotConfigured: "Эта форма не настроена.",
messagePasswordResetDisabled:
"Эта функция не работает, потому что двухфакторная аутентификация<br> включена в вашей учетной записи. Пожалуйста, свяжитесь с администратором.",
};
case "ja": // Japanese
return {
username: "ユーザー名またはメールアドレス",
password: "パスワード",
login: "ログイン",
forgotPasswordLink: "パスワードを忘れましたか?",
mailAddress: "メールアドレス",
requestLink: "リンクをリクエスト",
digits: "コード",
loginLink: "ログイン",
secondFactor: "二要素コード",
sendDigits: "コードを送信",
sendSecondFactorDigits: "コードを送信",
resetLoginProcess: "ログインに戻る",
messageEmptyUserName: "ユーザー名を入力してください",
messageEmptyPassword: "パスワードを入力してください",
messageEmptyBoth: "ユーザー名とパスワードを入力してください",
messageEmptyEmail: "メールアドレスを入力してください",
messageInvalidEmail: "有効なメールアドレスを入力してください",
digitsEmpty: "コードを入力してください",
digitsInvalid: "有効なコードを入力してください",
messageLoginFailed:
"ログインに失敗しました。入力内容をご確認ください。",
messageForbidden:
"ログイン成功しましたが、権限がありません。<b>ログインできません</b>。<br>他のユーザーを選択するか、管理者に連絡してください。",
messageSomethingWentWrong:
"問題が発生しました。後でもう一度お試しください。",
messageThisFormIsNotConfigured: "このフォームは設定されていません。",
messagePasswordResetDisabled:
"この機能は使用できません。アカウントで二要素認証が有効になっているため、管理者に連絡してください。",
};
case "pa": // Western Punjabi
return {
username: "ਵਰਤਕਰ ਨਮ ਜ ਈ-ਮਲ",
password: "ਪਸਵਰਡ",
login: "ਲਗਨ ਕਰ",
forgotPasswordLink: "ਪਸਵਰਡ ਭਲ ਗਏ?",
mailAddress: "ਈ-ਮਲ ਪਤ",
requestLink: "ਲਕ ਦ ਬਨਤ ਕਰ",
digits: "ਕਡ",
loginLink: "ਲਗਨ ਕਰ",
secondFactor: "ਦਜ ਫਕਟਰ ਕਡ",
sendDigits: "ਕਡ ਭਜ",
sendSecondFactorDigits: "ਕਡ ਭਜ",
resetLoginProcess: "ਲਗਨ 'ਤ ਵਪਸ ਜਓ",
messageEmptyUserName: "ਕਰਪ ਕਰਕ ਆਪਣ ਵਰਤਕਰ ਨਮ ਦਰਜ ਕਰ",
messageEmptyPassword: "ਕਰਪ ਕਰਕ ਆਪਣ ਪਸਵਰਡ ਦਰਜ ਕਰ",
messageEmptyBoth: "ਕਰਪ ਕਰਕ ਆਪਣ ਵਰਤਕਰ ਨਮ ਤ ਪਸਵਰਡ ਦਰਜ ਕਰ",
messageEmptyEmail: "ਕਰਪ ਕਰਕ ਆਪਣ ਈ-ਮਲ ਪਤ ਦਰਜ ਕਰ",
messageInvalidEmail: "ਕਰਪ ਕਰਕ ਇਕ ਵਧ ਈ-ਮਲ ਪਤ ਦਰਜ ਕਰ",
digitsEmpty: "ਕਰਪ ਕਰਕ ਆਪਣ ਕਡ ਦਰਜ ਕਰ",
digitsInvalid: "ਕਰਪ ਕਰਕ ਇਕ ਵਧ ਕਡ ਦਰਜ ਕਰ",
messageLoginFailed: "ਲਗਨ ਅਸਫਲ ਹਇਆ, ਕਰਪ ਕਰਕ ਆਪਣ ਜਣਕਰ ਦ ਜਚ ਕਰ।",
messageForbidden:
"ਲਗਨ ਸਫਲ ਹਇਆ, ਪਰ ਤਹਡ ਕਲ ਅਧਕਰ ਨਹ ਹਨ। ਤਸ <b>ਲਗਨ ਨਹ</b> ਕਰ ਸਕਦ।<br>ਤਸ ਹਰ ਵਰਤਕਰ ਚਣ सकत ਹ ਜ ਪਰਬਧਕ ਨਲ ਸਪਰਕ ਕਰ ਸਕਦ ਹ।",
messageSomethingWentWrong: "ਕਝ ਗਲਤ ਹ ਗਆ, ਕਰਪ ਕਰਕ ਬਅਦ ਵਚ ਮੜ ਕਸਸ ਕਰ।",
messageThisFormIsNotConfigured: "ਇਸ ਫਰਮ ਨ ਸਰਚਤ ਨਹ ਕਤ ਗਆ ਹ।",
messagePasswordResetDisabled:
"ਇਹ ਫਕਸਨ ਕਮ ਨਹ ਕਰਦ ਕਉਕ ਤਹਡ ਖਤ ਵਚ ਦ ਫਕਟਰ ਪਰਮਣਕਰਣ ਸਰਗਰਮ ਹ। ਕਰਪ ਕਰਕ ਪਰਬਧਕ ਨਲ ਸਪਰਕ ਕਰ।",
};
case "mr": // Marathi
return {
username: "वपरकरनव कव ईमल",
password: "सकतशब",
login: "पवश कर",
forgotPasswordLink: "सकतशब वसरलत क?",
mailAddress: "ईमल पत",
requestLink: "लकच वनत कर",
digits: "कड",
loginLink: "पवश कर",
secondFactor: "दसर घटक कड",
sendDigits: "कड पठव",
sendSecondFactorDigits: "कड पठव",
resetLoginProcess: "पवशवर परत ज",
messageEmptyUserName: "कपय आपल वपरकरनव पवष कर",
messageEmptyPassword: "कपय आपल सकतशब पवष कर",
messageEmptyBoth: "कपय आपल वपरकरनव आण सकतशब पवष कर",
messageEmptyEmail: "कपय आपल ईमल पत पवष कर",
messageInvalidEmail: "कपय वध ईमल पत पवष कर",
digitsEmpty: "कपय आपल कड पवष कर",
digitsInvalid: "कपय वध कड पवष कर",
messageLoginFailed: "पवश अयशस, कपय आपल महत तपस.",
messageForbidden:
"पवश यशस झल, परत आपलकड परवन नह. आपण <b>पवश कर शकत नह</b>.<br>आपण दसर वपरकर नवड शकत कव ववसपकश सपर सध शकत.",
messageSomethingWentWrong: "कहतर चकल, कपय नतर पन पयत कर.",
messageThisFormIsNotConfigured: "ह फर सरचत कलल नह.",
messagePasswordResetDisabled:
"य करच कमगर करत नह करण आपल खतत दन घटक पमणकरण सकय कल आह. कपय ववसपकश सपर सध.",
};
case "fr": // French
return {
username: "Nom d'utilisateur ou e-mail",
password: "Mot de passe",
login: "Connexion",
forgotPasswordLink: "Mot de passe oublié ?",
mailAddress: "Adresse e-mail",
requestLink: "Demander un lien",
digits: "Code",
loginLink: "Connexion",
secondFactor: "Code du deuxième facteur",
sendDigits: "Envoyer le code",
sendSecondFactorDigits: "Envoyer le code",
resetLoginProcess: "Retour à la connexion",
messageEmptyUserName: "Veuillez entrer votre nom d'utilisateur",
messageEmptyPassword: "Veuillez entrer votre mot de passe",
messageEmptyBoth:
"Veuillez entrer votre nom d'utilisateur et votre mot de passe",
messageEmptyEmail: "Veuillez entrer votre adresse e-mail",
messageInvalidEmail: "Veuillez entrer une adresse e-mail valide",
digitsEmpty: "Veuillez entrer votre code",
digitsInvalid: "Veuillez entrer un code valide",
messageLoginFailed:
"Échec de la connexion, veuillez vérifier vos informations.",
messageForbidden:
"La connexion a réussi, mais vous n'avez pas les permissions. Vous <b>ne pouvez pas</b> vous connecter.<br>Vous pouvez choisir un autre utilisateur ou contacter l'administrateur.",
messageSomethingWentWrong:
"Une erreur s'est produite, veuillez réessayer plus tard.",
messageThisFormIsNotConfigured: "Ce formulaire n'est pas configuré.",
messagePasswordResetDisabled:
"Cette fonctionnalité ne fonctionne pas car l'authentification à deux facteurs est activée sur<br>votre compte. Veuillez contacter l'administrateur.",
};
case "it": // Italian
return {
username: "Nome utente o e-mail",
password: "Password",
login: "Accedi",
forgotPasswordLink: "Password dimenticata?",
mailAddress: "Indirizzo e-mail",
requestLink: "Richiedi link",
digits: "Codice",
loginLink: "Accedi",
secondFactor: "Codice a due fattori",
sendDigits: "Invia codice",
sendSecondFactorDigits: "Invia codice",
resetLoginProcess: "Torna al login",
messageEmptyUserName: "Per favore, inserisci il tuo nome utente",
messageEmptyPassword: "Per favore, inserisci la tua password",
messageEmptyBoth:
"Per favore, inserisci il tuo nome utente e la tua password",
messageEmptyEmail: "Per favore, inserisci il tuo indirizzo e-mail",
messageInvalidEmail: "Per favore, inserisci un indirizzo e-mail valido",
digitsEmpty: "Per favore, inserisci il tuo codice",
digitsInvalid: "Per favore, inserisci un codice valido",
messageLoginFailed: "Accesso fallito, verifica i tuoi dati.",
messageForbidden:
"Accesso riuscito, ma non hai i permessi. Non puoi accedere.<br>Puoi scegliere un altro account o contattare l'amministratore.",
messageSomethingWentWrong:
"Qualcosa è andato storto, riprova più tardi.",
messageThisFormIsNotConfigured: "Questo modulo non è configurato.",
messagePasswordResetDisabled:
"Questa funzione non funziona perché l'autenticazione a due<br>fattori è attiva sul tuo account. Contatta l'amministratore.",
};
case "nl": // Dutch
return {
username: "Gebruikersnaam of e-mail",
password: "Wachtwoord",
login: "Inloggen",
forgotPasswordLink: "Wachtwoord vergeten?",
mailAddress: "E-mailadres",
requestLink: "Link aanvragen",
digits: "Code",
loginLink: "Inloggen",
secondFactor: "Tweede factor code",
sendDigits: "Code verzenden",
sendSecondFactorDigits: "Code verzenden",
resetLoginProcess: "Terug naar inloggen",
messageEmptyUserName: "Voer uw gebruikersnaam in",
messageEmptyPassword: "Voer uw wachtwoord in",
messageEmptyBoth: "Voer uw gebruikersnaam en wachtwoord in",
messageEmptyEmail: "Voer uw e-mailadres in",
messageInvalidEmail: "Voer een geldig e-mailadres in",
digitsEmpty: "Voer uw code in",
digitsInvalid: "Voer een geldige code in",
messageLoginFailed: "Inloggen mislukt, controleer uw gegevens.",
messageForbidden:
"Inloggen geslaagd, maar u heeft geen permissies. U <b>kunt niet</b> inloggen.<br>U kunt een andere gebruiker selecteren of de beheerder contacteren.",
messageSomethingWentWrong:
"Er is iets fout gegaan, probeer het later opnieuw.",
messageThisFormIsNotConfigured: "Dit formulier is niet geconfigureerd.",
messagePasswordResetDisabled:
"Deze functie werkt niet omdat tweefactorauthenticatie is<br>ingeschakeld op uw account. Neem contact op met de beheerder.",
};
case "sv": // Swedish
return {
username: "Användarnamn eller e-post",
password: "Lösenord",
login: "Logga in",
forgotPasswordLink: "Glömt lösenord?",
mailAddress: "E-postadress",
requestLink: "Begär länk",
digits: "Kod",
loginLink: "Logga in",
secondFactor: "Tvåfaktorkod",
sendDigits: "Skicka kod",
sendSecondFactorDigits: "Skicka kod",
resetLoginProcess: "Tillbaka till inloggning",
messageEmptyUserName: "Ange ditt användarnamn",
messageEmptyPassword: "Ange ditt lösenord",
messageEmptyBoth: "Ange ditt användarnamn och lösenord",
messageEmptyEmail: "Ange din e-postadress",
messageInvalidEmail: "Ange en giltig e-postadress",
digitsEmpty: "Ange din kod",
digitsInvalid: "Ange en giltig kod",
messageLoginFailed:
"Inloggning misslyckades, kontrollera dina uppgifter.",
messageForbidden:
"Inloggningen lyckades, men du har inte behörighet. Du <b>kan inte</b> logga in.<br>Du kan välja en annan användare eller kontakta administratören.",
messageSomethingWentWrong: "Något gick fel, försök igen senare.",
messageThisFormIsNotConfigured: "Detta formulär är inte konfigurerat.",
messagePasswordResetDisabled:
"Denna funktion fungerar inte eftersom tvåfaktorsautentisering är aktiverad på ditt konto. Kontakta administratören.",
};
case "pl": // Polish
return {
username: "Nazwa użytkownika lub e-mail",
password: "Hasło",
login: "Zaloguj się",
forgotPasswordLink: "Zapomniałeś hasła?",
mailAddress: "Adres e-mail",
requestLink: "Zażądaj linku",
digits: "Kod",
loginLink: "Zaloguj się",
secondFactor: "Kod drugiego czynnika",
sendDigits: "Wyślij kod",
sendSecondFactorDigits: "Wyślij kod",
resetLoginProcess: "Powrót do logowania",
messageEmptyUserName: "Wprowadź swoją nazwę użytkownika",
messageEmptyPassword: "Wprowadź swoje hasło",
messageEmptyBoth: "Wprowadź swoją nazwę użytkownika i hasło",
messageEmptyEmail: "Wprowadź swój adres e-mail",
messageInvalidEmail: "Wprowadź prawidłowy adres e-mail",
digitsEmpty: "Wprowadź swój kod",
digitsInvalid: "Wprowadź prawidłowy kod",
messageLoginFailed: "Logowanie nie powiodło się, sprawdź swoje dane.",
messageForbidden:
"Logowanie powiodło się, ale nie masz uprawnień. <b>Nie możesz</b> się zalogować.<br>Możesz wybrać innego użytkownika lub skontaktować się z administratorem.",
messageSomethingWentWrong:
"Coś poszło nie tak, spróbuj ponownie później.",
messageThisFormIsNotConfigured:
"Ten formularz nie jest skonfigurowany.",
messagePasswordResetDisabled:
"Ta funkcja nie działa, ponieważ włączona jest autoryzacja dwuetapowa na twoim koncie. Skontaktuj się z administratorem.",
};
case "da": // Danish
return {
username: "Brugernavn eller e-mail",
password: "Adgangskode",
login: "Log ind",
forgotPasswordLink: "Glemt adgangskode?",
mailAddress: "E-mail-adresse",
requestLink: "Anmod om link",
digits: "Kode",
loginLink: "Log ind",
secondFactor: "Tofaktorkode",
sendDigits: "Send kode",
sendSecondFactorDigits: "Send kode",
resetLoginProcess: "Tilbage til log ind",
messageEmptyUserName: "Indtast dit brugernavn",
messageEmptyPassword: "Indtast din adgangskode",
messageEmptyBoth: "Indtast dit brugernavn og din adgangskode",
messageEmptyEmail: "Indtast din e-mail-adresse",
messageInvalidEmail: "Indtast en gyldig e-mail-adresse",
digitsEmpty: "Indtast din kode",
digitsInvalid: "Indtast en gyldig kode",
messageLoginFailed: "Login mislykkedes, kontrollér dine oplysninger.",
messageForbidden:
"Login lykkedes, men du har ikke tilladelse. Du <b>kan ikke</b> logge ind.<br>Du kan vælge en anden bruger eller kontakte administratoren.",
messageSomethingWentWrong: "Noget gik galt, prøv igen senere.",
messageThisFormIsNotConfigured: "Denne formular er ikke konfigureret.",
messagePasswordResetDisabled:
"Denne funktion fungerer ikke, fordi tofaktorautentificering<br>er aktiveret på din konto. Kontakt administratoren.",
};
case "no": // Norwegian
return {
username: "Brukernavn eller e-post",
password: "Passord",
login: "Logg inn",
forgotPasswordLink: "Glemt passord?",
mailAddress: "E-postadresse",
requestLink: "Be om lenke",
digits: "Kode",
loginLink: "Logg inn",
secondFactor: "Tofaktorkode",
sendDigits: "Send kode",
sendSecondFactorDigits: "Send kode",
resetLoginProcess: "Tilbake til innlogging",
messageEmptyUserName: "Vennligst skriv inn brukernavnet ditt",
messageEmptyPassword: "Vennligst skriv inn passordet ditt",
messageEmptyBoth: "Vennligst skriv inn brukernavn og passord",
messageEmptyEmail: "Vennligst skriv inn e-postadressen din",
messageInvalidEmail: "Vennligst skriv inn en gyldig e-postadresse",
digitsEmpty: "Vennligst skriv inn koden din",
digitsInvalid: "Vennligst skriv inn en gyldig kode",
messageLoginFailed:
"Innlogging mislyktes, vennligst sjekk informasjonen din.",
messageForbidden:
"Innloggingen var vellykket, men du har ikke tillatelse. Du <b>kan ikke</b> logge inn.<br>Du kan velge en annen bruker eller kontakte administratoren.",
messageSomethingWentWrong:
"Noe gikk galt, vennligst prøv igjen senere.",
messageThisFormIsNotConfigured: "Dette skjemaet er ikke konfigurert.",
messagePasswordResetDisabled:
"Denne funksjonen fungerer ikke fordi<br>tofaktorautentisering er aktivert på kontoen din. Kontakt administratoren.",
};
case "cs": // Czech
return {
username: "Uživatelské jméno nebo e-mail",
password: "Heslo",
login: "Přihlásit se",
forgotPasswordLink: "Zapomněli jste heslo?",
mailAddress: "E-mailová adresa",
requestLink: "Požádat o odkaz",
digits: "Kód",
loginLink: "Přihlásit se",
secondFactor: "Dvoufázový kód",
sendDigits: "Odeslat kód",
sendSecondFactorDigits: "Odeslat kód",
resetLoginProcess: "Zpět na přihlášení",
messageEmptyUserName: "Zadejte své uživatelské jméno",
messageEmptyPassword: "Zadejte své heslo",
messageEmptyBoth: "Zadejte své uživatelské jméno a heslo",
messageEmptyEmail: "Zadejte svou e-mailovou adresu",
messageInvalidEmail: "Zadejte platnou e-mailovou adresu",
digitsEmpty: "Zadejte svůj kód",
digitsInvalid: "Zadejte platný kód",
messageLoginFailed: "Přihlášení se nezdařilo, zkontrolujte své údaje.",
messageForbidden:
"Přihlášení bylo úspěšné, ale nemáte oprávnění. Nemůžete se přihlásit.<br>Vyberte jiného uživatele nebo kontaktujte správce.",
messageSomethingWentWrong: "Něco se pokazilo, zkuste to později.",
messageThisFormIsNotConfigured: "Tento formulář není nakonfigurován.",
messagePasswordResetDisabled:
"Tato funkce nefunguje, protože je na vašem<br>účtu aktivováno dvoufaktorové ověřování. Kontaktujte správce.",
};
default:
return {
username: "Username or E-Mail",
password: "Password",
login: "Login",
forgotPasswordLink: "Forgot Password?",
mailAddress: "E-Mail-Address",
requestLink: "Request Link",
digits: "Code",
loginLink: "Login",
secondFactor: "Second Factor Code",
sendDigits: "Send Code",
sendSecondFactorDigits: "Send Code",
resetLoginProcess: "Back to Login",
messageEmptyUserName: "Please enter your username",
messageEmptyPassword: "Please enter your password",
messageEmptyBoth: "Please enter your username and password",
messageEmptyEmail: "Please enter your email address",
messageInvalidEmail: "Please enter a valid email address",
digitsEmpty: "Please enter your code",
digitsInvalid: "Please enter a valid code",
messageLoginFailed: "Login failed, please check your input.",
messageForbidden:
"Login was successful, but you have no permission. You <b>cannot</b> login.<br>You can select another user or contact the administrator.",
messageSomethingWentWrong:
"Something went wrong, please try again later.",
messageThisFormIsNotConfigured: "This form is not configured.",
messagePasswordResetDisabled:
"This function does not work because two-factor<br>authentication is enabled on your account.<br>Please contact the administrator.",
};
}
}
/**
* @private
* @return {initEventHandler}
*/
function initEventHandler() {
const self = this;
const element = this[loginElementSymbol];
const type = "click";
element.addEventListener(type, function (event) {
const callback = self.getOption("actions.click");
fireCustomEvent(self, "monster-login-clicked", {
element: self,
});
if (!isFunction(callback)) {
return;
}
const element = findTargetElementFromEvent(
event,
ATTRIBUTE_ROLE,
"control",
);
if (!(element instanceof Node && self.hasNode(element))) {
return;
}
callback.call(self, event);
});
this[forgotPasswordLinkSymbol].addEventListener(type, (event) => {
event.preventDefault();
this[forgotPasswordCollapseSymbol].open();
setTimeout(() => {
this.shadowRoot.querySelector("input[name='email']").focus();
}, 0);
});
this[loginLinkSymbol].addEventListener(type, (event) => {
event.preventDefault();
this[loginCollapseSymbol].open();
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
});
for (const e of this[resetLoginProcessLinksSymbol]) {
e.addEventListener(type, (event) => {
event.preventDefault();
this[loginCollapseSymbol].open();
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
});
}
this[loginCollapseSymbol].addEventListener("keydown", (event) => {
if (event.key === "Enter") {
this[loginButtonSymbol].click();
}
});
this[secondFactorCollapseSymbol].addEventListener("keydown", (event) => {
if (event.key === "Enter") {
this[secondFactorButtonSymbol].click();
}
});
this[forgotPasswordCollapseSymbol].addEventListener("keydown", (event) => {
if (event.key === "Enter") {
this[requestLinkButtonSymbol].click();
}
});
this[digitsCollapseSymbol].addEventListener("keydown", (event) => {
if (event.key === "Enter") {
this[digitsButtonSymbol].click();
}
});
this[loginButtonSymbol].setOption("actions.click", (event) => {
let username = this.shadowRoot.querySelector(
"input[name='username']",
).value;
const userCallback = this.getOption("callbacks.username");
if (isFunction(userCallback)) {
const userCallbackResult = userCallback.call(this, username);
if (userCallbackResult !== undefined) {
username = userCallbackResult;
}
}
const password = this.shadowRoot.querySelector("monster-password").value;
let missingBits = 0;
if (username === "" || username === null) {
this.setOption("classes.usernameInvalid", "invalid");
missingBits |= 1; // Set bit 1 for username
} else {
this.setOption("classes.usernameInvalid", "");
}
if (password === "" || password === null) {
this.setOption("classes.passwordInvalid", "invalid");
missingBits |= 2; // Set bit 2 for password
} else {
this.setOption("classes.passwordInvalid", "");
}
let msg = null;
if (missingBits === 1) {
// missing username
msg = this.getOption("labels.messageEmptyUserName");
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
} else if (missingBits === 2) {
// missing password
msg = this.getOption("labels.messageEmptyPassword");
setTimeout(() => {
this.shadowRoot.querySelector("monster-password").focus();
});
} else if (missingBits === 3) {
msg = this.getOption("labels.messageEmptyBoth");
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
}
const timeout = this.getOption("timeoutForMessage");
if (msg !== null && msg !== undefined) {
this[loginButtonSymbol].setMessage(msg);
this[loginButtonSymbol].showMessage(timeout);
this[loginButtonSymbol].setState("failed", timeout);
return;
}
const url = this.getOption("fetch.login.url");
if (url === "" || url === null || url === undefined) {
this[loginButtonSymbol].setMessage(
this.getOption("labels.messageThisFormIsNotConfigured"),
);
this[loginButtonSymbol].showMessage(timeout);
this[loginButtonSymbol].setState("failed", timeout);
return;
}
const options = {
method: this.getOption("fetch.login.method"),
mode: this.getOption("fetch.login.mode"),
headers: this.getOption("fetch.login.headers"),
credentials: this.getOption("fetch.login.credentials"),
body: JSON.stringify({ username, password }),
};
getWindow()
.fetch(url, options)
.then((response) => {
if (response.ok) {
this[loginButtonSymbol].setState("successful", timeout);
setTimeout(() => {
this.openLoggedIn();
}, 1200);
} else {
if (response.status === 403) {
this[loginButtonSymbol].setMessage(
this.getOption("labels.messageForbidden"),
);
} else if (response.status === 401) {
const wwwAuthenticateHeader =
response.headers.get("www-authenticate");
if (wwwAuthenticateHeader) {
const wwwAuthenticateParts = wwwAuthenticateHeader
.split(/,\s*/)
.map((part) => {
const [key, value] = part.split("=");
return {
key: key.trim(),
value: value ? value.trim().replace(/^"|"$/g, "") : null,
};
});
const filteredParts = wwwAuthenticateParts.filter(
(part) => part.key.toLowerCase() === "2fa",
);
if (filteredParts.length > 0) {
const timeout = this.getOption("timeoutForSuccess");
this[loginButtonSymbol].setState("successful", timeout);
setTimeout(() => {
this.openSecondFactor();
const digitsElement = this.shadowRoot.getElementById(
"secondFactorControl",
);
digitsElement.focus();
}, timeout);
return;
}
}
this[loginButtonSymbol].setMessage(
this.getOption("labels.messageLoginFailed"),
);
} else {
this[loginButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
}
this[loginButtonSymbol].showMessage(timeout);
this[loginButtonSymbol].setState("failed", timeout);
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
}
})
.catch((error) => {
this[loginButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
this[loginButtonSymbol].showMessage(timeout);
this[loginButtonSymbol].setState("failed", timeout);
setTimeout(() => {
this.shadowRoot.querySelector("input[name='username']").focus();
}, 0);
});
});
this[requestLinkButtonSymbol].setOption("actions.click", (event) => {
const emailElement = this.shadowRoot.querySelector("input[name='email']");
// get username and password
let mail = emailElement.value;
let valid = emailElement.checkValidity();
const mailCallback = this.getOption("callbacks.forgotPassword");
if (isFunction(mailCallback)) {
const mailCallbackResult = mailCallback.call(this, mail);
if (mailCallbackResult !== undefined) {
mail = mailCallbackResult;
valid = true;
}
}
let msg = null;
if (mail === "" || mail === null) {
this.setOption("classes.emailInvalid", "invalid");
msg = this.getOption("labels.messageEmptyEmail");
} else if (!valid) {
this.setOption("classes.emailInvalid", "invalid");
msg = this.getOption("labels.messageInvalidEmail");
} else {
this.setOption("classes.emailInvalid", "");
}
const timeout = this.getOption("timeoutForMessage");
if (msg !== null && msg !== undefined) {
this[requestLinkButtonSymbol].setMessage(msg);
this[requestLinkButtonSymbol].showMessage(timeout);
this[requestLinkButtonSymbol].setState("failed", timeout);
return;
}
const url = this.getOption("fetch.forgotPassword.url");
if (url === "" || url === null || url === undefined) {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messageThisFormIsNotConfigured"),
);
this[requestLinkButtonSymbol].showMessage(timeout);
this[requestLinkButtonSymbol].setState("failed", timeout);
return;
}
const options = {
method: this.getOption("fetch.forgotPassword.method"),
mode: this.getOption("fetch.forgotPassword.mode"),
headers: this.getOption("fetch.forgotPassword.headers"),
credentials: this.getOption("fetch.forgotPassword.credentials"),
body: JSON.stringify({ mail }),
};
getWindow()
.fetch(url, options)
.then((response) => {
if (response.ok) {
const timeout = this.getOption("timeoutForSuccess");
this[requestLinkButtonSymbol].setState("successful", timeout);
setTimeout(() => {
this.openDigits();
}, timeout);
} else {
if (response.status === 403) {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messageForbidden"),
);
} else if (response.status === 401) {
if (
response.headers.has("x-password-reset") &&
response.headers.get("x-password-reset").includes("disabled")
) {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messagePasswordResetDisabled"),
);
} else {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messageLoginFailed"),
);
}
} else {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
}
this[requestLinkButtonSymbol].showMessage(timeout);
this[requestLinkButtonSymbol].setState("failed", timeout);
}
})
.catch(() => {
this[requestLinkButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
this[requestLinkButtonSymbol].showMessage(timeout);
this[requestLinkButtonSymbol].setState("failed", timeout);
});
});
this[secondFactorButtonSymbol].setOption("actions.click", (event) => {
const digitsElement = this.shadowRoot.getElementById("secondFactorControl");
const digits = digitsElement.value;
const valid = digitsElement.checkValidity();
let msg = null;
if (digits === "" || digits === null) {
msg = this.getOption("labels.digitsEmpty");
} else if (!valid) {
msg = this.getOption("labels.digitsInvalid");
}
const timeout = this.getOption("timeoutForMessage");
if (msg !== null && msg !== undefined) {
this[secondFactorButtonSymbol].setMessage(msg);
this[secondFactorButtonSymbol].showMessage(timeout);
this[secondFactorButtonSymbol].setState("failed", timeout);
return;
}
const url = this.getOption("fetch.secondFactor.url");
if (url === "" || url === null || url === undefined) {
this[secondFactorButtonSymbol].setMessage(
this.getOption("labels.messageThisFormIsNotConfigured"),
);
this[secondFactorButtonSymbol].showMessage(timeout);
this[secondFactorButtonSymbol].setState("failed", timeout);
return;
}
const options = {
method: this.getOption("fetch.secondFactor.method"),
mode: this.getOption("fetch.secondFactor.mode"),
headers: this.getOption("fetch.secondFactor.headers"),
credentials: this.getOption("fetch.secondFactor.credentials"),
body: JSON.stringify({ digits }),
};
getWindow()
.fetch(url, options)
.then((response) => {
if (response.ok) {
const timeout = this.getOption("timeoutForSuccess");
this[secondFactorButtonSymbol].setState("successful", timeout);
setTimeout(() => {
this.openLoggedIn();
}, timeout);
} else {
if (response.status === 403) {
this[secondFactorButtonSymbol].setMessage(
this.getOption("labels.messageForbidden"),
);
} else if (response.status === 401) {
this[secondFactorButtonSymbol].setMessage(
this.getOption("labels.messageLoginFailed"),
);
} else {
this[secondFactorButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
}
this[secondFactorButtonSymbol].showMessage(timeout);
this[secondFactorButtonSymbol].setState("failed", timeout);
setTimeout(() => {
digitsElement.focus();
}, 0);
}
})
.catch(() => {
this[secondFactorButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
this[secondFactorButtonSymbol].showMessage(timeout);
this[secondFactorButtonSymbol].setState("failed", timeout);
setTimeout(() => {
digitsElement.focus();
}, 0);
});
});
this[digitsButtonSymbol].setOption("actions.click", (event) => {
const digitsElement = this.shadowRoot.getElementById("digitsControl");
const digits = digitsElement.value;
const valid = digitsElement.checkValidity();
let msg = null;
if (digits === "" || digits === null || digits === undefined) {
msg = this.getOption("labels.digitsEmpty");
} else if (!valid) {
msg = this.getOption("labels.digitsInvalid");
}
const timeout = this.getOption("timeoutForMessage");
if (msg !== null && msg !== undefined) {
this[digitsButtonSymbol].setMessage(msg);
this[digitsButtonSymbol].showMessage(timeout);
this[digitsButtonSymbol].setState("failed", timeout);
return;
}
const url = this.getOption("fetch.digits.url");
if (url === "" || url === null || url === undefined) {
this[digitsButtonSymbol].setMessage(
this.getOption("labels.messageThisFormIsNotConfigured"),
);
this[digitsButtonSymbol].showMessage(timeout);
this[digitsButtonSymbol].setState("failed", timeout);
return;
}
const options = {
method: this.getOption("fetch.digits.method"),
mode: this.getOption("fetch.digits.mode"),
headers: this.getOption("fetch.digits.headers"),
credentials: this.getOption("fetch.digits.credentials"),
body: JSON.stringify({ digits }),
};
getWindow()
.fetch(url, options)
.then((response) => {
const timeout = this.getOption("timeoutForSuccess");
if (response.ok) {
this[digitsButtonSymbol].setState("successful", timeout);
setTimeout(() => {
this.openLoggedIn();
}, timeout);
} else {
if (response.status === 403) {
this[digitsButtonSymbol].setMessage(
this.getOption("labels.messageForbidden"),
);
} else if (response.status === 401) {
this[digitsButtonSymbol].setMessage(
this.getOption("labels.messageLoginFailed"),
);
} else {
this[digitsButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
}
this[digitsButtonSymbol].showMessage(timeout);
this[digitsButtonSymbol].setState("failed", timeout);
setTimeout(() => {
digitsElement.focus();
}, 0);
}
})
.catch(() => {
this[digitsButtonSymbol].setMessage(
this.getOption("labels.messageSomethingWentWrong"),
);
this[digitsButtonSymbol].showMessage(timeout);
this[digitsButtonSymbol].setState("failed", timeout);
setTimeout(() => {
digitsElement.focus();
}, 0);
});
});
return this;
}
/**
* @private
* @return {void}
*/
function initControlReferences() {
this[loginElementSymbol] = this.shadowRoot.querySelector(
`[${ATTRIBUTE_ROLE}="control"]`,
);
// data-monster-role="forgot-password-link"
this[forgotPasswordLinkSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="forgot-password-link"]`,
);
this[forgotPasswordCollapseSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="forgot-password-collapse"]`,
);
this[loginCollapseSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="login-collapse"]`,
);
this[loginLinkSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="login-link"]`,
);
this[secondFactorCollapseSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="second-factor-collapse"]`,
);
this[resetLoginProcessLinksSymbol] = this.shadowRoot.querySelectorAll(
`[data-monster-role="reset-login-process-link"]`,
);
this[loggedInCollapseSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="logged-in-collapse"]`,
);
this[digitsCollapseSymbol] = this.shadowRoot.querySelector(
`[data-monster-role="digits-collapse"]`,
);
this[loginButtonSymbol] = this.shadowRoot.getElementById("loginButton");
this[requestLinkButtonSymbol] =
this.shadowRoot.getElementById("requestLinkButton");
this[secondFactorButtonSymbol] =
this.shadowRoot.getElementById("secondFactorButton");
this[digitsButtonSymbol] = this.shadowRoot.getElementById("digitsButton");
}
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<template id="urls">
<li><a data-monster-attributes="href path:urls.url"
data-monster-replace="path:urls.label"></a></li>
</template>
<div data-monster-role="control" part="control">
<monster-collapse data-monster-option-openByDefault="true"
data-monster-role="login-collapse"
exportparts="control:collapse-login-control,
container:collapse-login-container,
deco:collapse-login-deco"
part="login-collapse">
<monster-field-set
exportparts="
control:field-set-login-control,
header:field-set-login-header,
title:field-set-login-title,
container:field-set-login-container,
content:field-set-login-content,
collapse:field-set-login-collapse"
part="login-field-set">
<slot name="login-header"></slot>
<label part="login-label" data-monster-replace="path:labels.username"></label>
<input part="login-username"
data-monster-attributes="placeholder path:placeholder.username"
type="text" name="username" autofocus autocomplete="off">
<div data-monster-attributes="class path:classes.usernameInvalid"></div>
<label part="login-password-label" data-monster-replace="path:labels.password"></label>
<monster-password
data-monster-attributes="data-monster-option-placeholder path:placeholder.password"
exportparts="input-group:input-group-password,
control:input-group-password-control,
input:input-group-password-input" part="login-password"
data-monster-bind="path:password"></monster-password>
<div data-monster-attributes="class path:classes.passwordInvalid"></div>
<monster-message-state-button id="loginButton"
part="login-button"
data-monster-attributes="data-monster-option-classes-button path:classes.button"
exportparts="
control:login-button-control,
button:login-button-button,
popper:login-button-popper,
message:login-button-message,
button-button:login-button-button-button,
button-control:login-button-control"
data-monster-replace="path:labels.login"></monster-message-state-button>
<a href="#" data-monster-role="forgot-password-link" part="forgot-password-link"
data-monster-attributes="class path:features.forgotPassword | ?::hidden"
data-monster-replace="path:labels.forgotPasswordLink"></a>
<slot name="login-footer"></slot>
</monster-field-set>
</monster-collapse>
<monster-collapse data-monster-role="forgot-password-collapse"
exportparts="control:collapse-forgot-password-control,
container:collapse-forgot-password-container,
deco:collapse-forgot-password-deco"
part="forgot-password-collapse">
<monster-field-set part="forgot-password-field-set"
exportparts="
control:field-set-forgot-password-control,
header:field-set-forgot-password-header,
title:field-set-forgot-password-title,
container:field-set-forgot-password-container,
content:field-set-forgot-password-content,
collapse:field-set-forgot-password-collapse">
<slot name="forgot-password-header"></slot>
<label part="forgot-password-label" data-monster-replace="path:labels.mailAddress"></label>
<input type="email" name="email"
part="field-set-forgot-password-email"
data-monster-attributes="accesskey path:accessKeys.username,
placeholder path:placeholder.email"
autocomplete="off">
<div data-monster-attributes="class path:classes.emailInvalid"></div>
<monster-message-state-button id="requestLinkButton"
part="request-link-button"
data-monster-attributes="data-monster-option-classes-button path:classes.button"
exportparts="
control:forgot-password-button-control,
button:forgot-password-button-button,
popper:forgot-password-button-popper,
message:forgot-password-button-message,
button-button:forgot-password-button-button-button,
button-control:forgot-password-button-control"
data-monster-attributes="accesskey path:accessKeys.password"
data-monster-replace="path:labels.requestLink"></monster-message-state-button>
<a href="#"
data-monster-attributes="accesskey path:accessKeys.loginLink"
data-monster-role="login-link"
part="login-link"
data-monster-replace="path:labels.resetLoginProcess"></a>
<slot name="forgot-password-footer"></slot>
</monster-field-set>
</monster-collapse>
<monster-collapse data-monster-role="second-factor-collapse"
exportparts="control:collapse-second-factor-control,
container:collapse-second-factor-container,
deco:collapse-second-factor-deco"
part="second-factor-collapse">
<monster-field-set part="second-factor-field-set"
exportparts="
control:field-set-second-factor-control,
header:field-set-second-factor-header,
title:field-set-second-factor-title,
container:field-set-second-factor-container,
content:field-set-second-factor-content,
collapse:field-set-second-factor-collapse">
<slot name="second-factor-header"></slot>
<label part="second-factor-label" data-monster-replace="path:labels.secondFactor"></label>
<div>
<monster-digits data-monster-attributes="data-monster-option-digits path:digits"
part="second-factor-digits-control"
id="secondFactorControl"></monster-digits>
</div>
<!-- SECOND FACTOR COLLAPSE -->
<monster-message-state-button id="secondFactorButton"
part="secondFactor-digits-button"
data-monster-attributes="data-monster-option-classes-button path:classes.button"
exportparts="
control:second-factor-button-control,
button:second-factor-button-button,
popper:second-factor-button-popper,
message:second-factor-button-message,
button-button:second-factor-button-button-button,
button-control:second-factor-button-control"
data-monster-replace="path:labels.sendSecondFactorDigits"></monster-message-state-button>
<a href="#" data-monster-role="reset-login-process-link"
part="reset-login-process-link"
data-monster-replace="path:labels.resetLoginProcess"></a>
<slot name="second-factor-footer"></slot>
</monster-field-set>
</monster-collapse>
<monster-collapse data-monster-role="digits-collapse"
exportparts="control:collapse-digits-control,
container:collapse-digits-container,
deco:collapse-digits-deco"
part="digits-collapse">
<monster-field-set part="digits-field-set"
exportparts="
control:field-set-digits-control,
header:field-set-digits-header,
title:field-set-digits-title,
container:field-set-digits-container,
content:field-set-digits-content,
collapse:field-set-digits-collapse">
<slot name="digits-header"></slot>
<label part="digits-label" data-monster-replace="path:labels.digits"></label>
<div>
<monster-digits part="digits-control"
data-monster-attributes="data-monster-option-digits path:digits"
id="digitsControl"></monster-digits>
</div>
<monster-message-state-button id="digitsButton"
part="digits-button"
data-monster-attributes="data-monster-option-classes-button path:classes.button"
exportparts="
control:digits-button-control,
button:digits-button-button,
popper:digits-button-popper,
message:digits-button-message,
button-button:digits-button-button,
button-control:digits-button-control"
data-monster-replace="path:labels.sendDigits"></monster-message-state-button>
<a href="#" data-monster-role="reset-login-process-link"
part="reset-login-process-link"
data-monster-replace="path:labels.resetLoginProcess"></a>
<slot name="digits-footer"></slot>
</monster-field-set>
</monster-collapse>
<monster-collapse data-monster-role="logged-in-collapse"
exportparts="
control:collapse-success-control,
container:collapse-success-container,
deco:collapse-success-deco"
part="logged-in-collapse">
<div>
<slot name="logged-in-header"></slot>
<ul data-monster-insert="urls path:successUrls"></ul>
<slot name="logged-in-content"></slot>
<slot name="logged-in-footer"></slot>
</div>
</monster-collapse>
</div>`;
}
registerCustomElement(Login);