HeliosLauncher/app/assets/js/scripts/login.js
Daniel Scalzi 2f66d44824
Further organization of the browser scripts.
Moved server selection styles to launcher.css, as they are nearly finished.
Moved overlay convenience functions to overlay.js.
Moved launch area (landing.ejs) convenience functions to landing.js.
Various cleanups and documentation also added.
2018-04-25 20:11:10 -04:00

247 lines
7.6 KiB
JavaScript

/**
* 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')
// 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:<br>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:<br>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:<br>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:<br>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:<br>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 <a href="https://help.mojang.com/">Mojang\'s help portal</a>.'
}
}
}
}
}
if(err.message != null){
// Unknown error with request.
return {
title: 'Error During Login:<br>Unknown Error',
desc: err.message
}
} else {
// Unknown Mojang error.
return {
title: err.error,
desc: err.errorMessage
}
}
}
// Bind login button behavior.
loginButton.addEventListener('click', () => {
// Disable form.
formDisabled(true)
// Show loading stuff.
loginLoading(true)
AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => {
loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'SUCCESS')
$('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle()
//console.log(value)
setTimeout(() => {
$('#loginContainer').fadeOut(500, () => {
$('#landingContainer').fadeIn(500)
})
}, 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)
})
})