Skip to content
Snippets Groups Projects
Select Git revision
  • 11efd9861637da52c04f3f5d8c2d4236e32fdea8
  • master default protected
  • 1.31
  • 4.38.2
  • 4.38.1
  • 4.38.0
  • 4.37.2
  • 4.37.1
  • 4.37.0
  • 4.36.0
  • 4.35.0
  • 4.34.1
  • 4.34.0
  • 4.33.1
  • 4.33.0
  • 4.32.2
  • 4.32.1
  • 4.32.0
  • 4.31.0
  • 4.30.1
  • 4.30.0
  • 4.29.1
  • 4.29.0
23 results

tutorial-dom-form-handling.html

Blame
  • 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);