2018-04-26 00:11:10 +00:00
/ * *
* Script for landing . ejs
* /
2018-04-25 21:40:46 +00:00
// Requirements
2018-04-26 06:39:47 +00:00
const cp = require ( 'child_process' )
2018-06-05 03:08:03 +00:00
const crypto = require ( 'crypto' )
2021-10-31 06:20:03 +00:00
const { URL } = require ( 'url' )
2022-02-06 23:23:44 +00:00
const { MojangRestAPI , getServerStatus } = require ( 'helios-core/mojang' )
2018-04-25 21:40:46 +00:00
// Internal Requirements
2018-07-22 15:40:15 +00:00
const DiscordWrapper = require ( './assets/js/discordwrapper' )
const ProcessBuilder = require ( './assets/js/processbuilder' )
2022-02-12 00:51:28 +00:00
const { RestResponseStatus , isDisplayableError } = require ( 'helios-core/common' )
2018-04-25 21:40:46 +00:00
// Launch Elements
2018-04-26 06:39:47 +00:00
const launch _content = document . getElementById ( 'launch_content' )
const launch _details = document . getElementById ( 'launch_details' )
const launch _progress = document . getElementById ( 'launch_progress' )
const launch _progress _label = document . getElementById ( 'launch_progress_label' )
const launch _details _text = document . getElementById ( 'launch_details_text' )
const server _selection _button = document . getElementById ( 'server_selection_button' )
2018-04-29 22:05:59 +00:00
const user _text = document . getElementById ( 'user_text' )
2018-04-25 21:40:46 +00:00
2022-02-12 00:51:28 +00:00
const loggerLanding = LoggerUtil1 ( '%c[Landing]' , 'color: #000668; font-weight: bold' )
2018-08-22 18:21:49 +00:00
2018-04-26 00:11:10 +00:00
/* Launch Progress Wrapper Functions */
/ * *
* Show / hide the loading area .
*
* @ param { boolean } loading True if the loading area should be shown , otherwise false .
* /
function toggleLaunchArea ( loading ) {
if ( loading ) {
launch _details . style . display = 'flex'
launch _content . style . display = 'none'
} else {
launch _details . style . display = 'none'
launch _content . style . display = 'inline-flex'
}
}
/ * *
* Set the details text of the loading area .
*
* @ param { string } details The new text for the loading details .
* /
function setLaunchDetails ( details ) {
launch _details _text . innerHTML = details
}
/ * *
* Set the value of the loading progress bar and display that value .
*
* @ param { number } value The progress value .
* @ param { number } max The total size .
* @ param { number | string } percent Optional . The percentage to display on the progress label .
* /
function setLaunchPercentage ( value , max , percent = ( ( value / max ) * 100 ) ) {
launch _progress . setAttribute ( 'max' , max )
launch _progress . setAttribute ( 'value' , value )
launch _progress _label . innerHTML = percent + '%'
}
/ * *
* Set the value of the OS progress bar and display that on the UI .
*
* @ param { number } value The progress value .
* @ param { number } max The total download size .
* @ param { number | string } percent Optional . The percentage to display on the progress label .
* /
function setDownloadPercentage ( value , max , percent = ( ( value / max ) * 100 ) ) {
remote . getCurrentWindow ( ) . setProgressBar ( value / max )
setLaunchPercentage ( value , max , percent )
}
2018-04-26 22:41:26 +00:00
/ * *
* Enable or disable the launch button .
*
* @ param { boolean } val True to enable , false to disable .
* /
function setLaunchEnabled ( val ) {
document . getElementById ( 'launch_button' ) . disabled = ! val
}
2018-04-25 21:40:46 +00:00
// Bind launch button
document . getElementById ( 'launch_button' ) . addEventListener ( 'click' , function ( e ) {
2018-08-22 18:21:49 +00:00
loggerLanding . log ( 'Launching game..' )
2018-12-31 15:39:27 +00:00
const mcVersion = DistroManager . getDistribution ( ) . getServer ( ConfigManager . getSelectedServer ( ) ) . getMinecraftVersion ( )
2018-04-25 21:40:46 +00:00
const jExe = ConfigManager . getJavaExecutable ( )
if ( jExe == null ) {
2018-12-31 15:39:27 +00:00
asyncSystemScan ( mcVersion )
2018-04-25 21:40:46 +00:00
} else {
2019-04-08 03:33:40 +00:00
setLaunchDetails ( Lang . queryJS ( 'landing.launch.pleaseWait' ) )
2018-04-25 21:40:46 +00:00
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
2019-03-06 04:05:01 +00:00
const jg = new JavaGuard ( mcVersion )
jg . _validateJavaBinary ( jExe ) . then ( ( v ) => {
2018-08-22 18:21:49 +00:00
loggerLanding . log ( 'Java version meta' , v )
2018-05-15 10:07:28 +00:00
if ( v . valid ) {
2018-04-25 21:40:46 +00:00
dlAsync ( )
} else {
2018-12-31 15:39:27 +00:00
asyncSystemScan ( mcVersion )
2018-04-25 21:40:46 +00:00
}
} )
}
} )
2018-05-30 20:00:07 +00:00
// Bind settings button
document . getElementById ( 'settingsMediaButton' ) . onclick = ( e ) => {
2018-05-31 02:22:17 +00:00
prepareSettings ( )
2018-05-30 20:00:07 +00:00
switchView ( getCurrentView ( ) , VIEWS . settings )
}
2018-06-21 16:38:21 +00:00
// Bind avatar overlay button.
document . getElementById ( 'avatarOverlay' ) . onclick = ( e ) => {
prepareSettings ( )
switchView ( getCurrentView ( ) , VIEWS . settings , 500 , 500 , ( ) => {
settingsNavItemListener ( document . getElementById ( 'settingsNavAccount' ) , false )
} )
}
2018-04-29 22:05:59 +00:00
// Bind selected account
function updateSelectedAccount ( authUser ) {
let username = 'No Account Selected'
2018-07-02 06:01:28 +00:00
if ( authUser != null ) {
if ( authUser . displayName != null ) {
username = authUser . displayName
}
if ( authUser . uuid != null ) {
2021-07-19 15:08:55 +00:00
document . getElementById ( 'avatarContainer' ) . style . backgroundImage = ` url('https://mc-heads.net/body/ ${ authUser . uuid } /right') `
2018-07-02 06:01:28 +00:00
}
2018-04-29 22:05:59 +00:00
}
user _text . innerHTML = username
}
updateSelectedAccount ( ConfigManager . getSelectedAccount ( ) )
2018-04-25 21:40:46 +00:00
// Bind selected server
2018-11-20 10:19:59 +00:00
function updateSelectedServer ( serv ) {
if ( getCurrentView ( ) === VIEWS . settings ) {
saveAllModConfigurations ( )
2018-04-26 22:41:26 +00:00
}
2018-11-20 10:19:59 +00:00
ConfigManager . setSelectedServer ( serv != null ? serv . getID ( ) : null )
ConfigManager . save ( )
server _selection _button . innerHTML = '\u2022 ' + ( serv != null ? serv . getName ( ) : 'No Server Selected' )
2018-08-07 04:58:32 +00:00
if ( getCurrentView ( ) === VIEWS . settings ) {
animateModsTabRefresh ( )
}
2018-11-20 10:19:59 +00:00
setLaunchEnabled ( serv != null )
2018-04-26 22:41:26 +00:00
}
2018-05-07 05:34:57 +00:00
// Real text is set in uibinder.js on distributionIndexDone.
2018-11-20 10:19:59 +00:00
server _selection _button . innerHTML = '\u2022 Loading..'
2018-08-22 14:54:09 +00:00
server _selection _button . onclick = ( e ) => {
2018-04-26 06:39:47 +00:00
e . target . blur ( )
2018-04-27 04:04:09 +00:00
toggleServerSelection ( true )
2018-08-22 14:54:09 +00:00
}
2018-04-25 21:40:46 +00:00
// Update Mojang Status Color
const refreshMojangStatuses = async function ( ) {
2018-08-22 18:21:49 +00:00
loggerLanding . log ( 'Refreshing Mojang Statuses..' )
2018-05-17 07:11:44 +00:00
2018-04-25 21:40:46 +00:00
let status = 'grey'
2018-07-22 17:31:15 +00:00
let tooltipEssentialHTML = ''
let tooltipNonEssentialHTML = ''
2018-05-17 07:11:44 +00:00
2022-02-06 23:23:44 +00:00
const response = await MojangRestAPI . status ( )
let statuses
if ( response . responseStatus === RestResponseStatus . SUCCESS ) {
statuses = response . data
} else {
loggerLanding . warn ( 'Unable to refresh Mojang service status.' )
statuses = MojangRestAPI . getDefaultStatuses ( )
}
greenCount = 0
greyCount = 0
for ( let i = 0 ; i < statuses . length ; i ++ ) {
const service = statuses [ i ]
if ( service . essential ) {
tooltipEssentialHTML += ` <div class="mojangStatusContainer">
< span class = "mojangStatusIcon" style = "color: ${MojangRestAPI.statusToHex(service.status)};" > & # 8226 ; < / s p a n >
< span class = "mojangStatusName" > $ { service . name } < / s p a n >
< / d i v > `
} else {
tooltipNonEssentialHTML += ` <div class="mojangStatusContainer">
< span class = "mojangStatusIcon" style = "color: ${MojangRestAPI.statusToHex(service.status)};" > & # 8226 ; < / s p a n >
< span class = "mojangStatusName" > $ { service . name } < / s p a n >
< / d i v > `
2018-04-25 21:40:46 +00:00
}
2018-05-17 07:11:44 +00:00
2022-02-06 23:23:44 +00:00
if ( service . status === 'yellow' && status !== 'red' ) {
status = 'yellow'
} else if ( service . status === 'red' ) {
status = 'red'
} else {
if ( service . status === 'grey' ) {
++ greyCount
2018-05-22 12:41:22 +00:00
}
2022-02-06 23:23:44 +00:00
++ greenCount
2018-04-25 21:40:46 +00:00
}
2018-05-17 07:11:44 +00:00
2022-02-06 23:23:44 +00:00
}
if ( greenCount === statuses . length ) {
if ( greyCount === statuses . length ) {
status = 'grey'
} else {
status = 'green'
}
2018-04-25 21:40:46 +00:00
}
2018-05-17 07:11:44 +00:00
document . getElementById ( 'mojangStatusEssentialContainer' ) . innerHTML = tooltipEssentialHTML
document . getElementById ( 'mojangStatusNonEssentialContainer' ) . innerHTML = tooltipNonEssentialHTML
2022-02-06 23:23:44 +00:00
document . getElementById ( 'mojang_status_icon' ) . style . color = MojangRestAPI . statusToHex ( status )
2018-04-25 21:40:46 +00:00
}
2018-04-26 22:41:26 +00:00
const refreshServerStatus = async function ( fade = false ) {
2018-08-22 18:21:49 +00:00
loggerLanding . log ( 'Refreshing Server Status' )
2018-07-22 15:40:15 +00:00
const serv = DistroManager . getDistribution ( ) . getServer ( ConfigManager . getSelectedServer ( ) )
2018-04-25 21:40:46 +00:00
let pLabel = 'SERVER'
let pVal = 'OFFLINE'
try {
2018-07-22 15:40:15 +00:00
const serverURL = new URL ( 'my://' + serv . getAddress ( ) )
2021-10-31 06:20:03 +00:00
const servStat = await getServerStatus ( 47 , serverURL . hostname , Number ( serverURL . port ) )
console . log ( servStat )
pLabel = 'PLAYERS'
pVal = servStat . players . online + '/' + servStat . players . max
2018-04-25 21:40:46 +00:00
} catch ( err ) {
2018-08-22 18:21:49 +00:00
loggerLanding . warn ( 'Unable to refresh server status, assuming offline.' )
loggerLanding . debug ( err )
2018-04-25 21:40:46 +00:00
}
2018-04-26 22:41:26 +00:00
if ( fade ) {
$ ( '#server_status_wrapper' ) . fadeOut ( 250 , ( ) => {
document . getElementById ( 'landingPlayerLabel' ) . innerHTML = pLabel
document . getElementById ( 'player_count' ) . innerHTML = pVal
$ ( '#server_status_wrapper' ) . fadeIn ( 500 )
} )
} else {
document . getElementById ( 'landingPlayerLabel' ) . innerHTML = pLabel
document . getElementById ( 'player_count' ) . innerHTML = pVal
}
2018-04-25 21:40:46 +00:00
}
refreshMojangStatuses ( )
2018-05-07 05:34:57 +00:00
// Server Status is refreshed in uibinder.js on distributionIndexDone.
2018-04-25 21:40:46 +00:00
// Set refresh rate to once every 5 minutes.
2018-04-26 22:41:26 +00:00
let mojangStatusListener = setInterval ( ( ) => refreshMojangStatuses ( true ) , 300000 )
let serverStatusListener = setInterval ( ( ) => refreshServerStatus ( true ) , 300000 )
2018-04-25 21:40:46 +00:00
2019-01-20 11:55:13 +00:00
/ * *
* Shows an error overlay , toggles off the launch area .
*
* @ param { string } title The overlay title .
* @ param { string } desc The overlay description .
* /
function showLaunchFailure ( title , desc ) {
setOverlayContent (
title ,
desc ,
'Okay'
)
setOverlayHandler ( null )
toggleOverlay ( true )
toggleLaunchArea ( false )
}
2018-04-25 21:40:46 +00:00
/* System (Java) Scan */
let sysAEx
let scanAt
2018-04-29 03:34:23 +00:00
let extractListener
2018-12-31 15:39:27 +00:00
/ * *
* Asynchronously scan the system for valid Java installations .
*
* @ param { string } mcVersion The Minecraft version we are scanning for .
* @ param { boolean } launchAfter Whether we should begin to launch after scanning .
* /
function asyncSystemScan ( mcVersion , launchAfter = true ) {
2018-04-25 21:40:46 +00:00
setLaunchDetails ( 'Please wait..' )
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
2022-02-12 00:51:28 +00:00
const loggerSysAEx = LoggerUtil1 ( '%c[SysAEx]' , 'color: #353232; font-weight: bold' )
2018-08-22 18:21:49 +00:00
2019-01-04 19:25:27 +00:00
const forkEnv = JSON . parse ( JSON . stringify ( process . env ) )
forkEnv . CONFIG _DIRECT _PATH = ConfigManager . getLauncherDirectory ( )
2018-04-25 21:40:46 +00:00
// Fork a process to run validations.
sysAEx = cp . fork ( path . join ( _ _dirname , 'assets' , 'js' , 'assetexec.js' ) , [
2019-03-06 04:05:01 +00:00
'JavaGuard' ,
mcVersion
2018-05-07 22:15:59 +00:00
] , {
2019-01-04 19:25:27 +00:00
env : forkEnv ,
2018-05-07 22:15:59 +00:00
stdio : 'pipe'
} )
// Stdout
2018-08-22 18:21:49 +00:00
sysAEx . stdio [ 1 ] . setEncoding ( 'utf8' )
2018-05-07 22:15:59 +00:00
sysAEx . stdio [ 1 ] . on ( 'data' , ( data ) => {
2018-08-22 18:21:49 +00:00
loggerSysAEx . log ( data )
2018-05-07 22:15:59 +00:00
} )
// Stderr
2018-08-22 18:21:49 +00:00
sysAEx . stdio [ 2 ] . setEncoding ( 'utf8' )
2018-05-07 22:15:59 +00:00
sysAEx . stdio [ 2 ] . on ( 'data' , ( data ) => {
2018-08-22 18:21:49 +00:00
loggerSysAEx . log ( data )
2018-05-07 22:15:59 +00:00
} )
2018-04-25 21:40:46 +00:00
sysAEx . on ( 'message' , ( m ) => {
2018-07-22 15:40:15 +00:00
if ( m . context === 'validateJava' ) {
2018-04-25 21:40:46 +00:00
if ( m . result == null ) {
// If the result is null, no valid Java installation was found.
// Show this information to the user.
setOverlayContent (
'No Compatible<br>Java Installation Found' ,
2021-10-15 03:17:40 +00:00
'In order to join WesterosCraft, you need a 64-bit installation of Java 8. Would you like us to install a copy?' ,
2018-04-25 21:40:46 +00:00
'Install Java' ,
'Install Manually'
)
setOverlayHandler ( ( ) => {
setLaunchDetails ( 'Preparing Java Download..' )
2019-06-02 22:11:39 +00:00
sysAEx . send ( { task : 'changeContext' , class : 'AssetGuard' , args : [ ConfigManager . getCommonDirectory ( ) , ConfigManager . getJavaExecutable ( ) ] } )
sysAEx . send ( { task : 'execute' , function : '_enqueueOpenJDK' , argsArr : [ ConfigManager . getDataDirectory ( ) ] } )
2018-04-25 21:40:46 +00:00
toggleOverlay ( false )
} )
setDismissHandler ( ( ) => {
$ ( '#overlayContent' ) . fadeOut ( 250 , ( ) => {
//$('#overlayDismiss').toggle(false)
setOverlayContent (
2018-08-20 08:02:27 +00:00
'Java is Required<br>to Launch' ,
2019-09-05 20:08:47 +00:00
'A valid x64 installation of Java 8 is required to launch.<br><br>Please refer to our <a href="https://github.com/dscalzi/HeliosLauncher/wiki/Java-Management#manually-installing-a-valid-version-of-java">Java Management Guide</a> for instructions on how to manually install Java.' ,
2018-04-25 21:40:46 +00:00
'I Understand' ,
'Go Back'
)
setOverlayHandler ( ( ) => {
toggleLaunchArea ( false )
toggleOverlay ( false )
} )
setDismissHandler ( ( ) => {
toggleOverlay ( false , true )
asyncSystemScan ( )
} )
$ ( '#overlayContent' ) . fadeIn ( 250 )
} )
} )
toggleOverlay ( true , true )
} else {
// Java installation found, use this to launch the game.
ConfigManager . setJavaExecutable ( m . result )
ConfigManager . save ( )
2018-06-14 07:49:55 +00:00
// We need to make sure that the updated value is on the settings UI.
// Just incase the settings UI is already open.
settingsJavaExecVal . value = m . result
populateJavaExecDetails ( settingsJavaExecVal . value )
2018-04-25 21:40:46 +00:00
if ( launchAfter ) {
dlAsync ( )
}
sysAEx . disconnect ( )
}
2019-06-02 22:11:39 +00:00
} else if ( m . context === '_enqueueOpenJDK' ) {
2018-04-25 21:40:46 +00:00
if ( m . result === true ) {
// Oracle JRE enqueued successfully, begin download.
setLaunchDetails ( 'Downloading Java..' )
2018-07-22 15:40:15 +00:00
sysAEx . send ( { task : 'execute' , function : 'processDlQueues' , argsArr : [ [ { id : 'java' , limit : 1 } ] ] } )
2018-04-25 21:40:46 +00:00
} else {
// Oracle JRE enqueue failed. Probably due to a change in their website format.
// User will have to follow the guide to install Java.
setOverlayContent (
'Unexpected Issue:<br>Java Download Failed' ,
2019-09-05 20:08:47 +00:00
'Unfortunately we\'ve encountered an issue while attempting to install Java. You will need to manually install a copy. Please check out our <a href="https://github.com/dscalzi/HeliosLauncher/wiki">Troubleshooting Guide</a> for more details and instructions.' ,
2018-04-25 21:40:46 +00:00
'I Understand'
)
setOverlayHandler ( ( ) => {
toggleOverlay ( false )
toggleLaunchArea ( false )
} )
toggleOverlay ( true )
sysAEx . disconnect ( )
}
2018-07-22 15:40:15 +00:00
} else if ( m . context === 'progress' ) {
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
switch ( m . data ) {
case 'download' :
2019-06-02 22:11:39 +00:00
// Downloading..
2018-07-22 15:40:15 +00:00
setDownloadPercentage ( m . value , m . total , m . percent )
break
}
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
} else if ( m . context === 'complete' ) {
2018-07-02 06:24:13 +00:00
2018-07-22 15:40:15 +00:00
switch ( m . data ) {
2018-07-22 17:31:15 +00:00
case 'download' : {
2018-07-22 15:40:15 +00:00
// Show installing progress bar.
remote . getCurrentWindow ( ) . setProgressBar ( 2 )
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
// Wait for extration to complete.
const eLStr = 'Extracting'
let dotStr = ''
setLaunchDetails ( eLStr )
extractListener = setInterval ( ( ) => {
if ( dotStr . length >= 3 ) {
dotStr = ''
} else {
dotStr += '.'
}
setLaunchDetails ( eLStr + dotStr )
} , 750 )
break
2018-07-22 17:31:15 +00:00
}
2018-07-22 15:40:15 +00:00
case 'java' :
2018-07-22 17:31:15 +00:00
// Download & extraction complete, remove the loading from the OS progress bar.
2018-07-22 15:40:15 +00:00
remote . getCurrentWindow ( ) . setProgressBar ( - 1 )
// Extraction completed successfully.
ConfigManager . setJavaExecutable ( m . args [ 0 ] )
ConfigManager . save ( )
2018-04-29 03:34:23 +00:00
2018-07-22 15:40:15 +00:00
if ( extractListener != null ) {
clearInterval ( extractListener )
extractListener = null
}
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Java Installed!' )
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
if ( launchAfter ) {
dlAsync ( )
}
sysAEx . disconnect ( )
break
2018-04-25 21:40:46 +00:00
}
2018-07-22 15:40:15 +00:00
2019-06-02 22:11:39 +00:00
} else if ( m . context === 'error' ) {
console . log ( m . error )
2018-04-25 21:40:46 +00:00
}
} )
// Begin system Java scan.
setLaunchDetails ( 'Checking system info..' )
2019-03-06 04:05:01 +00:00
sysAEx . send ( { task : 'execute' , function : 'validateJava' , argsArr : [ ConfigManager . getDataDirectory ( ) ] } )
2018-04-25 21:40:46 +00:00
}
// Keep reference to Minecraft Process
let proc
// Is DiscordRPC enabled
let hasRPC = false
// Joined server regex
2020-06-29 15:28:55 +00:00
// Change this if your server uses something different.
const GAME _JOINED _REGEX = /\[.+\]: Sound engine started/
2020-06-08 18:00:07 +00:00
const GAME _LAUNCH _REGEX = /^\[.+\]: (?:MinecraftForge .+ Initialized|ModLauncher .+ starting: .+)$/
const MIN _LINGER = 5000
2018-04-25 21:40:46 +00:00
let aEx
let serv
let versionData
let forgeData
2018-04-29 03:34:23 +00:00
let progressListener
2018-04-25 21:40:46 +00:00
function dlAsync ( login = true ) {
// Login parameter is temporary for debug purposes. Allows testing the validation/downloads without
// launching the game.
if ( login ) {
if ( ConfigManager . getSelectedAccount ( ) == null ) {
2018-08-22 18:21:49 +00:00
loggerLanding . error ( 'You must be logged into an account.' )
2018-04-25 21:40:46 +00:00
return
}
}
setLaunchDetails ( 'Please wait..' )
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
2022-02-12 00:51:28 +00:00
const loggerAEx = LoggerUtil1 ( '%c[AEx]' , 'color: #353232; font-weight: bold' )
const loggerLaunchSuite = LoggerUtil1 ( '%c[LaunchSuite]' , 'color: #000668; font-weight: bold' )
2018-08-22 18:21:49 +00:00
2019-01-04 19:25:27 +00:00
const forkEnv = JSON . parse ( JSON . stringify ( process . env ) )
forkEnv . CONFIG _DIRECT _PATH = ConfigManager . getLauncherDirectory ( )
2018-04-25 21:40:46 +00:00
// Start AssetExec to run validations and downloads in a forked process.
aEx = cp . fork ( path . join ( _ _dirname , 'assets' , 'js' , 'assetexec.js' ) , [
2019-03-06 04:05:01 +00:00
'AssetGuard' ,
2018-06-04 04:17:20 +00:00
ConfigManager . getCommonDirectory ( ) ,
2018-07-22 15:40:15 +00:00
ConfigManager . getJavaExecutable ( )
2018-05-07 22:15:59 +00:00
] , {
2019-01-04 19:25:27 +00:00
env : forkEnv ,
2018-05-07 22:15:59 +00:00
stdio : 'pipe'
} )
// Stdout
2018-08-22 18:21:49 +00:00
aEx . stdio [ 1 ] . setEncoding ( 'utf8' )
2018-05-07 22:15:59 +00:00
aEx . stdio [ 1 ] . on ( 'data' , ( data ) => {
2018-08-22 18:21:49 +00:00
loggerAEx . log ( data )
2018-05-07 22:15:59 +00:00
} )
// Stderr
2018-08-22 18:21:49 +00:00
aEx . stdio [ 2 ] . setEncoding ( 'utf8' )
2018-05-07 22:15:59 +00:00
aEx . stdio [ 2 ] . on ( 'data' , ( data ) => {
2018-08-22 18:21:49 +00:00
loggerAEx . log ( data )
2018-05-07 22:15:59 +00:00
} )
2019-01-20 11:55:13 +00:00
aEx . on ( 'error' , ( err ) => {
loggerLaunchSuite . error ( 'Error during launch' , err )
showLaunchFailure ( 'Error During Launch' , err . message || 'See console (CTRL + Shift + i) for more details.' )
} )
aEx . on ( 'close' , ( code , signal ) => {
if ( code !== 0 ) {
loggerLaunchSuite . error ( ` AssetExec exited with code ${ code } , assuming error. ` )
showLaunchFailure ( 'Error During Launch' , 'See console (CTRL + Shift + i) for more details.' )
}
} )
2018-04-25 21:40:46 +00:00
// Establish communications between the AssetExec and current process.
aEx . on ( 'message' , ( m ) => {
2018-07-22 15:40:15 +00:00
if ( m . context === 'validate' ) {
switch ( m . data ) {
case 'distribution' :
setLaunchPercentage ( 20 , 100 )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Validated distibution index.' )
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Loading version information..' )
break
case 'version' :
setLaunchPercentage ( 40 , 100 )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Version data loaded.' )
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Validating asset integrity..' )
break
case 'assets' :
setLaunchPercentage ( 60 , 100 )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Asset Validation Complete' )
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Validating library integrity..' )
break
case 'libraries' :
setLaunchPercentage ( 80 , 100 )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Library validation complete.' )
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Validating miscellaneous file integrity..' )
break
case 'files' :
setLaunchPercentage ( 100 , 100 )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'File validation complete.' )
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Downloading files..' )
break
2018-04-25 21:40:46 +00:00
}
2018-07-22 15:40:15 +00:00
} else if ( m . context === 'progress' ) {
switch ( m . data ) {
2018-07-22 17:31:15 +00:00
case 'assets' : {
2018-07-22 15:40:15 +00:00
const perc = ( m . value / m . total ) * 20
setLaunchPercentage ( 40 + perc , 100 , parseInt ( 40 + perc ) )
break
2018-07-22 17:31:15 +00:00
}
2018-07-22 15:40:15 +00:00
case 'download' :
setDownloadPercentage ( m . value , m . total , m . percent )
break
2018-07-22 17:31:15 +00:00
case 'extract' : {
2018-07-22 15:40:15 +00:00
// Show installing progress bar.
remote . getCurrentWindow ( ) . setProgressBar ( 2 )
// Download done, extracting.
const eLStr = 'Extracting libraries'
let dotStr = ''
setLaunchDetails ( eLStr )
progressListener = setInterval ( ( ) => {
if ( dotStr . length >= 3 ) {
dotStr = ''
} else {
dotStr += '.'
}
setLaunchDetails ( eLStr + dotStr )
} , 750 )
break
2018-07-22 17:31:15 +00:00
}
2018-07-22 15:40:15 +00:00
}
} else if ( m . context === 'complete' ) {
switch ( m . data ) {
case 'download' :
// Download and extraction complete, remove the loading from the OS progress bar.
remote . getCurrentWindow ( ) . setProgressBar ( - 1 )
if ( progressListener != null ) {
clearInterval ( progressListener )
progressListener = null
}
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
setLaunchDetails ( 'Preparing to launch..' )
break
}
} else if ( m . context === 'error' ) {
switch ( m . data ) {
case 'download' :
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . error ( 'Error while downloading:' , m . error )
2018-07-22 15:40:15 +00:00
if ( m . error . code === 'ENOENT' ) {
2019-01-20 11:55:13 +00:00
showLaunchFailure (
2018-07-22 15:40:15 +00:00
'Download Error' ,
2019-01-20 11:55:13 +00:00
'Could not connect to the file server. Ensure that you are connected to the internet and try again.'
2018-07-22 15:40:15 +00:00
)
2018-04-29 03:34:23 +00:00
} else {
2019-01-20 11:55:13 +00:00
showLaunchFailure (
2018-07-22 15:40:15 +00:00
'Download Error' ,
2019-01-20 11:55:13 +00:00
'Check the console (CTRL + Shift + i) for more details. Please try again.'
2018-07-22 15:40:15 +00:00
)
2018-04-29 03:34:23 +00:00
}
2018-05-08 10:34:16 +00:00
2018-07-22 15:40:15 +00:00
remote . getCurrentWindow ( ) . setProgressBar ( - 1 )
2018-04-25 21:40:46 +00:00
2018-07-22 15:40:15 +00:00
// Disconnect from AssetExec
aEx . disconnect ( )
break
2018-04-25 21:40:46 +00:00
}
2018-07-22 15:40:15 +00:00
} else if ( m . context === 'validateEverything' ) {
2018-04-25 21:40:46 +00:00
2019-02-18 11:31:01 +00:00
let allGood = true
2018-07-26 02:30:47 +00:00
// If these properties are not defined it's likely an error.
if ( m . result . forgeData == null || m . result . versionData == null ) {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . error ( 'Error during validation:' , m . result )
2019-02-18 11:31:01 +00:00
loggerLaunchSuite . error ( 'Error during launch' , m . result . error )
showLaunchFailure ( 'Error During Launch' , 'Please check the console (CTRL + Shift + i) for more details.' )
allGood = false
2018-07-26 02:30:47 +00:00
}
2018-07-22 15:40:15 +00:00
forgeData = m . result . forgeData
versionData = m . result . versionData
2018-04-25 21:40:46 +00:00
2019-02-18 11:31:01 +00:00
if ( login && allGood ) {
2018-04-25 21:40:46 +00:00
const authUser = ConfigManager . getSelectedAccount ( )
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( ` Sending selected account ( ${ authUser . displayName } ) to ProcessBuilder. ` )
2019-02-18 11:31:01 +00:00
let pb = new ProcessBuilder ( serv , versionData , forgeData , authUser , remote . app . getVersion ( ) )
2018-04-25 21:40:46 +00:00
setLaunchDetails ( 'Launching game..' )
2020-08-25 21:11:40 +00:00
// const SERVER_JOINED_REGEX = /\[.+\]: \[CHAT\] [a-zA-Z0-9_]{1,16} joined the game/
const SERVER _JOINED _REGEX = new RegExp ( ` \\ [.+ \\ ]: \\ [CHAT \\ ] ${ authUser . displayName } joined the game ` )
2020-06-08 18:00:07 +00:00
const onLoadComplete = ( ) => {
toggleLaunchArea ( false )
if ( hasRPC ) {
DiscordWrapper . updateDetails ( 'Loading game..' )
}
proc . stdout . on ( 'data' , gameStateChange )
proc . stdout . removeListener ( 'data' , tempListener )
proc . stderr . removeListener ( 'data' , gameErrorListener )
}
const start = Date . now ( )
2018-08-13 12:25:25 +00:00
// Attach a temporary listener to the client output.
// Will wait for a certain bit of text meaning that
// the client application has started, and we can hide
// the progress bar stuff.
const tempListener = function ( data ) {
if ( GAME _LAUNCH _REGEX . test ( data . trim ( ) ) ) {
2020-06-08 18:00:07 +00:00
const diff = Date . now ( ) - start
if ( diff < MIN _LINGER ) {
setTimeout ( onLoadComplete , MIN _LINGER - diff )
} else {
onLoadComplete ( )
2018-04-25 21:40:46 +00:00
}
}
2018-08-13 12:25:25 +00:00
}
2018-04-25 21:40:46 +00:00
2018-08-13 12:25:25 +00:00
// Listener for Discord RPC.
const gameStateChange = function ( data ) {
data = data . trim ( )
if ( SERVER _JOINED _REGEX . test ( data ) ) {
DiscordWrapper . updateDetails ( 'Exploring the Realm!' )
} else if ( GAME _JOINED _REGEX . test ( data ) ) {
DiscordWrapper . updateDetails ( 'Sailing to Westeros!' )
2018-04-25 21:40:46 +00:00
}
2018-08-13 12:25:25 +00:00
}
2018-08-19 07:16:08 +00:00
const gameErrorListener = function ( data ) {
data = data . trim ( )
if ( data . indexOf ( 'Could not find or load main class net.minecraft.launchwrapper.Launch' ) > - 1 ) {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . error ( 'Game launch failed, LaunchWrapper was not downloaded properly.' )
2019-09-05 20:08:47 +00:00
showLaunchFailure ( 'Error During Launch' , 'The main file, LaunchWrapper, failed to download properly. As a result, the game cannot launch.<br><br>To fix this issue, temporarily turn off your antivirus software and launch the game again.<br><br>If you have time, please <a href="https://github.com/dscalzi/HeliosLauncher/issues">submit an issue</a> and let us know what antivirus software you use. We\'ll contact them and try to straighten things out.' )
2018-08-19 07:16:08 +00:00
}
}
2018-08-13 12:25:25 +00:00
try {
// Build Minecraft process.
proc = pb . build ( )
2018-04-25 21:40:46 +00:00
// Bind listeners to stdout.
proc . stdout . on ( 'data' , tempListener )
2018-08-19 07:16:08 +00:00
proc . stderr . on ( 'data' , gameErrorListener )
2018-08-13 12:25:25 +00:00
setLaunchDetails ( 'Done. Enjoy the server!' )
2018-04-25 21:40:46 +00:00
// Init Discord Hook
2018-07-22 15:40:15 +00:00
const distro = DistroManager . getDistribution ( )
2018-04-25 21:40:46 +00:00
if ( distro . discord != null && serv . discord != null ) {
DiscordWrapper . initRPC ( distro . discord , serv . discord )
hasRPC = true
proc . on ( 'close' , ( code , signal ) => {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Shutting down Discord Rich Presence..' )
2018-04-25 21:40:46 +00:00
DiscordWrapper . shutdownRPC ( )
hasRPC = false
proc = null
} )
}
} catch ( err ) {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . error ( 'Error during launch' , err )
2019-01-20 11:55:13 +00:00
showLaunchFailure ( 'Error During Launch' , 'Please check the console (CTRL + Shift + i) for more details.' )
2018-04-25 21:40:46 +00:00
}
}
// Disconnect from AssetExec
aEx . disconnect ( )
}
} )
// Begin Validations
// Validate Forge files.
setLaunchDetails ( 'Loading server information..' )
2018-05-09 00:10:46 +00:00
2018-07-22 15:40:15 +00:00
refreshDistributionIndex ( true , ( data ) => {
onDistroRefresh ( data )
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
} , ( err ) => {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . log ( 'Error while fetching a fresh copy of the distribution index.' , err )
2018-05-09 00:10:46 +00:00
refreshDistributionIndex ( false , ( data ) => {
onDistroRefresh ( data )
2018-07-22 15:40:15 +00:00
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
2018-05-09 00:10:46 +00:00
} , ( err ) => {
2018-08-22 18:21:49 +00:00
loggerLaunchSuite . error ( 'Unable to refresh distribution index.' , err )
2018-07-22 15:40:15 +00:00
if ( DistroManager . getDistribution ( ) == null ) {
2019-01-20 11:55:13 +00:00
showLaunchFailure ( 'Fatal Error' , 'Could not load a copy of the distribution index. See the console (CTRL + Shift + i) for more details.' )
2018-05-09 00:10:46 +00:00
// Disconnect from AssetExec
aEx . disconnect ( )
} else {
2018-07-22 15:40:15 +00:00
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
2018-05-09 00:10:46 +00:00
}
} )
2018-07-22 15:40:15 +00:00
} )
2018-05-07 01:45:20 +00:00
}
/ * *
* News Loading Functions
* /
2018-06-20 13:12:44 +00:00
// DOM Cache
const newsContent = document . getElementById ( 'newsContent' )
const newsArticleTitle = document . getElementById ( 'newsArticleTitle' )
const newsArticleDate = document . getElementById ( 'newsArticleDate' )
const newsArticleAuthor = document . getElementById ( 'newsArticleAuthor' )
const newsArticleComments = document . getElementById ( 'newsArticleComments' )
const newsNavigationStatus = document . getElementById ( 'newsNavigationStatus' )
const newsArticleContentScrollable = document . getElementById ( 'newsArticleContentScrollable' )
const nELoadSpan = document . getElementById ( 'nELoadSpan' )
2018-05-07 01:45:20 +00:00
// News slide caches.
let newsActive = false
let newsGlideCount = 0
/ * *
* Show the news UI via a slide animation .
*
* @ param { boolean } up True to slide up , otherwise false .
* /
function slide _ ( up ) {
const lCUpper = document . querySelector ( '#landingContainer > #upper' )
const lCLLeft = document . querySelector ( '#landingContainer > #lower > #left' )
const lCLCenter = document . querySelector ( '#landingContainer > #lower > #center' )
const lCLRight = document . querySelector ( '#landingContainer > #lower > #right' )
const newsBtn = document . querySelector ( '#landingContainer > #lower > #center #content' )
const landingContainer = document . getElementById ( 'landingContainer' )
const newsContainer = document . querySelector ( '#landingContainer > #newsContainer' )
newsGlideCount ++
if ( up ) {
lCUpper . style . top = '-200vh'
lCLLeft . style . top = '-200vh'
lCLCenter . style . top = '-200vh'
lCLRight . style . top = '-200vh'
newsBtn . style . top = '130vh'
newsContainer . style . top = '0px'
//date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric'})
//landingContainer.style.background = 'rgba(29, 29, 29, 0.55)'
landingContainer . style . background = 'rgba(0, 0, 0, 0.50)'
setTimeout ( ( ) => {
if ( newsGlideCount === 1 ) {
lCLCenter . style . transition = 'none'
newsBtn . style . transition = 'none'
}
newsGlideCount --
} , 2000 )
} else {
setTimeout ( ( ) => {
newsGlideCount --
} , 2000 )
landingContainer . style . background = null
lCLCenter . style . transition = null
newsBtn . style . transition = null
newsContainer . style . top = '100%'
lCUpper . style . top = '0px'
lCLLeft . style . top = '0px'
lCLCenter . style . top = '0px'
lCLRight . style . top = '0px'
newsBtn . style . top = '10px'
}
}
// Bind news button.
document . getElementById ( 'newsButton' ) . onclick = ( ) => {
2018-05-10 09:48:55 +00:00
// Toggle tabbing.
if ( newsActive ) {
2018-06-05 03:08:03 +00:00
$ ( '#landingContainer *' ) . removeAttr ( 'tabindex' )
$ ( '#newsContainer *' ) . attr ( 'tabindex' , '-1' )
2018-05-10 09:48:55 +00:00
} else {
2018-06-05 03:08:03 +00:00
$ ( '#landingContainer *' ) . attr ( 'tabindex' , '-1' )
$ ( '#newsContainer, #newsContainer *, #lower, #lower #center *' ) . removeAttr ( 'tabindex' )
if ( newsAlertShown ) {
$ ( '#newsButtonAlert' ) . fadeOut ( 2000 )
newsAlertShown = false
ConfigManager . setNewsCacheDismissed ( true )
ConfigManager . save ( )
}
2018-05-10 09:48:55 +00:00
}
2018-05-07 01:45:20 +00:00
slide _ ( ! newsActive )
newsActive = ! newsActive
}
// Array to store article meta.
let newsArr = null
// News load animation listener.
let newsLoadingListener = null
/ * *
* Set the news loading animation .
*
* @ param { boolean } val True to set loading animation , otherwise false .
* /
function setNewsLoading ( val ) {
if ( val ) {
const nLStr = 'Checking for News'
let dotStr = '..'
nELoadSpan . innerHTML = nLStr + dotStr
newsLoadingListener = setInterval ( ( ) => {
if ( dotStr . length >= 3 ) {
dotStr = ''
} else {
dotStr += '.'
}
nELoadSpan . innerHTML = nLStr + dotStr
} , 750 )
} else {
if ( newsLoadingListener != null ) {
clearInterval ( newsLoadingListener )
newsLoadingListener = null
}
}
}
// Bind retry button.
newsErrorRetry . onclick = ( ) => {
$ ( '#newsErrorFailed' ) . fadeOut ( 250 , ( ) => {
initNews ( )
$ ( '#newsErrorLoading' ) . fadeIn ( 250 )
} )
}
2018-06-20 13:12:44 +00:00
newsArticleContentScrollable . onscroll = ( e ) => {
if ( e . target . scrollTop > Number . parseFloat ( $ ( '.newsArticleSpacerTop' ) . css ( 'height' ) ) ) {
newsContent . setAttribute ( 'scrolled' , '' )
} else {
newsContent . removeAttribute ( 'scrolled' )
}
}
2018-05-07 01:45:20 +00:00
/ * *
* Reload the news without restarting .
2018-05-10 09:48:55 +00:00
*
* @ returns { Promise . < void > } A promise which resolves when the news
* content has finished loading and transitioning .
2018-05-07 01:45:20 +00:00
* /
2018-05-10 09:48:55 +00:00
function reloadNews ( ) {
return new Promise ( ( resolve , reject ) => {
$ ( '#newsContent' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorLoading' ) . fadeIn ( 250 )
initNews ( ) . then ( ( ) => {
resolve ( )
} )
} )
2018-05-07 01:45:20 +00:00
} )
}
2018-06-05 03:08:03 +00:00
let newsAlertShown = false
/ * *
* Show the news alert indicating there is new news .
* /
function showNewsAlert ( ) {
newsAlertShown = true
$ ( newsButtonAlert ) . fadeIn ( 250 )
}
2018-05-07 01:45:20 +00:00
/ * *
* Initialize News UI . This will load the news and prepare
* the UI accordingly .
2018-05-10 09:48:55 +00:00
*
* @ returns { Promise . < void > } A promise which resolves when the news
* content has finished loading and transitioning .
2018-05-07 01:45:20 +00:00
* /
2018-05-10 09:48:55 +00:00
function initNews ( ) {
2018-05-07 01:45:20 +00:00
2018-05-10 09:48:55 +00:00
return new Promise ( ( resolve , reject ) => {
setNewsLoading ( true )
2018-05-07 01:45:20 +00:00
2018-05-10 09:48:55 +00:00
let news = { }
loadNews ( ) . then ( news => {
2018-05-07 01:45:20 +00:00
2018-05-10 09:48:55 +00:00
newsArr = news . articles || null
2018-05-07 01:45:20 +00:00
2018-05-10 09:48:55 +00:00
if ( newsArr == null ) {
// News Loading Failed
setNewsLoading ( false )
2018-05-07 01:45:20 +00:00
2018-05-10 09:48:55 +00:00
$ ( '#newsErrorLoading' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorFailed' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
} else if ( newsArr . length === 0 ) {
// No News Articles
setNewsLoading ( false )
2018-05-07 01:45:20 +00:00
2018-06-05 03:08:03 +00:00
ConfigManager . setNewsCache ( {
date : null ,
content : null ,
dismissed : false
} )
ConfigManager . save ( )
2018-05-10 09:48:55 +00:00
$ ( '#newsErrorLoading' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorNone' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
} else {
// Success
setNewsLoading ( false )
2018-06-05 03:08:03 +00:00
const lN = newsArr [ 0 ]
const cached = ConfigManager . getNewsCache ( )
let newHash = crypto . createHash ( 'sha1' ) . update ( lN . content ) . digest ( 'hex' )
let newDate = new Date ( lN . date )
let isNew = false
if ( cached . date != null && cached . content != null ) {
if ( new Date ( cached . date ) >= newDate ) {
// Compare Content
if ( cached . content !== newHash ) {
isNew = true
showNewsAlert ( )
} else {
if ( ! cached . dismissed ) {
isNew = true
showNewsAlert ( )
}
}
} else {
isNew = true
showNewsAlert ( )
}
} else {
isNew = true
showNewsAlert ( )
}
if ( isNew ) {
ConfigManager . setNewsCache ( {
date : newDate . getTime ( ) ,
content : newHash ,
dismissed : false
} )
ConfigManager . save ( )
}
2018-05-10 09:48:55 +00:00
const switchHandler = ( forward ) => {
let cArt = parseInt ( newsContent . getAttribute ( 'article' ) )
let nxtArt = forward ? ( cArt >= newsArr . length - 1 ? 0 : cArt + 1 ) : ( cArt <= 0 ? newsArr . length - 1 : cArt - 1 )
displayArticle ( newsArr [ nxtArt ] , nxtArt + 1 )
}
2018-06-30 19:16:31 +00:00
2018-05-10 09:48:55 +00:00
document . getElementById ( 'newsNavigateRight' ) . onclick = ( ) => { switchHandler ( true ) }
document . getElementById ( 'newsNavigateLeft' ) . onclick = ( ) => { switchHandler ( false ) }
$ ( '#newsErrorContainer' ) . fadeOut ( 250 , ( ) => {
displayArticle ( newsArr [ 0 ] , 1 )
$ ( '#newsContent' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
}
2018-05-07 01:45:20 +00:00
} )
2018-05-10 09:48:55 +00:00
} )
2018-05-07 01:45:20 +00:00
}
2018-06-30 19:16:31 +00:00
/ * *
* Add keyboard controls to the news UI . Left and right arrows toggle
* between articles . If you are on the landing page , the up arrow will
* open the news UI .
* /
document . addEventListener ( 'keydown' , ( e ) => {
if ( newsActive ) {
if ( e . key === 'ArrowRight' || e . key === 'ArrowLeft' ) {
document . getElementById ( e . key === 'ArrowRight' ? 'newsNavigateRight' : 'newsNavigateLeft' ) . click ( )
}
// Interferes with scrolling an article using the down arrow.
// Not sure of a straight forward solution at this point.
// if(e.key === 'ArrowDown'){
// document.getElementById('newsButton').click()
// }
} else {
if ( getCurrentView ( ) === VIEWS . landing ) {
if ( e . key === 'ArrowUp' ) {
document . getElementById ( 'newsButton' ) . click ( )
}
}
}
} )
2018-05-07 01:45:20 +00:00
/ * *
* Display a news article on the UI .
*
* @ param { Object } articleObject The article meta object .
* @ param { number } index The article index .
* /
function displayArticle ( articleObject , index ) {
newsArticleTitle . innerHTML = articleObject . title
newsArticleTitle . href = articleObject . link
newsArticleAuthor . innerHTML = 'by ' + articleObject . author
newsArticleDate . innerHTML = articleObject . date
newsArticleComments . innerHTML = articleObject . comments
newsArticleComments . href = articleObject . commentsLink
2018-06-20 13:12:44 +00:00
newsArticleContentScrollable . innerHTML = '<div id="newsArticleContentWrapper"><div class="newsArticleSpacerTop"></div>' + articleObject . content + '<div class="newsArticleSpacerBot"></div></div>'
2018-09-05 06:30:26 +00:00
Array . from ( newsArticleContentScrollable . getElementsByClassName ( 'bbCodeSpoilerButton' ) ) . forEach ( v => {
v . onclick = ( ) => {
const text = v . parentElement . getElementsByClassName ( 'bbCodeSpoilerText' ) [ 0 ]
text . style . display = text . style . display === 'block' ? 'none' : 'block'
}
} )
2018-05-07 01:45:20 +00:00
newsNavigationStatus . innerHTML = index + ' of ' + newsArr . length
newsContent . setAttribute ( 'article' , index - 1 )
}
/ * *
* Load news information from the RSS feed specified in the
* distribution index .
* /
function loadNews ( ) {
return new Promise ( ( resolve , reject ) => {
2018-07-22 15:40:15 +00:00
const distroData = DistroManager . getDistribution ( )
const newsFeed = distroData . getRSS ( )
2018-05-09 00:10:46 +00:00
const newsHost = new URL ( newsFeed ) . origin + '/'
2020-03-18 23:54:46 +00:00
$ . ajax ( {
url : newsFeed ,
success : ( data ) => {
const items = $ ( data ) . find ( 'item' )
const articles = [ ]
for ( let i = 0 ; i < items . length ; i ++ ) {
// JQuery Element
const el = $ ( items [ i ] )
// Resolve date.
const date = new Date ( el . find ( 'pubDate' ) . text ( ) ) . toLocaleDateString ( 'en-US' , { month : 'short' , day : 'numeric' , year : 'numeric' , hour : 'numeric' , minute : 'numeric' } )
// Resolve comments.
let comments = el . find ( 'slash\\:comments' ) . text ( ) || '0'
comments = comments + ' Comment' + ( comments === '1' ? '' : 's' )
// Fix relative links in content.
let content = el . find ( 'content\\:encoded' ) . text ( )
let regex = /src="(?!http:\/\/|https:\/\/)(.+?)"/g
let matches
while ( ( matches = regex . exec ( content ) ) ) {
content = content . replace ( ` " ${ matches [ 1 ] } " ` , ` " ${ newsHost + matches [ 1 ] } " ` )
2018-07-22 17:31:15 +00:00
}
2020-03-18 23:54:46 +00:00
let link = el . find ( 'link' ) . text ( )
let title = el . find ( 'title' ) . text ( )
let author = el . find ( 'dc\\:creator' ) . text ( )
// Generate article.
articles . push (
{
link ,
title ,
date ,
author ,
content ,
comments ,
commentsLink : link + '#comments'
}
)
}
resolve ( {
articles
} )
} ,
timeout : 2500
} ) . catch ( err => {
2018-11-09 06:03:28 +00:00
resolve ( {
articles : null
2018-05-07 01:45:20 +00:00
} )
2018-11-09 06:03:28 +00:00
} )
2018-05-07 01:45:20 +00:00
} )
2021-07-19 15:08:55 +00:00
}