Add files via upload

This commit is contained in:
MastermDEV 2020-06-18 18:19:52 -07:00 committed by GitHub
parent 6c0e851f2b
commit bf8ecb8f8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 3742 additions and 3742 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,300 +1,300 @@
/** /**
* Script for login.ejs * Script for login.ejs
*/ */
// Validation Regexes. // Validation Regexes.
const validUsername = /^[a-zA-Z0-9_]{1,16}$/ const validUsername = /^[a-zA-Z0-9_]{1,16}$/
const basicEmail = /^\S+@\S+\.\S+$/ const basicEmail = /^\S+@\S+\.\S+$/
//const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i //const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
// Login Elements // Login Elements
const loginCancelContainer = document.getElementById('loginCancelContainer') const loginCancelContainer = document.getElementById('loginCancelContainer')
const loginCancelButton = document.getElementById('loginCancelButton') const loginCancelButton = document.getElementById('loginCancelButton')
const loginEmailError = document.getElementById('loginEmailError') const loginEmailError = document.getElementById('loginEmailError')
const loginUsername = document.getElementById('loginUsername') const loginUsername = document.getElementById('loginUsername')
const loginPasswordError = document.getElementById('loginPasswordError') const loginPasswordError = document.getElementById('loginPasswordError')
const loginPassword = document.getElementById('loginPassword') const loginPassword = document.getElementById('loginPassword')
const checkmarkContainer = document.getElementById('checkmarkContainer') const checkmarkContainer = document.getElementById('checkmarkContainer')
const loginRememberOption = document.getElementById('loginRememberOption') const loginRememberOption = document.getElementById('loginRememberOption')
const loginButton = document.getElementById('loginButton') const loginButton = document.getElementById('loginButton')
const loginForm = document.getElementById('loginForm') const loginForm = document.getElementById('loginForm')
// Control variables. // Control variables.
let lu = false, lp = false let lu = false, lp = false
const loggerLogin = LoggerUtil('%c[Login]', 'color: #000668; font-weight: bold') const loggerLogin = LoggerUtil('%c[Login]', 'color: #000668; font-weight: bold')
/** /**
* Show a login error. * Show a login error.
* *
* @param {HTMLElement} element The element on which to display the error. * @param {HTMLElement} element The element on which to display the error.
* @param {string} value The error text. * @param {string} value The error text.
*/ */
function showError(element, value){ function showError(element, value){
element.innerHTML = value element.innerHTML = value
element.style.opacity = 1 element.style.opacity = 1
} }
/** /**
* Shake a login error to add emphasis. * Shake a login error to add emphasis.
* *
* @param {HTMLElement} element The element to shake. * @param {HTMLElement} element The element to shake.
*/ */
function shakeError(element){ function shakeError(element){
if(element.style.opacity == 1){ if(element.style.opacity == 1){
element.classList.remove('shake') element.classList.remove('shake')
void element.offsetWidth void element.offsetWidth
element.classList.add('shake') element.classList.add('shake')
} }
} }
/** /**
* Validate that an email field is neither empty nor invalid. * Validate that an email field is neither empty nor invalid.
* *
* @param {string} value The email value. * @param {string} value The email value.
*/ */
function validateEmail(value){ function validateEmail(value){
if(value){ if(value){
if(!basicEmail.test(value) && !validUsername.test(value)){ if(!basicEmail.test(value) && !validUsername.test(value)){
showError(loginEmailError, Lang.queryJS('login.error.invalidValue')) showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true) loginDisabled(true)
lu = false lu = false
} else { } else {
loginEmailError.style.opacity = 0 loginEmailError.style.opacity = 0
lu = true lu = true
if(lp){ if(lp){
loginDisabled(false) loginDisabled(false)
} }
} }
} else { } else {
lu = false lu = false
showError(loginEmailError, Lang.queryJS('login.error.requiredValue')) showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
loginDisabled(true) loginDisabled(true)
} }
} }
/** /**
* Validate that the password field is not empty. * Validate that the password field is not empty.
* *
* @param {string} value The password value. * @param {string} value The password value.
*/ */
function validatePassword(value){ function validatePassword(value){
if(value){ if(value){
loginPasswordError.style.opacity = 0 loginPasswordError.style.opacity = 0
lp = true lp = true
if(lu){ if(lu){
loginDisabled(false) loginDisabled(false)
} }
} else { } else {
lp = false lp = false
showError(loginPasswordError, Lang.queryJS('login.error.invalidValue')) showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true) loginDisabled(true)
} }
} }
// Emphasize errors with shake when focus is lost. // Emphasize errors with shake when focus is lost.
loginUsername.addEventListener('focusout', (e) => { loginUsername.addEventListener('focusout', (e) => {
validateEmail(e.target.value) validateEmail(e.target.value)
shakeError(loginEmailError) shakeError(loginEmailError)
}) })
loginPassword.addEventListener('focusout', (e) => { loginPassword.addEventListener('focusout', (e) => {
validatePassword(e.target.value) validatePassword(e.target.value)
shakeError(loginPasswordError) shakeError(loginPasswordError)
}) })
// Validate input for each field. // Validate input for each field.
loginUsername.addEventListener('input', (e) => { loginUsername.addEventListener('input', (e) => {
validateEmail(e.target.value) validateEmail(e.target.value)
}) })
loginPassword.addEventListener('input', (e) => { loginPassword.addEventListener('input', (e) => {
validatePassword(e.target.value) validatePassword(e.target.value)
}) })
/** /**
* Enable or disable the login button. * Enable or disable the login button.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function loginDisabled(v){ function loginDisabled(v){
if(loginButton.disabled !== v){ if(loginButton.disabled !== v){
loginButton.disabled = v loginButton.disabled = v
} }
} }
/** /**
* Enable or disable loading elements. * Enable or disable loading elements.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function loginLoading(v){ function loginLoading(v){
if(v){ if(v){
loginButton.setAttribute('loading', v) loginButton.setAttribute('loading', v)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
} else { } else {
loginButton.removeAttribute('loading') loginButton.removeAttribute('loading')
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
} }
} }
/** /**
* Enable or disable login form. * Enable or disable login form.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function formDisabled(v){ function formDisabled(v){
loginDisabled(v) loginDisabled(v)
loginCancelButton.disabled = v loginCancelButton.disabled = v
loginUsername.disabled = v loginUsername.disabled = v
loginPassword.disabled = v loginPassword.disabled = v
if(v){ if(v){
checkmarkContainer.setAttribute('disabled', v) checkmarkContainer.setAttribute('disabled', v)
} else { } else {
checkmarkContainer.removeAttribute('disabled') checkmarkContainer.removeAttribute('disabled')
} }
loginRememberOption.disabled = v loginRememberOption.disabled = v
} }
/** /**
* Parses an error and returns a user-friendly title and description * Parses an error and returns a user-friendly title and description
* for our error overlay. * for our error overlay.
* *
* @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js * @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js
* error or Mojang error response. * error or Mojang error response.
*/ */
function resolveError(err){ function resolveError(err){
// Mojang Response => err.cause | err.error | err.errorMessage // Mojang Response => err.cause | err.error | err.errorMessage
// Node error => err.code | err.message // Node error => err.code | err.message
if(err.cause != null && err.cause === 'UserMigratedException') { if(err.cause != null && err.cause === 'UserMigratedException') {
return { return {
title: Lang.queryJS('login.error.userMigrated.title'), title: Lang.queryJS('login.error.userMigrated.title'),
desc: Lang.queryJS('login.error.userMigrated.desc') desc: Lang.queryJS('login.error.userMigrated.desc')
} }
} else { } else {
if(err.error != null){ if(err.error != null){
if(err.error === 'ForbiddenOperationException'){ if(err.error === 'ForbiddenOperationException'){
if(err.errorMessage != null){ if(err.errorMessage != null){
if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){ if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){
return { return {
title: Lang.queryJS('login.error.invalidCredentials.title'), title: Lang.queryJS('login.error.invalidCredentials.title'),
desc: Lang.queryJS('login.error.invalidCredentials.desc') desc: Lang.queryJS('login.error.invalidCredentials.desc')
} }
} else if(err.errorMessage === 'Invalid credentials.'){ } else if(err.errorMessage === 'Invalid credentials.'){
return { return {
title: Lang.queryJS('login.error.rateLimit.title'), title: Lang.queryJS('login.error.rateLimit.title'),
desc: Lang.queryJS('login.error.rateLimit.desc') desc: Lang.queryJS('login.error.rateLimit.desc')
} }
} }
} }
} }
} else { } else {
// Request errors (from Node). // Request errors (from Node).
if(err.code != null){ if(err.code != null){
if(err.code === 'ENOENT'){ if(err.code === 'ENOENT'){
// No Internet. // No Internet.
return { return {
title: Lang.queryJS('login.error.noInternet.title'), title: Lang.queryJS('login.error.noInternet.title'),
desc: Lang.queryJS('login.error.noInternet.desc') desc: Lang.queryJS('login.error.noInternet.desc')
} }
} else if(err.code === 'ENOTFOUND'){ } else if(err.code === 'ENOTFOUND'){
// Could not reach server. // Could not reach server.
return { return {
title: Lang.queryJS('login.error.authDown.title'), title: Lang.queryJS('login.error.authDown.title'),
desc: Lang.queryJS('login.error.authDown.desc') desc: Lang.queryJS('login.error.authDown.desc')
} }
} }
} }
} }
} }
if(err.message != null){ if(err.message != null){
if(err.message === 'NotPaidAccount'){ if(err.message === 'NotPaidAccount'){
return { return {
title: Lang.queryJS('login.error.notPaid.title'), title: Lang.queryJS('login.error.notPaid.title'),
desc: Lang.queryJS('login.error.notPaid.desc') desc: Lang.queryJS('login.error.notPaid.desc')
} }
} else { } else {
// Unknown error with request. // Unknown error with request.
return { return {
title: Lang.queryJS('login.error.unknown.title'), title: Lang.queryJS('login.error.unknown.title'),
desc: err.message desc: err.message
} }
} }
} else { } else {
// Unknown Mojang error. // Unknown Mojang error.
return { return {
title: err.error, title: err.error,
desc: err.errorMessage desc: err.errorMessage
} }
} }
} }
let loginViewOnSuccess = VIEWS.landing let loginViewOnSuccess = VIEWS.landing
let loginViewOnCancel = VIEWS.settings let loginViewOnCancel = VIEWS.settings
let loginViewCancelHandler let loginViewCancelHandler
function loginCancelEnabled(val){ function loginCancelEnabled(val){
if(val){ if(val){
$(loginCancelContainer).show() $(loginCancelContainer).show()
} else { } else {
$(loginCancelContainer).hide() $(loginCancelContainer).hide()
} }
} }
loginCancelButton.onclick = (e) => { loginCancelButton.onclick = (e) => {
switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => { switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
loginUsername.value = '' loginUsername.value = ''
loginPassword.value = '' loginPassword.value = ''
loginCancelEnabled(false) loginCancelEnabled(false)
if(loginViewCancelHandler != null){ if(loginViewCancelHandler != null){
loginViewCancelHandler() loginViewCancelHandler()
loginViewCancelHandler = null loginViewCancelHandler = null
} }
}) })
} }
// Disable default form behavior. // Disable default form behavior.
loginForm.onsubmit = () => { return false } loginForm.onsubmit = () => { return false }
// Bind login button behavior. // Bind login button behavior.
loginButton.addEventListener('click', () => { loginButton.addEventListener('click', () => {
// Disable form. // Disable form.
formDisabled(true) formDisabled(true)
// Show loading stuff. // Show loading stuff.
loginLoading(true) loginLoading(true)
AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => { AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => {
updateSelectedAccount(value) updateSelectedAccount(value)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
$('.circle-loader').toggleClass('load-complete') $('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle() $('.checkmark').toggle()
setTimeout(() => { setTimeout(() => {
switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => { switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => {
// Temporary workaround // Temporary workaround
if(loginViewOnSuccess === VIEWS.settings){ if(loginViewOnSuccess === VIEWS.settings){
prepareSettings() prepareSettings()
} }
loginViewOnSuccess = VIEWS.landing // Reset this for good measure. loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
loginCancelEnabled(false) // Reset this for good measure. loginCancelEnabled(false) // Reset this for good measure.
loginViewCancelHandler = null // Reset this for good measure. loginViewCancelHandler = null // Reset this for good measure.
loginUsername.value = '' loginUsername.value = ''
loginPassword.value = '' loginPassword.value = ''
$('.circle-loader').toggleClass('load-complete') $('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle() $('.checkmark').toggle()
loginLoading(false) loginLoading(false)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
formDisabled(false) formDisabled(false)
}) })
}, 1000) }, 1000)
}).catch((err) => { }).catch((err) => {
loginLoading(false) loginLoading(false)
const errF = resolveError(err) const errF = resolveError(err)
setOverlayContent(errF.title, errF.desc, Lang.queryJS('login.tryAgain')) setOverlayContent(errF.title, errF.desc, Lang.queryJS('login.tryAgain'))
setOverlayHandler(() => { setOverlayHandler(() => {
formDisabled(false) formDisabled(false)
toggleOverlay(false) toggleOverlay(false)
}) })
toggleOverlay(true) toggleOverlay(true)
loggerLogin.log('Error while logging in.', err) loggerLogin.log('Error while logging in.', err)
}) })
}) })

