/** * Script for login.ejs */ // Validation Regexes. const validUsername = /^[a-zA-Z0-9_]{1,16}$/ const basicEmail = /^\S+@\S+\.\S+$/ //const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i // Login Elements const loginEmailError = document.getElementById('loginEmailError') const loginUsername = document.getElementById('loginUsername') const loginPasswordError = document.getElementById('loginPasswordError') const loginPassword = document.getElementById('loginPassword') const checkmarkContainer = document.getElementById('checkmarkContainer') const loginRememberOption = document.getElementById('loginRememberOption') const loginButton = document.getElementById('loginButton') const loginForm = document.getElementById('loginForm') // Control variables. let lu = false, lp = false /** * Show a login error. * * @param {HTMLElement} element The element on which to display the error. * @param {string} value The error text. */ function showError(element, value){ element.innerHTML = value element.style.opacity = 1 } /** * Shake a login error to add emphasis. * * @param {HTMLElement} element The element to shake. */ function shakeError(element){ if(element.style.opacity == 1){ element.classList.remove('shake') void element.offsetWidth element.classList.add('shake') } } /** * Validate that an email field is neither empty nor invalid. * * @param {string} value The email value. */ function validateEmail(value){ if(value){ if(!basicEmail.test(value) && !validUsername.test(value)){ showError(loginEmailError, '* Invalid Value') loginDisabled(true) lu = false } else { loginEmailError.style.opacity = 0 lu = true if(lp){ loginDisabled(false) } } } else { lu = false showError(loginEmailError, '* Required') loginDisabled(true) } } /** * Validate that the password field is not empty. * * @param {string} value The password value. */ function validatePassword(value){ if(value){ loginPasswordError.style.opacity = 0 lp = true if(lu){ loginDisabled(false) } } else { lp = false showError(loginPasswordError, '* Required') loginDisabled(true) } } // Emphasize errors with shake when focus is lost. loginUsername.addEventListener('focusout', (e) => { validateEmail(e.target.value) shakeError(loginEmailError) }) loginPassword.addEventListener('focusout', (e) => { validatePassword(e.target.value) shakeError(loginPasswordError) }) // Validate input for each field. loginUsername.addEventListener('input', (e) => { validateEmail(e.target.value) }) loginPassword.addEventListener('input', (e) => { validatePassword(e.target.value) }) /** * Enable or disable the login button. * * @param {boolean} v True to enable, false to disable. */ function loginDisabled(v){ if(loginButton.disabled !== v){ loginButton.disabled = v } } /** * Enable or disable loading elements. * * @param {boolean} v True to enable, false to disable. */ function loginLoading(v){ if(v){ loginButton.setAttribute('loading', v) loginButton.innerHTML = loginButton.innerHTML.replace('LOGIN', 'LOGGING IN') } else { loginButton.removeAttribute('loading') loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'LOGIN') } } /** * Enable or disable login form. * * @param {boolean} v True to enable, false to disable. */ function formDisabled(v){ loginDisabled(v) loginUsername.disabled = v loginPassword.disabled = v if(v){ checkmarkContainer.setAttribute('disabled', v) } else { checkmarkContainer.removeAttribute('disabled') } loginRememberOption.disabled = v } /** * Parses an error and returns a user-friendly title and description * for our error overlay. * * @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js * error or Mojang error response. */ function resolveError(err){ // Mojang Response => err.cause | err.error | err.errorMessage // Node error => err.code | err.message if(err.cause != null && err.cause === 'UserMigratedException') { return { title: 'Error During Login:
Invalid Credentials', desc: 'You\'ve attempted to login with a migrated account. Try again using the account email as the username.' } } else { if(err.error != null){ if(err.error === 'ForbiddenOperationException'){ if(err.errorMessage != null){ if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){ return { title: 'Error During Login:
Invalid Credentials', desc: 'The email or password you\'ve entered is incorrect. Please try again.' } } else if(err.errorMessage === 'Invalid credentials.'){ return { title: 'Error During Login:
Too Many Attempts', desc: 'There have been too many login attempts with this account recently. Please try again later.' } } } } } else { // Request errors (from Node). if(err.code != null){ if(err.code === 'ENOENT'){ // No Internet. return { title: 'Error During Login:
No Internet Connection', desc: 'You must be connected to the internet in order to login. Please connect and try again.' } } else if(err.code === 'ENOTFOUND'){ // Could not reach server. return { title: 'Error During Login:
Authentication Server Offline', desc: 'Mojang\'s authentication server is currently offline or unreachable. Please wait a bit and try again. You can check the status of the server on Mojang\'s help portal.' } } } } } if(err.message != null){ // Unknown error with request. return { title: 'Error During Login:
Unknown Error', desc: err.message } } else { // Unknown Mojang error. return { title: err.error, desc: err.errorMessage } } } // Disable default form behavior. loginForm.onsubmit = () => { return false } // Bind login button behavior. loginButton.addEventListener('click', () => { // Disable form. formDisabled(true) // Show loading stuff. loginLoading(true) AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => { updateSelectedAccount(value) loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'SUCCESS') $('.circle-loader').toggleClass('load-complete') $('.checkmark').toggle() //console.log(value) setTimeout(() => { switchView(VIEWS.login, VIEWS.landing, 500, 500, () => { loginUsername.value = '' loginPassword.value = '' loginLoading(false) loginButton.innerHTML = loginButton.innerHTML.replace('SUCCESS', 'LOGIN') formDisabled(false) }) }, 1000) }).catch((err) => { loginLoading(false) const errF = resolveError(err) setOverlayContent(errF.title, errF.desc, 'Try Again') setOverlayHandler(() => { formDisabled(false) toggleOverlay(false) }) toggleOverlay(true) console.log(err) }) })