View File

@ -1,318 +1,318 @@
/** /**
* Script for overlay.ejs * Script for overlay.ejs
*/ */
/* Overlay Wrapper Functions */ /* Overlay Wrapper Functions */
/** /**
* Check to see if the overlay is visible. * Check to see if the overlay is visible.
* *
* @returns {boolean} Whether or not the overlay is visible. * @returns {boolean} Whether or not the overlay is visible.
*/ */
function isOverlayVisible(){ function isOverlayVisible(){
return document.getElementById('main').hasAttribute('overlay') return document.getElementById('main').hasAttribute('overlay')
} }
let overlayHandlerContent let overlayHandlerContent
/** /**
* Overlay keydown handler for a non-dismissable overlay. * Overlay keydown handler for a non-dismissable overlay.
* *
* @param {KeyboardEvent} e The keydown event. * @param {KeyboardEvent} e The keydown event.
*/ */
function overlayKeyHandler (e){ function overlayKeyHandler (e){
if(e.key === 'Enter' || e.key === 'Escape'){ if(e.key === 'Enter' || e.key === 'Escape'){
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click() document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click()
} }
} }
/** /**
* Overlay keydown handler for a dismissable overlay. * Overlay keydown handler for a dismissable overlay.
* *
* @param {KeyboardEvent} e The keydown event. * @param {KeyboardEvent} e The keydown event.
*/ */
function overlayKeyDismissableHandler (e){ function overlayKeyDismissableHandler (e){
if(e.key === 'Enter'){ if(e.key === 'Enter'){
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click() document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click()
} else if(e.key === 'Escape'){ } else if(e.key === 'Escape'){
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEsc')[0].click() document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEsc')[0].click()
} }
} }
/** /**
* Bind overlay keydown listeners for escape and exit. * Bind overlay keydown listeners for escape and exit.
* *
* @param {boolean} state Whether or not to add new event listeners. * @param {boolean} state Whether or not to add new event listeners.
* @param {string} content The overlay content which will be shown. * @param {string} content The overlay content which will be shown.
* @param {boolean} dismissable Whether or not the overlay is dismissable * @param {boolean} dismissable Whether or not the overlay is dismissable
*/ */
function bindOverlayKeys(state, content, dismissable){ function bindOverlayKeys(state, content, dismissable){
overlayHandlerContent = content overlayHandlerContent = content
document.removeEventListener('keydown', overlayKeyHandler) document.removeEventListener('keydown', overlayKeyHandler)
document.removeEventListener('keydown', overlayKeyDismissableHandler) document.removeEventListener('keydown', overlayKeyDismissableHandler)
if(state){ if(state){
if(dismissable){ if(dismissable){
document.addEventListener('keydown', overlayKeyDismissableHandler) document.addEventListener('keydown', overlayKeyDismissableHandler)
} else { } else {
document.addEventListener('keydown', overlayKeyHandler) document.addEventListener('keydown', overlayKeyHandler)
} }
} }
} }
/** /**
* Toggle the visibility of the overlay. * Toggle the visibility of the overlay.
* *
* @param {boolean} toggleState True to display, false to hide. * @param {boolean} toggleState True to display, false to hide.
* @param {boolean} dismissable Optional. True to show the dismiss option, otherwise false. * @param {boolean} dismissable Optional. True to show the dismiss option, otherwise false.
* @param {string} content Optional. The content div to be shown. * @param {string} content Optional. The content div to be shown.
*/ */
function toggleOverlay(toggleState, dismissable = false, content = 'overlayContent'){ function toggleOverlay(toggleState, dismissable = false, content = 'overlayContent'){
if(toggleState == null){ if(toggleState == null){
toggleState = !document.getElementById('main').hasAttribute('overlay') toggleState = !document.getElementById('main').hasAttribute('overlay')
} }
if(typeof dismissable === 'string'){ if(typeof dismissable === 'string'){
content = dismissable content = dismissable
dismissable = false dismissable = false
} }
bindOverlayKeys(toggleState, content, dismissable) bindOverlayKeys(toggleState, content, dismissable)
if(toggleState){ if(toggleState){
document.getElementById('main').setAttribute('overlay', true) document.getElementById('main').setAttribute('overlay', true)
// Make things untabbable. // Make things untabbable.
$('#main *').attr('tabindex', '-1') $('#main *').attr('tabindex', '-1')
$('#' + content).parent().children().hide() $('#' + content).parent().children().hide()
$('#' + content).show() $('#' + content).show()
if(dismissable){ if(dismissable){
$('#overlayDismiss').show() $('#overlayDismiss').show()
} else { } else {
$('#overlayDismiss').hide() $('#overlayDismiss').hide()
} }
$('#overlayContainer').fadeIn({ $('#overlayContainer').fadeIn({
duration: 250, duration: 250,
start: () => { start: () => {
if(getCurrentView() === VIEWS.settings){ if(getCurrentView() === VIEWS.settings){
document.getElementById('settingsContainer').style.backgroundColor = 'transparent' document.getElementById('settingsContainer').style.backgroundColor = 'transparent'
} }
} }
}) })
} else { } else {
document.getElementById('main').removeAttribute('overlay') document.getElementById('main').removeAttribute('overlay')
// Make things tabbable. // Make things tabbable.
$('#main *').removeAttr('tabindex') $('#main *').removeAttr('tabindex')
$('#overlayContainer').fadeOut({ $('#overlayContainer').fadeOut({
duration: 250, duration: 250,
start: () => { start: () => {
if(getCurrentView() === VIEWS.settings){ if(getCurrentView() === VIEWS.settings){
document.getElementById('settingsContainer').style.backgroundColor = 'rgba(0, 0, 0, 0.50)' document.getElementById('settingsContainer').style.backgroundColor = 'rgba(0, 0, 0, 0.50)'
} }
}, },
complete: () => { complete: () => {
$('#' + content).parent().children().hide() $('#' + content).parent().children().hide()
$('#' + content).show() $('#' + content).show()
if(dismissable){ if(dismissable){
$('#overlayDismiss').show() $('#overlayDismiss').show()
} else { } else {
$('#overlayDismiss').hide() $('#overlayDismiss').hide()
} }
} }
}) })
} }
} }
function toggleServerSelection(toggleState){ function toggleServerSelection(toggleState){
prepareServerSelectionList() prepareServerSelectionList()
toggleOverlay(toggleState, true, 'serverSelectContent') toggleOverlay(toggleState, true, 'serverSelectContent')
} }
/** /**
* Set the content of the overlay. * Set the content of the overlay.
* *
* @param {string} title Overlay title text. * @param {string} title Overlay title text.
* @param {string} description Overlay description text. * @param {string} description Overlay description text.
* @param {string} acknowledge Acknowledge button text. * @param {string} acknowledge Acknowledge button text.
* @param {string} dismiss Dismiss button text. * @param {string} dismiss Dismiss button text.
*/ */
function setOverlayContent(title, description, acknowledge, dismiss = 'Dismiss'){ function setOverlayContent(title, description, acknowledge, dismiss = 'Dismiss'){
document.getElementById('overlayTitle').innerHTML = title document.getElementById('overlayTitle').innerHTML = title
document.getElementById('overlayDesc').innerHTML = description document.getElementById('overlayDesc').innerHTML = description
document.getElementById('overlayAcknowledge').innerHTML = acknowledge document.getElementById('overlayAcknowledge').innerHTML = acknowledge
document.getElementById('overlayDismiss').innerHTML = dismiss document.getElementById('overlayDismiss').innerHTML = dismiss
} }
/** /**
* Set the onclick handler of the overlay acknowledge button. * Set the onclick handler of the overlay acknowledge button.
* If the handler is null, a default handler will be added. * If the handler is null, a default handler will be added.
* *
* @param {function} handler * @param {function} handler
*/ */
function setOverlayHandler(handler){ function setOverlayHandler(handler){
if(handler == null){ if(handler == null){
document.getElementById('overlayAcknowledge').onclick = () => { document.getElementById('overlayAcknowledge').onclick = () => {
toggleOverlay(false) toggleOverlay(false)
} }
} else { } else {
document.getElementById('overlayAcknowledge').onclick = handler document.getElementById('overlayAcknowledge').onclick = handler
} }
} }
/** /**
* Set the onclick handler of the overlay dismiss button. * Set the onclick handler of the overlay dismiss button.
* If the handler is null, a default handler will be added. * If the handler is null, a default handler will be added.
* *
* @param {function} handler * @param {function} handler
*/ */
function setDismissHandler(handler){ function setDismissHandler(handler){
if(handler == null){ if(handler == null){
document.getElementById('overlayDismiss').onclick = () => { document.getElementById('overlayDismiss').onclick = () => {
toggleOverlay(false) toggleOverlay(false)
} }
} else { } else {
document.getElementById('overlayDismiss').onclick = handler document.getElementById('overlayDismiss').onclick = handler
} }
} }
/* Server Select View */ /* Server Select View */
document.getElementById('serverSelectConfirm').addEventListener('click', () => { document.getElementById('serverSelectConfirm').addEventListener('click', () => {
const listings = document.getElementsByClassName('serverListing') const listings = document.getElementsByClassName('serverListing')
for(let i=0; i<listings.length; i++){ for(let i=0; i<listings.length; i++){
if(listings[i].hasAttribute('selected')){ if(listings[i].hasAttribute('selected')){
const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid')) const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid'))
updateSelectedServer(serv) updateSelectedServer(serv)
refreshServerStatus(true) refreshServerStatus(true)
toggleOverlay(false) toggleOverlay(false)
return return
} }
} }
// None are selected? Not possible right? Meh, handle it. // None are selected? Not possible right? Meh, handle it.
if(listings.length > 0){ if(listings.length > 0){
const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid')) const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid'))
updateSelectedServer(serv) updateSelectedServer(serv)
toggleOverlay(false) toggleOverlay(false)
} }
}) })
document.getElementById('accountSelectConfirm').addEventListener('click', () => { document.getElementById('accountSelectConfirm').addEventListener('click', () => {
const listings = document.getElementsByClassName('accountListing') const listings = document.getElementsByClassName('accountListing')
for(let i=0; i<listings.length; i++){ for(let i=0; i<listings.length; i++){
if(listings[i].hasAttribute('selected')){ if(listings[i].hasAttribute('selected')){
const authAcc = ConfigManager.setSelectedAccount(listings[i].getAttribute('uuid')) const authAcc = ConfigManager.setSelectedAccount(listings[i].getAttribute('uuid'))
ConfigManager.save() ConfigManager.save()
updateSelectedAccount(authAcc) updateSelectedAccount(authAcc)
toggleOverlay(false) toggleOverlay(false)
validateSelectedAccount() validateSelectedAccount()
return return
} }
} }
// None are selected? Not possible right? Meh, handle it. // None are selected? Not possible right? Meh, handle it.
if(listings.length > 0){ if(listings.length > 0){
const authAcc = ConfigManager.setSelectedAccount(listings[0].getAttribute('uuid')) const authAcc = ConfigManager.setSelectedAccount(listings[0].getAttribute('uuid'))
ConfigManager.save() ConfigManager.save()
updateSelectedAccount(authAcc) updateSelectedAccount(authAcc)
toggleOverlay(false) toggleOverlay(false)
validateSelectedAccount() validateSelectedAccount()
} }
}) })
// Bind server select cancel button. // Bind server select cancel button.
document.getElementById('serverSelectCancel').addEventListener('click', () => { document.getElementById('serverSelectCancel').addEventListener('click', () => {
toggleOverlay(false) toggleOverlay(false)
}) })
document.getElementById('accountSelectCancel').addEventListener('click', () => { document.getElementById('accountSelectCancel').addEventListener('click', () => {
$('#accountSelectContent').fadeOut(250, () => { $('#accountSelectContent').fadeOut(250, () => {
$('#overlayContent').fadeIn(250) $('#overlayContent').fadeIn(250)
}) })
}) })
function setServerListingHandlers(){ function setServerListingHandlers(){
const listings = Array.from(document.getElementsByClassName('serverListing')) const listings = Array.from(document.getElementsByClassName('serverListing'))
listings.map((val) => { listings.map((val) => {
val.onclick = e => { val.onclick = e => {
if(val.hasAttribute('selected')){ if(val.hasAttribute('selected')){
return return
} }
const cListings = document.getElementsByClassName('serverListing') const cListings = document.getElementsByClassName('serverListing')
for(let i=0; i<cListings.length; i++){ for(let i=0; i<cListings.length; i++){
if(cListings[i].hasAttribute('selected')){ if(cListings[i].hasAttribute('selected')){
cListings[i].removeAttribute('selected') cListings[i].removeAttribute('selected')
} }
} }
val.setAttribute('selected', '') val.setAttribute('selected', '')
document.activeElement.blur() document.activeElement.blur()
} }
}) })
} }
function setAccountListingHandlers(){ function setAccountListingHandlers(){
const listings = Array.from(document.getElementsByClassName('accountListing')) const listings = Array.from(document.getElementsByClassName('accountListing'))
listings.map((val) => { listings.map((val) => {
val.onclick = e => { val.onclick = e => {
if(val.hasAttribute('selected')){ if(val.hasAttribute('selected')){
return return
} }
const cListings = document.getElementsByClassName('accountListing') const cListings = document.getElementsByClassName('accountListing')
for(let i=0; i<cListings.length; i++){ for(let i=0; i<cListings.length; i++){
if(cListings[i].hasAttribute('selected')){ if(cListings[i].hasAttribute('selected')){
cListings[i].removeAttribute('selected') cListings[i].removeAttribute('selected')
} }
} }
val.setAttribute('selected', '') val.setAttribute('selected', '')
document.activeElement.blur() document.activeElement.blur()
} }
}) })
} }
function populateServerListings(){ function populateServerListings(){
const distro = DistroManager.getDistribution() const distro = DistroManager.getDistribution()
const giaSel = ConfigManager.getSelectedServer() const giaSel = ConfigManager.getSelectedServer()
const servers = distro.getServers() const servers = distro.getServers()
let htmlString = '' let htmlString = ''
for(const serv of servers){ for(const serv of servers){
htmlString += `<button class="serverListing" servid="${serv.getID()}" ${serv.getID() === giaSel ? 'selected' : ''}> htmlString += `<button class="serverListing" servid="${serv.getID()}" ${serv.getID() === giaSel ? 'selected' : ''}>
<img class="serverListingImg" src="${serv.getIcon()}"/> <img class="serverListingImg" src="${serv.getIcon()}"/>
<div class="serverListingDetails"> <div class="serverListingDetails">
<span class="serverListingName">${serv.getName()}</span> <span class="serverListingName">${serv.getName()}</span>
<span class="serverListingDescription">${serv.getDescription()}</span> <span class="serverListingDescription">${serv.getDescription()}</span>
<div class="serverListingInfo"> <div class="serverListingInfo">
<div class="serverListingVersion">${serv.getMinecraftVersion()}</div> <div class="serverListingVersion">${serv.getMinecraftVersion()}</div>
<div class="serverListingRevision">${serv.getVersion()}</div> <div class="serverListingRevision">${serv.getVersion()}</div>
${serv.isMainServer() ? `<div class="serverListingStarWrapper"> ${serv.isMainServer() ? `<div class="serverListingStarWrapper">
<svg id="Layer_1" viewBox="0 0 107.45 104.74" width="20px" height="20px"> <svg id="Layer_1" viewBox="0 0 107.45 104.74" width="20px" height="20px">
<defs> <defs>
<style>.cls-1{fill:#fff;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style> <style>.cls-1{fill:#fff;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style>
</defs> </defs>
<path class="cls-1" d="M100.93,65.54C89,62,68.18,55.65,63.54,52.13c2.7-5.23,18.8-19.2,28-27.55C81.36,31.74,63.74,43.87,58.09,45.3c-2.41-5.37-3.61-26.52-4.37-39-.77,12.46-2,33.64-4.36,39-5.7-1.46-23.3-13.57-33.49-20.72,9.26,8.37,25.39,22.36,28,27.55C39.21,55.68,18.47,62,6.52,65.55c12.32-2,33.63-6.06,39.34-4.9-.16,5.87-8.41,26.16-13.11,37.69,6.1-10.89,16.52-30.16,21-33.9,4.5,3.79,14.93,23.09,21,34C70,86.84,61.73,66.48,61.59,60.65,67.36,59.49,88.64,63.52,100.93,65.54Z"/> <path class="cls-1" d="M100.93,65.54C89,62,68.18,55.65,63.54,52.13c2.7-5.23,18.8-19.2,28-27.55C81.36,31.74,63.74,43.87,58.09,45.3c-2.41-5.37-3.61-26.52-4.37-39-.77,12.46-2,33.64-4.36,39-5.7-1.46-23.3-13.57-33.49-20.72,9.26,8.37,25.39,22.36,28,27.55C39.21,55.68,18.47,62,6.52,65.55c12.32-2,33.63-6.06,39.34-4.9-.16,5.87-8.41,26.16-13.11,37.69,6.1-10.89,16.52-30.16,21-33.9,4.5,3.79,14.93,23.09,21,34C70,86.84,61.73,66.48,61.59,60.65,67.36,59.49,88.64,63.52,100.93,65.54Z"/>
<circle class="cls-2" cx="53.73" cy="53.9" r="38"/> <circle class="cls-2" cx="53.73" cy="53.9" r="38"/>
</svg> </svg>
<span class="serverListingStarTooltip">Main Server</span> <span class="serverListingStarTooltip">Main Server</span>
</div>` : ''} </div>` : ''}
</div> </div>
</div> </div>
</button>` </button>`
} }
document.getElementById('serverSelectListScrollable').innerHTML = htmlString document.getElementById('serverSelectListScrollable').innerHTML = htmlString
} }
function populateAccountListings(){ function populateAccountListings(){
const accountsObj = ConfigManager.getAuthAccounts() const accountsObj = ConfigManager.getAuthAccounts()
const accounts = Array.from(Object.keys(accountsObj), v=>accountsObj[v]) const accounts = Array.from(Object.keys(accountsObj), v=>accountsObj[v])
let htmlString = '' let htmlString = ''
for(let i=0; i<accounts.length; i++){ for(let i=0; i<accounts.length; i++){
htmlString += `<button class="accountListing" uuid="${accounts[i].uuid}" ${i===0 ? 'selected' : ''}> htmlString += `<button class="accountListing" uuid="${accounts[i].uuid}" ${i===0 ? 'selected' : ''}>
<img src="https://crafatar.com/renders/head/${accounts[i].uuid}?scale=2&default=MHF_Steve&overlay"> <img src="https://crafatar.com/renders/head/${accounts[i].uuid}?scale=2&default=MHF_Steve&overlay">
<div class="accountListingName">${accounts[i].displayName}</div> <div class="accountListingName">${accounts[i].displayName}</div>
</button>` </button>`
} }
document.getElementById('accountSelectListScrollable').innerHTML = htmlString document.getElementById('accountSelectListScrollable').innerHTML = htmlString
} }
function prepareServerSelectionList(){ function prepareServerSelectionList(){
populateServerListings() populateServerListings()
setServerListingHandlers() setServerListingHandlers()
} }
function prepareAccountSelectionList(){ function prepareAccountSelectionList(){
populateAccountListings() populateAccountListings()
setAccountListingHandlers() setAccountListingHandlers()
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,419 +1,419 @@
/** /**
* Initialize UI functions which depend on internal modules. * Initialize UI functions which depend on internal modules.
* Loaded after core UI functions are initialized in uicore.js. * Loaded after core UI functions are initialized in uicore.js.
*/ */
// Requirements // Requirements
const path = require('path') const path = require('path')
const AuthManager = require('./assets/js/authmanager') const AuthManager = require('./assets/js/authmanager')
const ConfigManager = require('./assets/js/configmanager') const ConfigManager = require('./assets/js/configmanager')
const DistroManager = require('./assets/js/distromanager') const DistroManager = require('./assets/js/distromanager')
const Lang = require('./assets/js/langloader') const Lang = require('./assets/js/langloader')
let rscShouldLoad = false let rscShouldLoad = false
let fatalStartupError = false let fatalStartupError = false
// Mapping of each view to their container IDs. // Mapping of each view to their container IDs.
const VIEWS = { const VIEWS = {
landing: '#landingContainer', landing: '#landingContainer',
login: '#loginContainer', login: '#loginContainer',
settings: '#settingsContainer', settings: '#settingsContainer',
welcome: '#welcomeContainer' welcome: '#welcomeContainer'
} }
// The currently shown view container. // The currently shown view container.
let currentView let currentView
/** /**
* Switch launcher views. * Switch launcher views.
* *
* @param {string} current The ID of the current view container. * @param {string} current The ID of the current view container.
* @param {*} next The ID of the next view container. * @param {*} next The ID of the next view container.
* @param {*} currentFadeTime Optional. The fade out time for the current view. * @param {*} currentFadeTime Optional. The fade out time for the current view.
* @param {*} nextFadeTime Optional. The fade in time for the next view. * @param {*} nextFadeTime Optional. The fade in time for the next view.
* @param {*} onCurrentFade Optional. Callback function to execute when the current * @param {*} onCurrentFade Optional. Callback function to execute when the current
* view fades out. * view fades out.
* @param {*} onNextFade Optional. Callback function to execute when the next view * @param {*} onNextFade Optional. Callback function to execute when the next view
* fades in. * fades in.
*/ */
function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){ function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){
currentView = next currentView = next
$(`${current}`).fadeOut(currentFadeTime, () => { $(`${current}`).fadeOut(currentFadeTime, () => {
onCurrentFade() onCurrentFade()
$(`${next}`).fadeIn(nextFadeTime, () => { $(`${next}`).fadeIn(nextFadeTime, () => {
onNextFade() onNextFade()
}) })
}) })
} }
/** /**
* Get the currently shown view container. * Get the currently shown view container.
* *
* @returns {string} The currently shown view container. * @returns {string} The currently shown view container.
*/ */
function getCurrentView(){ function getCurrentView(){
return currentView return currentView
} }
function showMainUI(data){ function showMainUI(data){
if(!isDev){ if(!isDev){
loggerAutoUpdater.log('Initializing..') loggerAutoUpdater.log('Initializing..')
ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease()) ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease())
} }
prepareSettings(true) prepareSettings(true)
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer())) updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
refreshServerStatus() refreshServerStatus()
setTimeout(() => { setTimeout(() => {
document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)' document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')` document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')`
$('#main').show() $('#main').show()
const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0 const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0
// If this is enabled in a development environment we'll get ratelimited. // If this is enabled in a development environment we'll get ratelimited.
// The relaunch frequency is usually far too high. // The relaunch frequency is usually far too high.
if(!isDev && isLoggedIn){ if(!isDev && isLoggedIn){
validateSelectedAccount() validateSelectedAccount()
} }
if(ConfigManager.isFirstLaunch()){ if(ConfigManager.isFirstLaunch()){
currentView = VIEWS.welcome currentView = VIEWS.welcome
$(VIEWS.welcome).fadeIn(1000) $(VIEWS.welcome).fadeIn(1000)
} else { } else {
if(isLoggedIn){ if(isLoggedIn){
currentView = VIEWS.landing currentView = VIEWS.landing
$(VIEWS.landing).fadeIn(1000) $(VIEWS.landing).fadeIn(1000)
} else { } else {
currentView = VIEWS.login currentView = VIEWS.login
$(VIEWS.login).fadeIn(1000) $(VIEWS.login).fadeIn(1000)
} }
} }
setTimeout(() => { setTimeout(() => {
$('#loadingContainer').fadeOut(500, () => { $('#loadingContainer').fadeOut(500, () => {
$('#loadSpinnerImage').removeClass('rotating') $('#loadSpinnerImage').removeClass('rotating')
}) })
}, 250) }, 250)
}, 750) }, 750)
// Disable tabbing to the news container. // Disable tabbing to the news container.
initNews().then(() => { initNews().then(() => {
$('#newsContainer *').attr('tabindex', '-1') $('#newsContainer *').attr('tabindex', '-1')
}) })
} }
function showFatalStartupError(){ function showFatalStartupError(){
setTimeout(() => { setTimeout(() => {
$('#loadingContainer').fadeOut(250, () => { $('#loadingContainer').fadeOut(250, () => {
document.getElementById('overlayContainer').style.background = 'none' document.getElementById('overlayContainer').style.background = 'none'
setOverlayContent( setOverlayContent(
'Fatal Error: Unable to Load Distribution Index', 'Fatal Error: Unable to Load Distribution Index',
'A connection could not be established to our servers to download the distribution index. No local copies were available to load. <br><br>The distribution index is an essential file which provides the latest server information. The launcher is unable to start without it. Ensure you are connected to the internet and relaunch the application.', 'A connection could not be established to our servers to download the distribution index. No local copies were available to load. <br><br>The distribution index is an essential file which provides the latest server information. The launcher is unable to start without it. Ensure you are connected to the internet and relaunch the application.',
'Close' 'Close'
) )
setOverlayHandler(() => { setOverlayHandler(() => {
const window = remote.getCurrentWindow() const window = remote.getCurrentWindow()
window.close() window.close()
}) })
toggleOverlay(true) toggleOverlay(true)
}) })
}, 750) }, 750)
} }
/** /**
* Common functions to perform after refreshing the distro index. * Common functions to perform after refreshing the distro index.
* *
* @param {Object} data The distro index object. * @param {Object} data The distro index object.
*/ */
function onDistroRefresh(data){ function onDistroRefresh(data){
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer())) updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
refreshServerStatus() refreshServerStatus()
initNews() initNews()
syncModConfigurations(data) syncModConfigurations(data)
} }
/** /**
* Sync the mod configurations with the distro index. * Sync the mod configurations with the distro index.
* *
* @param {Object} data The distro index object. * @param {Object} data The distro index object.
*/ */
function syncModConfigurations(data){ function syncModConfigurations(data){
const syncedCfgs = [] const syncedCfgs = []
for(let serv of data.getServers()){ for(let serv of data.getServers()){
const id = serv.getID() const id = serv.getID()
const mdls = serv.getModules() const mdls = serv.getModules()
const cfg = ConfigManager.getModConfiguration(id) const cfg = ConfigManager.getModConfiguration(id)
if(cfg != null){ if(cfg != null){
const modsOld = cfg.mods const modsOld = cfg.mods
const mods = {} const mods = {}
for(let mdl of mdls){ for(let mdl of mdls){
const type = mdl.getType() const type = mdl.getType()
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(!mdl.getRequired().isRequired()){ if(!mdl.getRequired().isRequired()){
const mdlID = mdl.getVersionlessID() const mdlID = mdl.getVersionlessID()
if(modsOld[mdlID] == null){ if(modsOld[mdlID] == null){
mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl) mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl)
} else { } else {
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false) mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false)
} }
} else { } else {
if(mdl.hasSubModules()){ if(mdl.hasSubModules()){
const mdlID = mdl.getVersionlessID() const mdlID = mdl.getVersionlessID()
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(typeof v === 'object'){ if(typeof v === 'object'){
if(modsOld[mdlID] == null){ if(modsOld[mdlID] == null){
mods[mdlID] = v mods[mdlID] = v
} else { } else {
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true) mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true)
} }
} }
} }
} }
} }
} }
syncedCfgs.push({ syncedCfgs.push({
id, id,
mods mods
}) })
} else { } else {
const mods = {} const mods = {}
for(let mdl of mdls){ for(let mdl of mdls){
const type = mdl.getType() const type = mdl.getType()
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(!mdl.getRequired().isRequired()){ if(!mdl.getRequired().isRequired()){
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
} else { } else {
if(mdl.hasSubModules()){ if(mdl.hasSubModules()){
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(typeof v === 'object'){ if(typeof v === 'object'){
mods[mdl.getVersionlessID()] = v mods[mdl.getVersionlessID()] = v
} }
} }
} }
} }
} }
syncedCfgs.push({ syncedCfgs.push({
id, id,
mods mods
}) })
} }
} }
ConfigManager.setModConfigurations(syncedCfgs) ConfigManager.setModConfigurations(syncedCfgs)
ConfigManager.save() ConfigManager.save()
} }
/** /**
* Recursively scan for optional sub modules. If none are found, * Recursively scan for optional sub modules. If none are found,
* this function returns a boolean. If optional sub modules do exist, * this function returns a boolean. If optional sub modules do exist,
* a recursive configuration object is returned. * a recursive configuration object is returned.
* *
* @returns {boolean | Object} The resolved mod configuration. * @returns {boolean | Object} The resolved mod configuration.
*/ */
function scanOptionalSubModules(mdls, origin){ function scanOptionalSubModules(mdls, origin){
if(mdls != null){ if(mdls != null){
const mods = {} const mods = {}
for(let mdl of mdls){ for(let mdl of mdls){
const type = mdl.getType() const type = mdl.getType()
// Optional types. // Optional types.
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
// It is optional. // It is optional.
if(!mdl.getRequired().isRequired()){ if(!mdl.getRequired().isRequired()){
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
} else { } else {
if(mdl.hasSubModules()){ if(mdl.hasSubModules()){
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(typeof v === 'object'){ if(typeof v === 'object'){
mods[mdl.getVersionlessID()] = v mods[mdl.getVersionlessID()] = v
} }
} }
} }
} }
} }
if(Object.keys(mods).length > 0){ if(Object.keys(mods).length > 0){
const ret = { const ret = {
mods mods
} }
if(!origin.getRequired().isRequired()){ if(!origin.getRequired().isRequired()){
ret.value = origin.getRequired().isDefault() ret.value = origin.getRequired().isDefault()
} }
return ret return ret
} }
} }
return origin.getRequired().isDefault() return origin.getRequired().isDefault()
} }
/** /**
* Recursively merge an old configuration into a new configuration. * Recursively merge an old configuration into a new configuration.
* *
* @param {boolean | Object} o The old configuration value. * @param {boolean | Object} o The old configuration value.
* @param {boolean | Object} n The new configuration value. * @param {boolean | Object} n The new configuration value.
* @param {boolean} nReq If the new value is a required mod. * @param {boolean} nReq If the new value is a required mod.
* *
* @returns {boolean | Object} The merged configuration. * @returns {boolean | Object} The merged configuration.
*/ */
function mergeModConfiguration(o, n, nReq = false){ function mergeModConfiguration(o, n, nReq = false){
if(typeof o === 'boolean'){ if(typeof o === 'boolean'){
if(typeof n === 'boolean') return o if(typeof n === 'boolean') return o
else if(typeof n === 'object'){ else if(typeof n === 'object'){
if(!nReq){ if(!nReq){
n.value = o n.value = o
} }
return n return n
} }
} else if(typeof o === 'object'){ } else if(typeof o === 'object'){
if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true
else if(typeof n === 'object'){ else if(typeof n === 'object'){
if(!nReq){ if(!nReq){
n.value = typeof o.value !== 'undefined' ? o.value : true n.value = typeof o.value !== 'undefined' ? o.value : true
} }
const newMods = Object.keys(n.mods) const newMods = Object.keys(n.mods)
for(let i=0; i<newMods.length; i++){ for(let i=0; i<newMods.length; i++){
const mod = newMods[i] const mod = newMods[i]
if(o.mods[mod] != null){ if(o.mods[mod] != null){
n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod]) n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod])
} }
} }
return n return n
} }
} }
// If for some reason we haven't been able to merge, // If for some reason we haven't been able to merge,
// wipe the old value and use the new one. Just to be safe // wipe the old value and use the new one. Just to be safe
return n return n
} }
function refreshDistributionIndex(remote, onSuccess, onError){ function refreshDistributionIndex(remote, onSuccess, onError){
if(remote){ if(remote){
DistroManager.pullRemote() DistroManager.pullRemote()
.then(onSuccess) .then(onSuccess)
.catch(onError) .catch(onError)
} else { } else {
DistroManager.pullLocal() DistroManager.pullLocal()
.then(onSuccess) .then(onSuccess)
.catch(onError) .catch(onError)
} }
} }
async function validateSelectedAccount(){ async function validateSelectedAccount(){
const selectedAcc = ConfigManager.getSelectedAccount() const selectedAcc = ConfigManager.getSelectedAccount()
if(selectedAcc != null){ if(selectedAcc != null){
const val = await AuthManager.validateSelected() const val = await AuthManager.validateSelected()
if(!val){ if(!val){
ConfigManager.removeAuthAccount(selectedAcc.uuid) ConfigManager.removeAuthAccount(selectedAcc.uuid)
ConfigManager.save() ConfigManager.save()
const accLen = Object.keys(ConfigManager.getAuthAccounts()).length const accLen = Object.keys(ConfigManager.getAuthAccounts()).length
setOverlayContent( setOverlayContent(
'Failed to Refresh Login', 'Failed to Refresh Login',
`We were unable to refresh the login for <strong>${selectedAcc.displayName}</strong>. Please ${accLen > 0 ? 'select another account or ' : ''} login again.`, `We were unable to refresh the login for <strong>${selectedAcc.displayName}</strong>. Please ${accLen > 0 ? 'select another account or ' : ''} login again.`,
'Login', 'Login',
'Select Another Account' 'Select Another Account'
) )
setOverlayHandler(() => { setOverlayHandler(() => {
document.getElementById('loginUsername').value = selectedAcc.username document.getElementById('loginUsername').value = selectedAcc.username
validateEmail(selectedAcc.username) validateEmail(selectedAcc.username)
loginViewOnSuccess = getCurrentView() loginViewOnSuccess = getCurrentView()
loginViewOnCancel = getCurrentView() loginViewOnCancel = getCurrentView()
if(accLen > 0){ if(accLen > 0){
loginViewCancelHandler = () => { loginViewCancelHandler = () => {
ConfigManager.addAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName) ConfigManager.addAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
ConfigManager.save() ConfigManager.save()
validateSelectedAccount() validateSelectedAccount()
} }
loginCancelEnabled(true) loginCancelEnabled(true)
} }
toggleOverlay(false) toggleOverlay(false)
switchView(getCurrentView(), VIEWS.login) switchView(getCurrentView(), VIEWS.login)
}) })
setDismissHandler(() => { setDismissHandler(() => {
if(accLen > 1){ if(accLen > 1){
prepareAccountSelectionList() prepareAccountSelectionList()
$('#overlayContent').fadeOut(250, () => { $('#overlayContent').fadeOut(250, () => {
bindOverlayKeys(true, 'accountSelectContent', true) bindOverlayKeys(true, 'accountSelectContent', true)
$('#accountSelectContent').fadeIn(250) $('#accountSelectContent').fadeIn(250)
}) })
} else { } else {
const accountsObj = ConfigManager.getAuthAccounts() const accountsObj = ConfigManager.getAuthAccounts()
const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v]) const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v])
// This function validates the account switch. // This function validates the account switch.
setSelectedAccount(accounts[0].uuid) setSelectedAccount(accounts[0].uuid)
toggleOverlay(false) toggleOverlay(false)
} }
}) })
toggleOverlay(true, accLen > 0) toggleOverlay(true, accLen > 0)
} else { } else {
return true return true
} }
} else { } else {
return true return true
} }
} }
/** /**
* Temporary function to update the selected account along * Temporary function to update the selected account along
* with the relevent UI elements. * with the relevent UI elements.
* *
* @param {string} uuid The UUID of the account. * @param {string} uuid The UUID of the account.
*/ */
function setSelectedAccount(uuid){ function setSelectedAccount(uuid){
const authAcc = ConfigManager.setSelectedAccount(uuid) const authAcc = ConfigManager.setSelectedAccount(uuid)
ConfigManager.save() ConfigManager.save()
updateSelectedAccount(authAcc) updateSelectedAccount(authAcc)
validateSelectedAccount() validateSelectedAccount()
} }
// Synchronous Listener // Synchronous Listener
document.addEventListener('readystatechange', function(){ document.addEventListener('readystatechange', function(){
if (document.readyState === 'interactive' || document.readyState === 'complete'){ if (document.readyState === 'interactive' || document.readyState === 'complete'){
if(rscShouldLoad){ if(rscShouldLoad){
rscShouldLoad = false rscShouldLoad = false
if(!fatalStartupError){ if(!fatalStartupError){
const data = DistroManager.getDistribution() const data = DistroManager.getDistribution()
showMainUI(data) showMainUI(data)
} else { } else {
showFatalStartupError() showFatalStartupError()
} }
} }
} }
}, false) }, false)
// Actions that must be performed after the distribution index is downloaded. // Actions that must be performed after the distribution index is downloaded.
ipcRenderer.on('distributionIndexDone', (event, res) => { ipcRenderer.on('distributionIndexDone', (event, res) => {
if(res) { if(res) {
const data = DistroManager.getDistribution() const data = DistroManager.getDistribution()
syncModConfigurations(data) syncModConfigurations(data)
if(document.readyState === 'interactive' || document.readyState === 'complete'){ if(document.readyState === 'interactive' || document.readyState === 'complete'){
showMainUI(data) showMainUI(data)
} else { } else {
rscShouldLoad = true rscShouldLoad = true
} }
} else { } else {
fatalStartupError = true fatalStartupError = true
if(document.readyState === 'interactive' || document.readyState === 'complete'){ if(document.readyState === 'interactive' || document.readyState === 'complete'){
showFatalStartupError() showFatalStartupError()
} else { } else {
rscShouldLoad = true rscShouldLoad = true
} }
} }
}) })

View File

@ -1,213 +1,213 @@
/** /**
* Core UI functions are initialized in this file. This prevents * Core UI functions are initialized in this file. This prevents
* unexpected errors from breaking the core features. Specifically, * unexpected errors from breaking the core features. Specifically,
* actions in this file should not require the usage of any internal * actions in this file should not require the usage of any internal
* modules, excluding dependencies. * modules, excluding dependencies.
*/ */
// Requirements // Requirements
const $ = require('jquery') const $ = require('jquery')
const {ipcRenderer, remote, shell, webFrame} = require('electron') const {ipcRenderer, remote, shell, webFrame} = require('electron')
const isDev = require('./assets/js/isdev') const isDev = require('./assets/js/isdev')
const LoggerUtil = require('./assets/js/loggerutil') const LoggerUtil = require('./assets/js/loggerutil')
const loggerUICore = LoggerUtil('%c[UICore]', 'color: #000668; font-weight: bold') const loggerUICore = LoggerUtil('%c[UICore]', 'color: #000668; font-weight: bold')
const loggerAutoUpdater = LoggerUtil('%c[AutoUpdater]', 'color: #000668; font-weight: bold') const loggerAutoUpdater = LoggerUtil('%c[AutoUpdater]', 'color: #000668; font-weight: bold')
const loggerAutoUpdaterSuccess = LoggerUtil('%c[AutoUpdater]', 'color: #209b07; font-weight: bold') const loggerAutoUpdaterSuccess = LoggerUtil('%c[AutoUpdater]', 'color: #209b07; font-weight: bold')
// Log deprecation and process warnings. // Log deprecation and process warnings.
process.traceProcessWarnings = true process.traceProcessWarnings = true
process.traceDeprecation = true process.traceDeprecation = true
// Disable eval function. // Disable eval function.
// eslint-disable-next-line // eslint-disable-next-line
window.eval = global.eval = function () { window.eval = global.eval = function () {
throw new Error('Sorry, this app does not support window.eval().') throw new Error('Sorry, this app does not support window.eval().')
} }
// Display warning when devtools window is opened. // Display warning when devtools window is opened.
remote.getCurrentWebContents().on('devtools-opened', () => { remote.getCurrentWebContents().on('devtools-opened', () => {
console.log('%cThe console is dark and full of terrors.', 'color: white; -webkit-text-stroke: 4px #a02d2a; font-size: 60px; font-weight: bold') console.log('%cThe console is dark and full of terrors.', 'color: white; -webkit-text-stroke: 4px #a02d2a; font-size: 60px; font-weight: bold')
console.log('%cIf you\'ve been told to paste something here, you\'re being scammed.', 'font-size: 16px') console.log('%cIf you\'ve been told to paste something here, you\'re being scammed.', 'font-size: 16px')
console.log('%cUnless you know exactly what you\'re doing, close this window.', 'font-size: 16px') console.log('%cUnless you know exactly what you\'re doing, close this window.', 'font-size: 16px')
}) })
// Disable zoom, needed for darwin. // Disable zoom, needed for darwin.
webFrame.setZoomLevel(0) webFrame.setZoomLevel(0)
webFrame.setVisualZoomLevelLimits(1, 1) webFrame.setVisualZoomLevelLimits(1, 1)
// Initialize auto updates in production environments. // Initialize auto updates in production environments.
let updateCheckListener let updateCheckListener
if(!isDev){ if(!isDev){
ipcRenderer.on('autoUpdateNotification', (event, arg, info) => { ipcRenderer.on('autoUpdateNotification', (event, arg, info) => {
switch(arg){ switch(arg){
case 'checking-for-update': case 'checking-for-update':
loggerAutoUpdater.log('Checking for update..') loggerAutoUpdater.log('Checking for update..')
settingsUpdateButtonStatus('Checking for Updates..', true) settingsUpdateButtonStatus('Checking for Updates..', true)
break break
case 'update-available': case 'update-available':
loggerAutoUpdaterSuccess.log('New update available', info.version) loggerAutoUpdaterSuccess.log('New update available', info.version)
if(process.platform === 'darwin'){ if(process.platform === 'darwin'){
info.darwindownload = `https://github.com/dscalzi/HeliosLauncher/releases/download/v${info.version}/helioslauncher-setup-${info.version}.dmg` info.darwindownload = `https://github.com/MastermDEV/NemesisMC-Launcher/releases/download/v${info.version}/nemesismclauncher-setup${info.version}.dmg`
showUpdateUI(info) showUpdateUI(info)
} }
populateSettingsUpdateInformation(info) populateSettingsUpdateInformation(info)
break break
case 'update-downloaded': case 'update-downloaded':
loggerAutoUpdaterSuccess.log('Update ' + info.version + ' ready to be installed.') loggerAutoUpdaterSuccess.log('Update ' + info.version + ' ready to be installed.')
settingsUpdateButtonStatus('Install Now', false, () => { settingsUpdateButtonStatus('Install Now', false, () => {
if(!isDev){ if(!isDev){
ipcRenderer.send('autoUpdateAction', 'installUpdateNow') ipcRenderer.send('autoUpdateAction', 'installUpdateNow')
} }
}) })
showUpdateUI(info) showUpdateUI(info)
break break
case 'update-not-available': case 'update-not-available':
loggerAutoUpdater.log('No new update found.') loggerAutoUpdater.log('No new update found.')
settingsUpdateButtonStatus('Check for Updates') settingsUpdateButtonStatus('Check for Updates')
break break
case 'ready': case 'ready':
updateCheckListener = setInterval(() => { updateCheckListener = setInterval(() => {
ipcRenderer.send('autoUpdateAction', 'checkForUpdate') ipcRenderer.send('autoUpdateAction', 'checkForUpdate')
}, 1800000) }, 1800000)
ipcRenderer.send('autoUpdateAction', 'checkForUpdate') ipcRenderer.send('autoUpdateAction', 'checkForUpdate')
break break
case 'realerror': case 'realerror':
if(info != null && info.code != null){ if(info != null && info.code != null){
if(info.code === 'ERR_UPDATER_INVALID_RELEASE_FEED'){ if(info.code === 'ERR_UPDATER_INVALID_RELEASE_FEED'){
loggerAutoUpdater.log('No suitable releases found.') loggerAutoUpdater.log('No suitable releases found.')
} else if(info.code === 'ERR_XML_MISSED_ELEMENT'){ } else if(info.code === 'ERR_XML_MISSED_ELEMENT'){
loggerAutoUpdater.log('No releases found.') loggerAutoUpdater.log('No releases found.')
} else { } else {
loggerAutoUpdater.error('Error during update check..', info) loggerAutoUpdater.error('Error during update check..', info)
loggerAutoUpdater.debug('Error Code:', info.code) loggerAutoUpdater.debug('Error Code:', info.code)
} }
} }
break break
default: default:
loggerAutoUpdater.log('Unknown argument', arg) loggerAutoUpdater.log('Unknown argument', arg)
break break
} }
}) })
} }
/** /**
* Send a notification to the main process changing the value of * Send a notification to the main process changing the value of
* allowPrerelease. If we are running a prerelease version, then * allowPrerelease. If we are running a prerelease version, then
* this will always be set to true, regardless of the current value * this will always be set to true, regardless of the current value
* of val. * of val.
* *
* @param {boolean} val The new allow prerelease value. * @param {boolean} val The new allow prerelease value.
*/ */
function changeAllowPrerelease(val){ function changeAllowPrerelease(val){
ipcRenderer.send('autoUpdateAction', 'allowPrereleaseChange', val) ipcRenderer.send('autoUpdateAction', 'allowPrereleaseChange', val)
} }
function showUpdateUI(info){ function showUpdateUI(info){
//TODO Make this message a bit more informative `${info.version}` //TODO Make this message a bit more informative `${info.version}`
document.getElementById('image_seal_container').setAttribute('update', true) document.getElementById('image_seal_container').setAttribute('update', true)
document.getElementById('image_seal_container').onclick = () => { document.getElementById('image_seal_container').onclick = () => {
/*setOverlayContent('Update Available', 'A new update for the launcher is available. Would you like to install now?', 'Install', 'Later') /*setOverlayContent('Update Available', 'A new update for the launcher is available. Would you like to install now?', 'Install', 'Later')
setOverlayHandler(() => { setOverlayHandler(() => {
if(!isDev){ if(!isDev){
ipcRenderer.send('autoUpdateAction', 'installUpdateNow') ipcRenderer.send('autoUpdateAction', 'installUpdateNow')
} else { } else {
console.error('Cannot install updates in development environment.') console.error('Cannot install updates in development environment.')
toggleOverlay(false) toggleOverlay(false)
} }
}) })
setDismissHandler(() => { setDismissHandler(() => {
toggleOverlay(false) toggleOverlay(false)
}) })
toggleOverlay(true, true)*/ toggleOverlay(true, true)*/
switchView(getCurrentView(), VIEWS.settings, 500, 500, () => { switchView(getCurrentView(), VIEWS.settings, 500, 500, () => {
settingsNavItemListener(document.getElementById('settingsNavUpdate'), false) settingsNavItemListener(document.getElementById('settingsNavUpdate'), false)
}) })
} }
} }
/* jQuery Example /* jQuery Example
$(function(){ $(function(){
loggerUICore.log('UICore Initialized'); loggerUICore.log('UICore Initialized');
})*/ })*/
document.addEventListener('readystatechange', function () { document.addEventListener('readystatechange', function () {
if (document.readyState === 'interactive'){ if (document.readyState === 'interactive'){
loggerUICore.log('UICore Initializing..') loggerUICore.log('UICore Initializing..')
// Bind close button. // Bind close button.
Array.from(document.getElementsByClassName('fCb')).map((val) => { Array.from(document.getElementsByClassName('fCb')).map((val) => {
val.addEventListener('click', e => { val.addEventListener('click', e => {
const window = remote.getCurrentWindow() const window = remote.getCurrentWindow()
window.close() window.close()
}) })
}) })
// Bind restore down button. // Bind restore down button.
Array.from(document.getElementsByClassName('fRb')).map((val) => { Array.from(document.getElementsByClassName('fRb')).map((val) => {
val.addEventListener('click', e => { val.addEventListener('click', e => {
const window = remote.getCurrentWindow() const window = remote.getCurrentWindow()
if(window.isMaximized()){ if(window.isMaximized()){
window.unmaximize() window.unmaximize()
} else { } else {
window.maximize() window.maximize()
} }
document.activeElement.blur() document.activeElement.blur()
}) })
}) })
// Bind minimize button. // Bind minimize button.
Array.from(document.getElementsByClassName('fMb')).map((val) => { Array.from(document.getElementsByClassName('fMb')).map((val) => {
val.addEventListener('click', e => { val.addEventListener('click', e => {
const window = remote.getCurrentWindow() const window = remote.getCurrentWindow()
window.minimize() window.minimize()
document.activeElement.blur() document.activeElement.blur()
}) })
}) })
// Remove focus from social media buttons once they're clicked. // Remove focus from social media buttons once they're clicked.
Array.from(document.getElementsByClassName('mediaURL')).map(val => { Array.from(document.getElementsByClassName('mediaURL')).map(val => {
val.addEventListener('click', e => { val.addEventListener('click', e => {
document.activeElement.blur() document.activeElement.blur()
}) })
}) })
} else if(document.readyState === 'complete'){ } else if(document.readyState === 'complete'){
//266.01 //266.01
//170.8 //170.8
//53.21 //53.21
// Bind progress bar length to length of bot wrapper // Bind progress bar length to length of bot wrapper
//const targetWidth = document.getElementById("launch_content").getBoundingClientRect().width //const targetWidth = document.getElementById("launch_content").getBoundingClientRect().width
//const targetWidth2 = document.getElementById("server_selection").getBoundingClientRect().width //const targetWidth2 = document.getElementById("server_selection").getBoundingClientRect().width
//const targetWidth3 = document.getElementById("launch_button").getBoundingClientRect().width //const targetWidth3 = document.getElementById("launch_button").getBoundingClientRect().width
document.getElementById('launch_details').style.maxWidth = 266.01 document.getElementById('launch_details').style.maxWidth = 266.01
document.getElementById('launch_progress').style.width = 170.8 document.getElementById('launch_progress').style.width = 170.8
document.getElementById('launch_details_right').style.maxWidth = 170.8 document.getElementById('launch_details_right').style.maxWidth = 170.8
document.getElementById('launch_progress_label').style.width = 53.21 document.getElementById('launch_progress_label').style.width = 53.21
} }
}, false) }, false)
/** /**
* Open web links in the user's default browser. * Open web links in the user's default browser.
*/ */
$(document).on('click', 'a[href^="http"]', function(event) { $(document).on('click', 'a[href^="http"]', function(event) {
event.preventDefault() event.preventDefault()
shell.openExternal(this.href) shell.openExternal(this.href)
}) })
/** /**
* Opens DevTools window if you hold (ctrl + shift + i). * Opens DevTools window if you hold (ctrl + shift + i).
* This will crash the program if you are using multiple * This will crash the program if you are using multiple
* DevTools, for example the chrome debugger in VS Code. * DevTools, for example the chrome debugger in VS Code.
*/ */
document.addEventListener('keydown', function (e) { document.addEventListener('keydown', function (e) {
if((e.key === 'I' || e.key === 'i') && e.ctrlKey && e.shiftKey){ if((e.key === 'I' || e.key === 'i') && e.ctrlKey && e.shiftKey){
let window = remote.getCurrentWindow() let window = remote.getCurrentWindow()
window.toggleDevTools() window.toggleDevTools()
} }
}) })

View File

@ -1,6 +1,6 @@
/** /**
* Script for welcome.ejs * Script for welcome.ejs
*/ */
document.getElementById('welcomeButton').addEventListener('click', e => { document.getElementById('welcomeButton').addEventListener('click', e => {
switchView(VIEWS.welcome, VIEWS.login) switchView(VIEWS.welcome, VIEWS.login)
}) })