Compare commits

...

22 Commits

Author SHA1 Message Date
7eadc721cf disable helios services checks 2022-08-10 04:58:36 +03:00
b477da8e58 disable old dynMap iframe 2022-08-10 04:58:18 +03:00
27765e89ef Merge branch 'save_commit' into branch 2022-08-07 21:45:13 +03:00
9e71cfbbb9 Map connection buttons (not done yet) 2022-08-07 21:39:31 +03:00
a079b8a23e Merge branch 'offline-login' into map-on-single-view 2022-08-07 20:08:18 +03:00
c5038dd4c1 Merge remote-tracking branch 'origin/offline-login' into offline-login 2022-08-07 20:05:44 +03:00
f774e9a67b SVG icon for offline login 2022-08-07 19:58:08 +03:00
31ab6f15dd disable checks for offline account 2022-08-07 18:56:13 +03:00
960214efdf Back cancel button on offline login view 2022-08-04 06:03:54 +03:00
f1bc1ab719 add 7zip depency for build in docker 2022-08-04 04:14:45 +03:00
30b3f804c9 Add view to dynmap 2022-08-02 06:05:49 +03:00
cd757112b3 change distribution json link 2022-08-02 06:05:09 +03:00
0dffb86577 Map integration 2022-07-31 22:37:34 +03:00
d2dec49dcc Add iframe to news container 2022-07-31 19:28:39 +03:00
b038b30ec6 Temporary loginOffline styles fixes 2022-07-31 11:07:31 +03:00
9c69a99f7e Add cancel button to loginOffline.ejs 2022-07-31 07:23:33 +03:00
1330e01484 working offline accounts 2022-07-31 07:15:50 +03:00
9d37b49c79 Add offline accounts container in settings template 2022-07-31 06:35:26 +03:00
1496696694 Merge remote-tracking branch 'origin/offline-login' into offline-login 2022-07-31 05:40:29 +03:00
b6a2e7ae8c Change hardlink to distribution.json 2022-07-31 05:40:20 +03:00
2bd2bc33a2 Initial landing changes 2022-07-30 23:11:16 +03:00
a4204d1f07 initial-offline login 2022-07-30 16:07:51 +03:00
20 changed files with 10049 additions and 9393 deletions

View File

@ -1,55 +1,57 @@
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-In6B8teKZQll5heMl9bS7CESTbGvuAt3VVV86BUQBDk='"/> <meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-In6B8teKZQll5heMl9bS7CESTbGvuAt3VVV86BUQBDk='"/>
<title>Helios Launcher</title> <title>Helios Launcher</title>
<script src="./assets/js/scripts/uicore.js"></script> <script src="./assets/js/scripts/uicore.js"></script>
<script src="./assets/js/scripts/uibinder.js"></script> <script src="./assets/js/scripts/uibinder.js"></script>
<link type="text/css" rel="stylesheet" href="./assets/css/launcher.css"> <link type="text/css" rel="stylesheet" href="./assets/css/launcher.css">
<style> <style>
body { body {
/*background: url('assets/images/backgrounds/<%=bkid%>.jpg') no-repeat center center fixed;*/ /*background: url('assets/images/backgrounds/<%=bkid%>.jpg') no-repeat center center fixed;*/
transition: background-image 1s ease; transition: background-image 1s ease;
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBkZWZhdWx0IHF1YWxpdHkK/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAPwBwAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A8VooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k='); background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBkZWZhdWx0IHF1YWxpdHkK/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAPwBwAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A8VooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=');
background-size: cover; background-size: cover;
-webkit-user-select: none; -webkit-user-select: none;
} }
#main { #main {
display: none; display: none;
height: calc(100% - 22px); height: calc(100% - 22px);
background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%); background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%);
width: 100%; width: 100%;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
} }
#main[overlay] { #main[overlay] {
filter: blur(3px) contrast(0.9) brightness(1.0); filter: blur(3px) contrast(0.9) brightness(1.0);
} }
</style> </style>
</head> </head>
<body bkid="<%=bkid%>"> <body bkid="<%=bkid%>">
<%- include('frame') %> <%- include('frame') %>
<div id="main"> <div id="main">
<%- include('welcome') %> <%- include('welcome') %>
<%- include('login') %> <%- include('login') %>
<%- include('waiting') %> <%- include('loginOffline') %>
<%- include('loginOptions') %> <%- include('waiting') %>
<%- include('settings') %> <%- include('loginOptions') %>
<%- include('landing') %> <%- include('settings') %>
</div> <%- include('landing') %>
<%- include('overlay') %> <%- include('dynmap') %>
<div id="loadingContainer"> </div>
<div id="loadingContent"> <%- include('overlay') %>
<div id="loadSpinnerContainer"> <div id="loadingContainer">
<img id="loadCenterImage" src="assets/images/LoadingSeal.png"> <div id="loadingContent">
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png"> <div id="loadSpinnerContainer">
</div> <img id="loadCenterImage" src="assets/images/LoadingSeal.png">
</div> <img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
</div> </div>
<script> </div>
// Load language </div>
for(let key of Object.keys(Lang.query('html'))){ <script>
document.getElementById(key).innerHTML = Lang.query(`html.${key}`) // Load language
} for(let key of Object.keys(Lang.query('html'))){
</script> document.getElementById(key).innerHTML = Lang.query(`html.${key}`)
</body> }
</script>
</body>
</html> </html>

View File

@ -450,6 +450,79 @@ body, button {
transition: 0.25s ease; transition: 0.25s ease;
} }
/* loginOffline cancel button styles. */
#loginOfflineCancelContainer {
position: absolute;
top: 5%;
right: 5%;
}
/* loginOffline cancel button styles. */
#loginOfflineCancelButton {
background: none;
border: none;
outline: none;
cursor: pointer;
transition: 0.25s ease;
}
#loginOfflineCancelButton:hover #loginOfflineCancelIcon,
#loginOfflineCancelButton:hover #loginOfflineCancelText,
#loginOfflineCancelButton:focus #loginOfflineCancelIcon,
#loginOfflineCancelButton:focus #loginOfflineCancelText {
text-shadow: 0px 0px 20px white;
}
#loginOfflineCancelButton:hover #loginOfflineCancelIcon,
#loginOfflineCancelButton:focus #loginOfflineCancelIcon {
box-shadow: 0px 0px 20px white;
}
#loginOfflineCancelButton:active #loginOfflineCancelIcon,
#loginOfflineCancelButton:active #loginOfflineCancelText {
text-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75);
color: rgba(255, 255, 255, 0.75);
border-color: rgba(255, 255, 255, 0.75);
}
#loginOfflineCancelButton:active #loginOfflineCancelIcon {
box-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75);
}
#loginOfflineCancelButton:disabled {
pointer-events: none;
}
#loginOfflineCancelButton:disabled #loginOfflineCancelIcon,
#loginOfflineCancelButton:disabled #loginOfflineCancelText {
color: rgba(255, 255, 255, 0.75);
border-color: rgba(255, 255, 255, 0.75);
}
/* The X in a circle icon for the cancel button. */
#loginOfflineCancelIcon {
border-radius: 50%;
border: 1px solid white;
box-sizing: border-box;
height: 30px;
width: 30px;
font-size: 19px;
line-height: 30px;
margin: 0 auto;
margin-bottom: 5px;
transition: 0.25s ease;
}
/* Text for the loginOffline cancel button. */
#loginOfflineCancelText {
font-size: 15px;
transition: 0.25s ease;
}
/* Login content wrapper. */ /* Login content wrapper. */
#loginContent { #loginContent {
display: flex; display: flex;
@ -498,7 +571,7 @@ body, button {
/* Header on login view. */ /* Header on login view. */
#loginSubheader { #loginSubheader {
font-family: 'Avenir Medium'; font-family: 'Avenir Medium';
margin-bottom: 25px; margin-bottom: 15px;
font-size: 12px; font-size: 12px;
letter-spacing: 1px; letter-spacing: 1px;
font-weight: bold; font-weight: bold;
@ -518,6 +591,7 @@ body, button {
fill: #fff; fill: #fff;
height: 20px; height: 20px;
width: 20px; width: 20px;
padding-bottom: 0.7em;
} }
/* Span which displays errors related to login field content. */ /* Span which displays errors related to login field content. */
@ -560,17 +634,19 @@ body, button {
.loginField { .loginField {
font-family: 'Avenir Book'; font-family: 'Avenir Book';
background: none; background: none;
border-width: 1.5px 0px 0px 0px; border-width: 1px 1px 1px 1px;
border-style: solid; border-style: solid;
border-radius: 5px;
backdrop-filter: blur(5px);
width: 250px; width: 250px;
margin-bottom: 20px; margin-bottom: 15px;
border-color: #fff; border-color: #3e3e3e;
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
padding: 7.5px; padding: 7.5px;
font-size: 10px; font-size: 14px;
letter-spacing: 1px; letter-spacing: 1px;
} }
.loginField:focus { .loginField:focus {
@ -674,6 +750,72 @@ body, button {
display: initial; display: initial;
} }
/* Login Offline button styles. */
#loginOfflineButton {
background: none;
font-weight: bold;
letter-spacing: 2px;
border: none;
padding: 15px 5px;
margin: 10px 0px;
cursor: pointer;
position: relative;
right: -20px;
transition: 0.5s ease;
}
#loginOfflineutton:disabled {
color: rgba(255, 255, 255, 0.75);
pointer-events: none;
}
#loginOfflineButton[loading] {
color: #fff;
}
#loginOfflineButton:hover,
#loginOfflineButton:focus {
text-shadow: 0px 0px 20px #fff;
outline: none;
}
#loginOfflineButton:active {
color: #c7c7c7;
text-shadow: 0px 0px 20px #c7c7c7;
}
#loginSVG {
-webkit-transform: translate3d(0, 0, 0);
overflow: visible;
transform: rotate(90deg);
margin-left: 20px;
transition: 0.25s ease;
width: 20px;
height: 20px;
}
#loginOfflineButton:hover #loginSVG,
#loginOfflineButton:focus #loginSVG {
-webkit-filter: drop-shadow(0px 0px 2px #fff);
}
#loginOfflineButton:active #loginSVG .arrowLine {
stroke: #c7c7c7;
}
#loginOfflineButton:active #loginSVG {
-webkit-filter: drop-shadow(0px 0px 2px #c7c7c7);
}
#loginOfflineButton:disabled #loginSVG .arrowLine {
stroke: rgba(255, 255, 255, 0.75);
}
#loginOfflineButtonContent {
display: flex;
align-items: center;
}
#loginOfflineButton .circle-loader,
#loginOfflineButton[loading] #loginSVG {
display: none;
}
#loginOfflineButton[loading] .circle-loader,
#loginOfflineButton #loginSVG {
display: initial;
}
.circle-loader { .circle-loader {
margin-left: 20px; margin-left: 20px;
@ -3964,4 +4106,53 @@ input:checked + .toggleSwitchSlider:before {
/* Class which is applied when the spinner image is spinning. */ /* Class which is applied when the spinner image is spinning. */
.rotating { .rotating {
animation: rotating 10s linear infinite; animation: rotating 10s linear infinite;
}
/*******************************************************************************
* *
* Custom patches *
* *
******************************************************************************/
/* iframe patch for full size window. */
#iframecontainer {
width: 100%;
}
#dynmapiframe {
width: 100%;
height: 100%;
user-select: none;
}
#mapsvg {
-webkit-filter: invert(100%); /* safari 6.0 - 9.0 */
filter: invert(100%);
transition: 0.25s ease;
}
/* Map icon hover effect */
#newsButton:hover #mapsvg,
#newsButton:focus #mapsvg {
filter:invert(100%) drop-shadow(0 0 0.75rem white);
}
/* Return from map button */
.FloatingButtonContainer {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 22em;
right: 0;
text-align: center;
}
/* Return from map button style */
#dynmapDoneButton {
background-color: #222222;
width: 100px;
border-radius: 0px 0px 10px 10px;
border-color: #333333;
} }

View File

@ -0,0 +1,298 @@
{
"version": "1.0.0",
"discord": {
"clientId": "385581240906022916",
"smallImageText": "WesterosCraft",
"smallImageKey": "seal-circle"
},
"java": {
"oracle": "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"
},
"rss": "https://westeroscraft.com/articles/index.rss",
"servers": [
{
"id": "SkirdaTesting-1.12.2",
"name": "Skirda Test Server",
"description": "Сплошные баги блять",
"icon": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/server-prod.png",
"version": "4.6.5",
"address": "192.168.88.228:35012",
"minecraftVersion": "1.12.2",
"discord": {
"shortId": "Skirda Minecraft Server",
"largeImageText": "Skirda 1.12.2 Minecraft Server",
"largeImageKey": "skirda-testing"
},
"mainServer": false,
"autoconnect": true,
"modules": [
{
"id": "net.minecraftforge:forge:1.12.2-14.23.5.2847",
"name": "Minecraft Forge",
"type": "ForgeHosted",
"artifact": {
"size": 4884700,
"MD5": "90734a5a713e24902d24c45c15caa42c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/forge-1.12.2-14.23.5.2847-universal.jar"
},
"subModules": [
{
"id": "net.minecraft:launchwrapper:1.12",
"name": "Mojang (LaunchWrapper)",
"type": "Library",
"artifact": {
"size": 32999,
"MD5": "934b2d91c7c5be4a49577c9e6b40e8da",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/launchwrapper-1.12.jar"
}
},
{
"id": "org.ow2.asm:asm-all:5.2",
"name": "Mojang (ASM)",
"type": "Library",
"artifact": {
"size": 247787,
"MD5": "f5ad16c7f0338b541978b0430d51dc83",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/asm-all-5.2.jar"
}
},
{
"id": "jline:jline:2.13",
"name": "Mojang (jline)",
"type": "Library",
"artifact": {
"size": 248566,
"MD5": "f251ba666cccb260ff7215b2cbeee8d4",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/jline-2.13.jar"
}
},
{
"id": "org.scala-lang:scala-library:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-library)",
"type": "Library",
"artifact": {
"size": 1474672,
"MD5": "379c15c4f724421c6d5d7aecedaf87a6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-library-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-compiler:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-compiler)",
"type": "Library",
"artifact": {
"size": 3076920,
"MD5": "7d89e952f2d5c74577310cd2c28e3f20",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-compiler-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-actors-migration_2.11:1.1.0@jar.pack.xz",
"name": "Minecraft Forge (scala-actors-migration)",
"type": "Library",
"artifact": {
"size": 21324,
"MD5": "04e3428b2600ace33c7ae2bf1f6c0a4c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-actors-migration_2.11-1.1.0.jar.pack.xz"
}
},
{
"id": "org.scala-lang.plugins:scala-continuations-library_2.11:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-continuations-library)",
"type": "Library",
"artifact": {
"size": 7956,
"MD5": "ed9b1d27aba8ac4090a3749c4dfc895a",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-continuations-library_2.11-1.0.2.jar.pack.xz"
}
},
{
"id": "org.scala-lang.plugins:scala-continuations-plugin_2.11.1:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-continuations-plugin)",
"type": "Library",
"artifact": {
"size": 46140,
"MD5": "a8232db22a72a981de6b1399eb86dff7",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-continuations-plugin_2.11.1-1.0.2.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-parser-combinators_2.11:1.0.1@jar.pack.xz",
"name": "Minecraft Forge (scala-parser-combinators)",
"type": "Library",
"artifact": {
"size": 85568,
"MD5": "2e50a7df17680daadacca69f07f8a16d",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-parser-combinators_2.11-1.0.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-reflect:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-reflect)",
"type": "Library",
"artifact": {
"size": 1070312,
"MD5": "84e5dc81c10e2bd74c579c9d0332fdd9",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-reflect-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-swing_2.11:1.0.1",
"name": "Minecraft Forge (scala-swing)",
"type": "Library",
"artifact": {
"size": 736795,
"MD5": "1d360289e697022a3f57abaad344b28f",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-swing_2.11-1.0.1.jar"
}
},
{
"id": "org.scala-lang:scala-xml_2.11:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-xml)",
"type": "Library",
"artifact": {
"size": 217812,
"MD5": "cc891b094a4c32dedc56bfefe9b072ff",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-xml_2.11-1.0.2.jar.pack.xz"
}
},
{
"id": "com.typesafe.akka:akka-actor_2.11:2.3.3@jar.pack.xz",
"name": "Minecraft Forge (akka-actor)",
"type": "Library",
"artifact": {
"size": 746612,
"MD5": "25cb22c3078e9fb3f7a861c912924862",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/akka-actor_2.11-2.3.3.jar.pack.xz"
}
},
{
"id": "com.typesafe:config:1.2.1@jar.pack.xz",
"name": "Minecraft Forge (typesafe-config)",
"type": "Library",
"artifact": {
"size": 56636,
"MD5": "10ec4ccabc4e68aac9cf87165ead5d7d",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/config-1.2.1.jar.pack.xz"
}
},
{
"id": "lzma:lzma:0.0.1",
"name": "Mojang (LZMA)",
"type": "Library",
"artifact": {
"size": 5762,
"MD5": "a3e3c3186e41c4a1a3027ba2bb23cdc6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/lzma-0.0.1.jar"
}
},
{
"id": "net.sf.trove4j:trove4j:3.0.3",
"name": "Trove4J",
"type": "Library",
"artifact": {
"size": 2523218,
"MD5": "8fc4d4e0129244f9fd39650c5f30feb2",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/trove4j-3.0.3.jar"
}
},
{
"id": "java3d:vecmath:1.5.2",
"name": "Vecmath",
"type": "Library",
"artifact": {
"size": 318956,
"MD5": "e5d2b7f46c4800a32f62ce75676a5710",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/vecmath-1.5.2.jar"
}
},
{
"id": "net.sf.jopt-simple:jopt-simple:5.0.3",
"name": "Jopt-simple",
"type": "Library",
"artifact": {
"size": 78175,
"MD5": "0a5ec84e23df9d7cfb4063bc55f2744c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/jopt-simple-5.0.3.jar"
}
},
{
"id": "org.apache.maven:maven-artifact:3.5.3",
"name": "maven-artifact",
"type": "Library",
"artifact": {
"size": 54961,
"MD5": "7741ebf29690ee7d9dde9cf4376347fc",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/maven-artifact-3.5.3.jar"
}
},
{
"id": "net.minecraftforge:MercuriusUpdater:1.12.2",
"name": "MercuriusUpdater",
"type": "Library",
"artifact": {
"size": 15098,
"MD5": "6eb9e61097bee3103a2fdc42746b76a4",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/MercuriusUpdater-1.12.2.jar"
}
}
]
},
{
"id": "net.optifine:optifine:1.12.2_HD_U_F5",
"name": "Optifine",
"type": "ForgeMod",
"artifact": {
"size": 2598821,
"MD5": "043ac1db6f7441ea4cf31bcb621aff0b",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/OptiFine.jar"
}
},
{
"id": "mezz:jei:1.12.2-4.14.3.242",
"name": "JustEnoughItems",
"type": "ForgeMod",
"artifact": {
"size": 620682,
"MD5": "ae6d0e6e873ef6c20f41097dc7fee8c6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/jei.jar"
}
},
{
"id": "xaeros:minimap:1.12.2-20.15.0",
"name": "XaerosMinimap",
"type": "ForgeMod",
"required": {
"value": false
},
"artifact": {
"size": 528849,
"MD5": "cc12cfe20febd1404345f5339e522cda",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/Xaeros_Minimap.jar"
}
},
{
"id": "options.txt",
"name": "Default Client Options",
"type": "File",
"artifact": {
"size": 1973,
"path": "options.txt",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/options-1.12.2.txt"
}
},
{
"id": "servers.dat",
"name": "Saved Client Servers",
"type": "File",
"artifact": {
"size": 84,
"MD5": "71d99e229d7d2b8d2a6423e46832a4b8",
"path": "servers.dat",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/servers.dat"
}
}
]
}
]
}

View File

@ -15,6 +15,7 @@ const { RestResponseStatus } = require('helios-core/common')
const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang') const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang')
const { MicrosoftAuth, microsoftErrorDisplayable, MicrosoftErrorCode } = require('helios-core/microsoft') const { MicrosoftAuth, microsoftErrorDisplayable, MicrosoftErrorCode } = require('helios-core/microsoft')
const { AZURE_CLIENT_ID } = require('./ipcconstants') const { AZURE_CLIENT_ID } = require('./ipcconstants')
const { async } = require('node-stream-zip')
const log = LoggerUtil.getLogger('AuthManager') const log = LoggerUtil.getLogger('AuthManager')
@ -57,6 +58,11 @@ exports.addMojangAccount = async function(username, password) {
} }
} }
exports.addOfflineAccount = async function(usernameOffline){
//TODO: check for forbidden symbols and lenght!!
ConfigManager.addOfflineAccount(usernameOffline)
}
const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 } const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 }
/** /**
@ -181,6 +187,17 @@ exports.removeMojangAccount = async function(uuid){
} }
} }
exports.removeOfflineAccount = async function(uuid){
try {
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
} catch (err){
log.error('Error while removing account', err)
return Promise.reject(err)
}
}
/** /**
* Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout * Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout
* through the ipc renderer. * through the ipc renderer.

View File

@ -1,3 +1,4 @@
const { uuid } = require('discord-rpc-patch/src/util')
const fs = require('fs-extra') const fs = require('fs-extra')
const os = require('os') const os = require('os')
const path = require('path') const path = require('path')
@ -353,6 +354,19 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
return config.authenticationDatabase[uuid] return config.authenticationDatabase[uuid]
} }
exports.addOfflineAccount = function(usernameOffline){
fake_uuid = usernameOffline
config.selectedAccount = fake_uuid
accessToken = ""
config.authenticationDatabase[fake_uuid] = {
type: 'offline',
accessToken,
username: usernameOffline.trim(),
uuid: fake_uuid.trim(),
displayName: usernameOffline.trim()
}
return config.authenticationDatabase[fake_uuid]
}
/** /**
* Update the tokens of an authenticated microsoft account. * Update the tokens of an authenticated microsoft account.
* *

View File

@ -537,7 +537,8 @@ exports.pullRemote = function(){
return exports.pullLocal() return exports.pullLocal()
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json' //const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
const distroURL = "https://skirda.gregbrzezinski.com/distribution.json"
//const distroURL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/' //const distroURL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/'
const opts = { const opts = {
url: distroURL, url: distroURL,

View File

@ -0,0 +1,3 @@
dynmapDoneButton.addEventListener('click', () => {
switchView(getCurrentView(), VIEWS.landing)
})

View File

@ -108,6 +108,7 @@ document.getElementById('launch_button').addEventListener('click', function(e){
} }
}) })
// Bind settings button // Bind settings button
document.getElementById('settingsMediaButton').onclick = (e) => { document.getElementById('settingsMediaButton').onclick = (e) => {
prepareSettings() prepareSettings()
@ -158,65 +159,65 @@ server_selection_button.onclick = (e) => {
} }
// Update Mojang Status Color // Update Mojang Status Color
const refreshMojangStatuses = async function(){ // const refreshMojangStatuses = async function(){
loggerLanding.log('Refreshing Mojang Statuses..') // loggerLanding.log('Refreshing Mojang Statuses..')
let status = 'grey' // let status = 'grey'
let tooltipEssentialHTML = '' // let tooltipEssentialHTML = ''
let tooltipNonEssentialHTML = '' // let tooltipNonEssentialHTML = ''
const response = await MojangRestAPI.status() // const response = await MojangRestAPI.status()
let statuses // let statuses
if(response.responseStatus === RestResponseStatus.SUCCESS) { // if(response.responseStatus === RestResponseStatus.SUCCESS) {
statuses = response.data // statuses = response.data
} else { // } else {
loggerLanding.warn('Unable to refresh Mojang service status.') // loggerLanding.warn('Unable to refresh Mojang service status.')
statuses = MojangRestAPI.getDefaultStatuses() // statuses = MojangRestAPI.getDefaultStatuses()
} // }
greenCount = 0 // greenCount = 0
greyCount = 0 // greyCount = 0
for(let i=0; i<statuses.length; i++){ // for(let i=0; i<statuses.length; i++){
const service = statuses[i] // const service = statuses[i]
if(service.essential){ // if(service.essential){
tooltipEssentialHTML += `<div class="mojangStatusContainer"> // tooltipEssentialHTML += `<div class="mojangStatusContainer">
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">&#8226;</span> // <span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">&#8226;</span>
<span class="mojangStatusName">${service.name}</span> // <span class="mojangStatusName">${service.name}</span>
</div>` // </div>`
} else { // } else {
tooltipNonEssentialHTML += `<div class="mojangStatusContainer"> // tooltipNonEssentialHTML += `<div class="mojangStatusContainer">
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">&#8226;</span> // <span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">&#8226;</span>
<span class="mojangStatusName">${service.name}</span> // <span class="mojangStatusName">${service.name}</span>
</div>` // </div>`
} // }
if(service.status === 'yellow' && status !== 'red'){ // if(service.status === 'yellow' && status !== 'red'){
status = 'yellow' // status = 'yellow'
} else if(service.status === 'red'){ // } else if(service.status === 'red'){
status = 'red' // status = 'red'
} else { // } else {
if(service.status === 'grey'){ // if(service.status === 'grey'){
++greyCount // ++greyCount
} // }
++greenCount // ++greenCount
} // }
} // }
if(greenCount === statuses.length){ // if(greenCount === statuses.length){
if(greyCount === statuses.length){ // if(greyCount === statuses.length){
status = 'grey' // status = 'grey'
} else { // } else {
status = 'green' // status = 'green'
} // }
} // }
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML // document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML // document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status) // document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status)
} // }
const refreshServerStatus = async function(fade = false){ const refreshServerStatus = async function(fade = false){
loggerLanding.log('Refreshing Server Status') loggerLanding.log('Refreshing Server Status')
@ -250,12 +251,12 @@ const refreshServerStatus = async function(fade = false){
} }
refreshMojangStatuses() //refreshMojangStatuses()
// Server Status is refreshed in uibinder.js on distributionIndexDone. // Server Status is refreshed in uibinder.js on distributionIndexDone.
// Set refresh rate to once every 5 minutes. // Set refresh rate to once every 5 minutes.
let mojangStatusListener = setInterval(() => refreshMojangStatuses(true), 300000) //let mojangStatusListener = setInterval(() => refreshMojangStatuses(true), 300000)
let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000) //let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000)
/** /**
* Shows an error overlay, toggles off the launch area. * Shows an error overlay, toggles off the launch area.
@ -325,7 +326,7 @@ function asyncSystemScan(mcVersion, launchAfter = true){
// Show this information to the user. // Show this information to the user.
setOverlayContent( setOverlayContent(
'No Compatible<br>Java Installation Found', 'No Compatible<br>Java Installation Found',
'In order to join WesterosCraft, you need a 64-bit installation of Java 8. Would you like us to install a copy?', 'In order to join Skirda, you need a 64-bit installation of Java 8. Would you like us to install a copy?',
'Install Java', 'Install Java',
'Install Manually' 'Install Manually'
) )
@ -484,12 +485,12 @@ function dlAsync(login = true){
// Login parameter is temporary for debug purposes. Allows testing the validation/downloads without // Login parameter is temporary for debug purposes. Allows testing the validation/downloads without
// launching the game. // launching the game.
if(login) { /* if(login) {
if(ConfigManager.getSelectedAccount() == null){ if(ConfigManager.getSelectedAccount() == null){
loggerLanding.error('You must be logged into an account.') loggerLanding.error('You must be logged into an account.')
return return
} }
} }*/
setLaunchDetails('Please wait..') setLaunchDetails('Please wait..')
toggleLaunchArea(true) toggleLaunchArea(true)
@ -808,6 +809,8 @@ function slide_(up){
//date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric'}) //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(29, 29, 29, 0.55)'
landingContainer.style.background = 'rgba(0, 0, 0, 0.50)' landingContainer.style.background = 'rgba(0, 0, 0, 0.50)'
//Frame bar dimming (map patch)
frameBar.style.background = 'rgba(0, 0, 0)'
setTimeout(() => { setTimeout(() => {
if(newsGlideCount === 1){ if(newsGlideCount === 1){
lCLCenter.style.transition = 'none' lCLCenter.style.transition = 'none'
@ -820,6 +823,8 @@ function slide_(up){
newsGlideCount-- newsGlideCount--
}, 2000) }, 2000)
landingContainer.style.background = null landingContainer.style.background = null
//Frame bar dimming revert (map patch)
frameBar.style.background = 'rgba(0, 0, 0, 0.50)'
lCLCenter.style.transition = null lCLCenter.style.transition = null
newsBtn.style.transition = null newsBtn.style.transition = null
newsContainer.style.top = '100%' newsContainer.style.top = '100%'
@ -833,8 +838,9 @@ function slide_(up){
// Bind news button. // Bind news button.
document.getElementById('newsButton').onclick = () => { document.getElementById('newsButton').onclick = () => {
switchView(getCurrentView(), VIEWS.dynmap, 500, 500)
// Toggle tabbing. // Toggle tabbing.
if(newsActive){ /*if(newsActive){
$('#landingContainer *').removeAttr('tabindex') $('#landingContainer *').removeAttr('tabindex')
$('#newsContainer *').attr('tabindex', '-1') $('#newsContainer *').attr('tabindex', '-1')
} else { } else {
@ -846,9 +852,9 @@ document.getElementById('newsButton').onclick = () => {
ConfigManager.setNewsCacheDismissed(true) ConfigManager.setNewsCacheDismissed(true)
ConfigManager.save() ConfigManager.save()
} }
} }*/
slide_(!newsActive) //slide_(!newsActive)
newsActive = !newsActive //newsActive = !newsActive
} }
// Array to store article meta. // Array to store article meta.
@ -862,42 +868,42 @@ let newsLoadingListener = null
* *
* @param {boolean} val True to set loading animation, otherwise false. * @param {boolean} val True to set loading animation, otherwise false.
*/ */
function setNewsLoading(val){ // function setNewsLoading(val){
if(val){ // if(val){
const nLStr = 'Checking for News' // const nLStr = 'Checking for News'
let dotStr = '..' // let dotStr = '..'
nELoadSpan.innerHTML = nLStr + dotStr // nELoadSpan.innerHTML = nLStr + dotStr
newsLoadingListener = setInterval(() => { // newsLoadingListener = setInterval(() => {
if(dotStr.length >= 3){ // if(dotStr.length >= 3){
dotStr = '' // dotStr = ''
} else { // } else {
dotStr += '.' // dotStr += '.'
} // }
nELoadSpan.innerHTML = nLStr + dotStr // nELoadSpan.innerHTML = nLStr + dotStr
}, 750) // }, 750)
} else { // } else {
if(newsLoadingListener != null){ // if(newsLoadingListener != null){
clearInterval(newsLoadingListener) // clearInterval(newsLoadingListener)
newsLoadingListener = null // newsLoadingListener = null
} // }
} // }
} // }
// Bind retry button. // Bind retry button.
newsErrorRetry.onclick = () => { // newsErrorRetry.onclick = () => {
$('#newsErrorFailed').fadeOut(250, () => { // $('#newsErrorFailed').fadeOut(250, () => {
initNews() // initNews()
$('#newsErrorLoading').fadeIn(250) // $('#newsErrorLoading').fadeIn(250)
}) // })
} // }
newsArticleContentScrollable.onscroll = (e) => { // newsArticleContentScrollable.onscroll = (e) => {
if(e.target.scrollTop > Number.parseFloat($('.newsArticleSpacerTop').css('height'))){ // if(e.target.scrollTop > Number.parseFloat($('.newsArticleSpacerTop').css('height'))){
newsContent.setAttribute('scrolled', '') // newsContent.setAttribute('scrolled', '')
} else { // } else {
newsContent.removeAttribute('scrolled') // newsContent.removeAttribute('scrolled')
} // }
} // }
/** /**
* Reload the news without restarting. * Reload the news without restarting.
@ -933,107 +939,107 @@ function showNewsAlert(){
* @returns {Promise.<void>} A promise which resolves when the news * @returns {Promise.<void>} A promise which resolves when the news
* content has finished loading and transitioning. * content has finished loading and transitioning.
*/ */
function initNews(){ // function initNews(){
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
setNewsLoading(true) // setNewsLoading(true)
let news = {} // let news = {}
loadNews().then(news => { // loadNews().then(news => {
newsArr = news.articles || null // newsArr = news.articles || null
if(newsArr == null){ // if(newsArr == null){
// News Loading Failed // // News Loading Failed
setNewsLoading(false) // setNewsLoading(false)
$('#newsErrorLoading').fadeOut(250, () => { // $('#newsErrorLoading').fadeOut(250, () => {
$('#newsErrorFailed').fadeIn(250, () => { // $('#newsErrorFailed').fadeIn(250, () => {
resolve() // resolve()
}) // })
}) // })
} else if(newsArr.length === 0) { // } else if(newsArr.length === 0) {
// No News Articles // // No News Articles
setNewsLoading(false) // setNewsLoading(false)
ConfigManager.setNewsCache({ // ConfigManager.setNewsCache({
date: null, // date: null,
content: null, // content: null,
dismissed: false // dismissed: false
}) // })
ConfigManager.save() // ConfigManager.save()
$('#newsErrorLoading').fadeOut(250, () => { // $('#newsErrorLoading').fadeOut(250, () => {
$('#newsErrorNone').fadeIn(250, () => { // $('#newsErrorNone').fadeIn(250, () => {
resolve() // resolve()
}) // })
}) // })
} else { // } else {
// Success // // Success
setNewsLoading(false) // setNewsLoading(false)
const lN = newsArr[0] // const lN = newsArr[0]
const cached = ConfigManager.getNewsCache() // const cached = ConfigManager.getNewsCache()
let newHash = crypto.createHash('sha1').update(lN.content).digest('hex') // let newHash = crypto.createHash('sha1').update(lN.content).digest('hex')
let newDate = new Date(lN.date) // let newDate = new Date(lN.date)
let isNew = false // let isNew = false
if(cached.date != null && cached.content != null){ // if(cached.date != null && cached.content != null){
if(new Date(cached.date) >= newDate){ // if(new Date(cached.date) >= newDate){
// Compare Content // // Compare Content
if(cached.content !== newHash){ // if(cached.content !== newHash){
isNew = true // isNew = true
showNewsAlert() // showNewsAlert()
} else { // } else {
if(!cached.dismissed){ // if(!cached.dismissed){
isNew = true // isNew = true
showNewsAlert() // showNewsAlert()
} // }
} // }
} else { // } else {
isNew = true // isNew = true
showNewsAlert() // showNewsAlert()
} // }
} else { // } else {
isNew = true // isNew = true
showNewsAlert() // showNewsAlert()
} // }
if(isNew){ // if(isNew){
ConfigManager.setNewsCache({ // ConfigManager.setNewsCache({
date: newDate.getTime(), // date: newDate.getTime(),
content: newHash, // content: newHash,
dismissed: false // dismissed: false
}) // })
ConfigManager.save() // ConfigManager.save()
} // }
const switchHandler = (forward) => { // const switchHandler = (forward) => {
let cArt = parseInt(newsContent.getAttribute('article')) // let cArt = parseInt(newsContent.getAttribute('article'))
let nxtArt = forward ? (cArt >= newsArr.length-1 ? 0 : cArt + 1) : (cArt <= 0 ? newsArr.length-1 : cArt - 1) // let nxtArt = forward ? (cArt >= newsArr.length-1 ? 0 : cArt + 1) : (cArt <= 0 ? newsArr.length-1 : cArt - 1)
displayArticle(newsArr[nxtArt], nxtArt+1) // displayArticle(newsArr[nxtArt], nxtArt+1)
} // }
document.getElementById('newsNavigateRight').onclick = () => { switchHandler(true) } // document.getElementById('newsNavigateRight').onclick = () => { switchHandler(true) }
document.getElementById('newsNavigateLeft').onclick = () => { switchHandler(false) } // document.getElementById('newsNavigateLeft').onclick = () => { switchHandler(false) }
$('#newsErrorContainer').fadeOut(250, () => { // $('#newsErrorContainer').fadeOut(250, () => {
displayArticle(newsArr[0], 1) // displayArticle(newsArr[0], 1)
$('#newsContent').fadeIn(250, () => { // $('#newsContent').fadeIn(250, () => {
resolve() // resolve()
}) // })
}) // })
} // }
}) // })
}) // })
} // }
/** /**
* Add keyboard controls to the news UI. Left and right arrows toggle * Add keyboard controls to the news UI. Left and right arrows toggle

View File

@ -1,239 +1,239 @@
/** /**
* 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 = LoggerUtil1('%c[Login]', 'color: #000668; font-weight: bold') const loggerLogin = LoggerUtil1('%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
} }
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.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => { AuthManager.addMojangAccount(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((displayableError) => { }).catch((displayableError) => {
loginLoading(false) loginLoading(false)
let actualDisplayableError let actualDisplayableError
if(isDisplayableError(displayableError)) { if(isDisplayableError(displayableError)) {
msftLoginLogger.error('Error while logging in.', displayableError) msftLoginLogger.error('Error while logging in.', displayableError)
actualDisplayableError = displayableError actualDisplayableError = displayableError
} else { } else {
// Uh oh. // Uh oh.
msftLoginLogger.error('Unhandled error during login.', displayableError) msftLoginLogger.error('Unhandled error during login.', displayableError)
actualDisplayableError = { actualDisplayableError = {
title: 'Unknown Error During Login', title: 'Unknown Error During Login',
desc: 'An unknown error has occurred. Please see the console for details.' desc: 'An unknown error has occurred. Please see the console for details.'
} }
} }
setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain')) setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain'))
setOverlayHandler(() => { setOverlayHandler(() => {
formDisabled(false) formDisabled(false)
toggleOverlay(false) toggleOverlay(false)
}) })
toggleOverlay(true) toggleOverlay(true)
}) })
}) })

View File

@ -0,0 +1,39 @@
const loggerOfflineLogin = LoggerUtil1('%c[LoginOffline]', 'color: #000668; font-weight: bold')
const loginOfflineButton = document.getElementById('loginOfflineButton')
const loginOfflineCancelContainer = document.getElementById('loginOfflineCancelContainer')
const loginOfflineCancelButton = document.getElementById('loginOfflineCancelButton')
function loginOfflineDisabled(v){
if(loginOfflineButton.disabled !== v){
loginOfflineButton.disabled = v
}
}
function formOfflineDisabled(v){
loginOfflineDisabled(v)
loginOfflineUsername.disabled = v
}
loginOfflineButton.addEventListener('click', () => {
formOfflineDisabled(true)
AuthManager.addOfflineAccount(loginOfflineUsername.value).then((value) =>{
switchView(VIEWS.loginOffline, VIEWS.landing, 500, 500)
})
loginOfflineUsername = ''
})
loginOfflineCancelButton.addEventListener('click', () => {
if (Object.keys(ConfigManager.getAuthAccounts()).length === 0) {
loginOfflineUsername.value = ''
switchView(VIEWS.loginOffline, VIEWS.loginOptions, 500, 500)
}
else
{
loginOfflineUsername.value = ''
switchView(VIEWS.loginOffline, VIEWS.landing, 500, 500)
}
})

View File

@ -1,50 +1,45 @@
const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer') const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer')
const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft') const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft')
const loginOptionMojang = document.getElementById('loginOptionMojang') const loginOptionMojang = document.getElementById('loginOptionMojang')
const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton') const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton')
let loginOptionsCancellable = false let loginOptionsCancellable = true
let loginOptionsViewOnLoginSuccess let loginOptionsViewOnLoginSuccess
let loginOptionsViewOnLoginCancel let loginOptionsViewOnLoginCancel
let loginOptionsViewOnCancel let loginOptionsViewOnCancel
let loginOptionsViewCancelHandler let loginOptionsViewCancelHandler
function loginOptionsCancelEnabled(val){ function loginOptionsCancelEnabled(val){
if(val){ if(val){
$(loginOptionsCancelContainer).show() $(loginOptionsCancelContainer).show()
} else { } else {
$(loginOptionsCancelContainer).hide() $(loginOptionsCancelContainer).hide()
} }
} }
loginOptionMicrosoft.onclick = (e) => { loginOptionMicrosoft.onclick = (e) => {
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
ipcRenderer.send( ipcRenderer.send(
MSFT_OPCODE.OPEN_LOGIN, MSFT_OPCODE.OPEN_LOGIN,
loginOptionsViewOnLoginSuccess, loginOptionsViewOnLoginSuccess,
loginOptionsViewOnLoginCancel loginOptionsViewOnLoginCancel
) )
}) })
} }
loginOptionMojang.onclick = (e) => { loginOptionMojang.onclick = (e) => {
switchView(getCurrentView(), VIEWS.login, 500, 500, () => { switchView(getCurrentView(), VIEWS.login, 500, 500, () => {
loginViewOnSuccess = loginOptionsViewOnLoginSuccess loginViewOnSuccess = loginOptionsViewOnLoginSuccess
loginViewOnCancel = loginOptionsViewOnLoginCancel loginViewOnCancel = loginOptionsViewOnLoginCancel
loginCancelEnabled(true) loginCancelEnabled(true)
}) })
} }
loginOptionsCancelButton.onclick = (e) => { loginOptionOffline.onclick = (e) => {
switchView(getCurrentView(), loginOptionsViewOnCancel, 500, 500, () => { switchView(getCurrentView(), VIEWS.loginOffline, 500, 500, () => {
// Clear login values (Mojang login) loginViewOnSuccess = loginOptionsViewOnLoginSuccess
// No cleanup needed for Microsoft. loginViewOnCancel = loginOptionsViewOnLoginCancel
loginUsername.value = '' loginCancelEnabled(true)
loginPassword.value = '' })
if(loginOptionsViewCancelHandler != null){ }
loginOptionsViewCancelHandler()
loginOptionsViewCancelHandler = null
}
})
}

View File

@ -331,6 +331,14 @@ document.getElementById('settingsAddMojangAccount').onclick = (e) => {
}) })
} }
document.getElementById('settingsAddOfflineAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.loginOffline, 500, 500, () => {
loginViewOnCancel = VIEWS.settings
loginViewOnSuccess = VIEWS.settings
loginCancelEnabled(true)
})
}
// Bind the add microsoft account button. // Bind the add microsoft account button.
document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => { document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
@ -501,6 +509,24 @@ function processLogOut(val, isLastAccount){
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount) ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount)
}) })
} if(targetAcc.type === 'offline') {
AuthManager.removeOfflineAccount(uuid).then(() => {
if(!isLastAccount && uuid === prevSelAcc.uuid){
const selAcc = ConfigManager.getSelectedAccount()
refreshAuthAccountSelected(selAcc.uuid)
updateSelectedAccount(selAcc)
//validateSelectedAccount()
}
if(isLastAccount) {
loginOptionsCancelEnabled(false)
loginOptionsViewOnLoginSuccess = VIEWS.settings
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
switchView(getCurrentView(), VIEWS.loginOptions)
}
})
$(parent).fadeOut(250, () => {
parent.remove()
})
} else { } else {
AuthManager.removeMojangAccount(uuid).then(() => { AuthManager.removeMojangAccount(uuid).then(() => {
if(!isLastAccount && uuid === prevSelAcc.uuid){ if(!isLastAccount && uuid === prevSelAcc.uuid){
@ -603,6 +629,7 @@ function refreshAuthAccountSelected(uuid){
const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts') const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts')
const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts') const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts')
const settingsCurrentOfflineAccounts = document.getElementById('settingsCurrentOfflineAccounts')
/** /**
* Add auth account elements for each one stored in the authentication database. * Add auth account elements for each one stored in the authentication database.
@ -617,6 +644,7 @@ function populateAuthAccounts(){
let microsoftAuthAccountStr = '' let microsoftAuthAccountStr = ''
let mojangAuthAccountStr = '' let mojangAuthAccountStr = ''
let offlineAuthAccountStr = ''
authKeys.forEach((val) => { authKeys.forEach((val) => {
const acc = authAccounts[val] const acc = authAccounts[val]
@ -647,6 +675,8 @@ function populateAuthAccounts(){
if(acc.type === 'microsoft') { if(acc.type === 'microsoft') {
microsoftAuthAccountStr += accHtml microsoftAuthAccountStr += accHtml
} if (acc.type === 'offline') {
offlineAuthAccountStr += accHtml
} else { } else {
mojangAuthAccountStr += accHtml mojangAuthAccountStr += accHtml
} }
@ -655,6 +685,7 @@ function populateAuthAccounts(){
settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr
settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr
settingsCurrentOfflineAccounts.innerHTML = offlineAuthAccountStr
} }
/** /**

View File

@ -1,450 +1,455 @@
/** /**
* 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',
loginOptions: '#loginOptionsContainer', loginOptions: '#loginOptionsContainer',
login: '#loginContainer', login: '#loginContainer',
settings: '#settingsContainer', loginOffline: '#loginOfflineContainer',
welcome: '#welcomeContainer', settings: '#settingsContainer',
waiting: '#waitingContainer' welcome: '#welcomeContainer',
} waiting: '#waitingContainer',
dynmap: '#dynmapContainer'
// The currently shown view container. }
let currentView
// The currently shown view container.
/** let currentView
* Switch launcher views.
* /**
* @param {string} current The ID of the current view container. * Switch launcher views.
* @param {*} next The ID of the next view container. *
* @param {*} currentFadeTime Optional. The fade out time for the current view. * @param {string} current The ID of the current view container.
* @param {*} nextFadeTime Optional. The fade in time for the next view. * @param {*} next The ID of the next view container.
* @param {*} onCurrentFade Optional. Callback function to execute when the current * @param {*} currentFadeTime Optional. The fade out time for the current view.
* view fades out. * @param {*} nextFadeTime Optional. The fade in time for the next view.
* @param {*} onNextFade Optional. Callback function to execute when the next view * @param {*} onCurrentFade Optional. Callback function to execute when the current
* fades in. * view fades out.
*/ * @param {*} onNextFade Optional. Callback function to execute when the next view
function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){ * fades in.
currentView = next */
$(`${current}`).fadeOut(currentFadeTime, () => { function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){
onCurrentFade() currentView = next
$(`${next}`).fadeIn(nextFadeTime, () => { $(`${current}`).fadeOut(currentFadeTime, () => {
onNextFade() onCurrentFade()
}) $(`${next}`).fadeIn(nextFadeTime, () => {
}) onNextFade()
} })
})
/** }
* Get the currently shown view container.
* /**
* @returns {string} The currently shown view container. * Get the currently shown view container.
*/ *
function getCurrentView(){ * @returns {string} The currently shown view container.
return currentView */
} function getCurrentView(){
return currentView
function showMainUI(data){ }
if(!isDev){ function showMainUI(data){
loggerAutoUpdater.log('Initializing..')
ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease()) if(!isDev){
} loggerAutoUpdater.log('Initializing..')
ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease())
prepareSettings(true) }
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
refreshServerStatus() prepareSettings(true)
setTimeout(() => { updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)' refreshServerStatus()
document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')` setTimeout(() => {
$('#main').show() document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')`
const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0 $('#main').show()
// If this is enabled in a development environment we'll get ratelimited. const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0
// The relaunch frequency is usually far too high.
if(!isDev && isLoggedIn){ // If this is enabled in a development environment we'll get ratelimited.
validateSelectedAccount() // The relaunch frequency is usually far too high.
} //if(!isDev && isLoggedIn){
if(isLoggedIn){
if(ConfigManager.isFirstLaunch()){ validateSelectedAccount()
currentView = VIEWS.welcome }
$(VIEWS.welcome).fadeIn(1000)
} else { if(ConfigManager.isFirstLaunch()){
if(isLoggedIn){ currentView = VIEWS.welcome
currentView = VIEWS.landing $(VIEWS.welcome).fadeIn(1000)
$(VIEWS.landing).fadeIn(1000) } else {
} else { if(isLoggedIn){
loginOptionsCancelEnabled(false) currentView = VIEWS.landing
loginOptionsViewOnLoginSuccess = VIEWS.landing $(VIEWS.landing).fadeIn(1000)
loginOptionsViewOnLoginCancel = VIEWS.loginOptions } else {
currentView = VIEWS.loginOptions loginOptionsCancelEnabled(false)
$(VIEWS.loginOptions).fadeIn(1000) loginOptionsViewOnLoginSuccess = VIEWS.landing
} loginOptionsViewOnLoginCancel = VIEWS.loginOptions
} currentView = VIEWS.loginOptions
$(VIEWS.loginOptions).fadeIn(1000)
setTimeout(() => { }
$('#loadingContainer').fadeOut(500, () => { }
$('#loadSpinnerImage').removeClass('rotating')
}) setTimeout(() => {
}, 250) $('#loadingContainer').fadeOut(500, () => {
$('#loadSpinnerImage').removeClass('rotating')
}, 750) })
// Disable tabbing to the news container. }, 250)
initNews().then(() => {
$('#newsContainer *').attr('tabindex', '-1') }, 750)
}) // Disable tabbing to the news container.
} // initNews().then(() => {
// $('#newsContainer *').attr('tabindex', '-1')
function showFatalStartupError(){ // })
setTimeout(() => { }
$('#loadingContainer').fadeOut(250, () => {
document.getElementById('overlayContainer').style.background = 'none' function showFatalStartupError(){
setOverlayContent( setTimeout(() => {
'Fatal Error: Unable to Load Distribution Index', $('#loadingContainer').fadeOut(250, () => {
'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.', document.getElementById('overlayContainer').style.background = 'none'
'Close' setOverlayContent(
) 'Fatal Error: Unable to Load Distribution Index',
setOverlayHandler(() => { '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.',
const window = remote.getCurrentWindow() 'Close'
window.close() )
}) setOverlayHandler(() => {
toggleOverlay(true) const window = remote.getCurrentWindow()
}) window.close()
}, 750) })
} toggleOverlay(true)
})
/** }, 750)
* Common functions to perform after refreshing the distro index. }
*
* @param {Object} data The distro index object. /**
*/ * Common functions to perform after refreshing the distro index.
function onDistroRefresh(data){ *
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer())) * @param {Object} data The distro index object.
refreshServerStatus() */
initNews() function onDistroRefresh(data){
syncModConfigurations(data) updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
} refreshServerStatus()
initNews()
/** syncModConfigurations(data)
* Sync the mod configurations with the distro index. }
*
* @param {Object} data The distro index object. /**
*/ * Sync the mod configurations with the distro index.
function syncModConfigurations(data){ *
* @param {Object} data The distro index object.
const syncedCfgs = [] */
function syncModConfigurations(data){
for(let serv of data.getServers()){
const syncedCfgs = []
const id = serv.getID()
const mdls = serv.getModules() for(let serv of data.getServers()){
const cfg = ConfigManager.getModConfiguration(id)
const id = serv.getID()
if(cfg != null){ const mdls = serv.getModules()
const cfg = ConfigManager.getModConfiguration(id)
const modsOld = cfg.mods
const mods = {} if(cfg != null){
for(let mdl of mdls){ const modsOld = cfg.mods
const type = mdl.getType() const mods = {}
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ for(let mdl of mdls){
if(!mdl.getRequired().isRequired()){ const type = mdl.getType()
const mdlID = mdl.getVersionlessID()
if(modsOld[mdlID] == null){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl) if(!mdl.getRequired().isRequired()){
} else { const mdlID = mdl.getVersionlessID()
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false) if(modsOld[mdlID] == null){
} mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl)
} else { } else {
if(mdl.hasSubModules()){ mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false)
const mdlID = mdl.getVersionlessID() }
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) } else {
if(typeof v === 'object'){ if(mdl.hasSubModules()){
if(modsOld[mdlID] == null){ const mdlID = mdl.getVersionlessID()
mods[mdlID] = v const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
} else { if(typeof v === 'object'){
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true) if(modsOld[mdlID] == null){
} mods[mdlID] = v
} } else {
} mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true)
} }
} }
} }
}
syncedCfgs.push({ }
id, }
mods
}) syncedCfgs.push({
id,
} else { mods
})
const mods = {}
} else {
for(let mdl of mdls){
const type = mdl.getType() const mods = {}
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(!mdl.getRequired().isRequired()){ for(let mdl of mdls){
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) const type = mdl.getType()
} else { if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(mdl.hasSubModules()){ if(!mdl.getRequired().isRequired()){
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(typeof v === 'object'){ } else {
mods[mdl.getVersionlessID()] = v if(mdl.hasSubModules()){
} const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
} if(typeof v === 'object'){
} mods[mdl.getVersionlessID()] = v
} }
} }
}
syncedCfgs.push({ }
id, }
mods
}) syncedCfgs.push({
id,
} mods
} })
ConfigManager.setModConfigurations(syncedCfgs) }
ConfigManager.save() }
}
ConfigManager.setModConfigurations(syncedCfgs)
/** ConfigManager.save()
* Recursively scan for optional sub modules. If none are found, }
* this function returns a boolean. If optional sub modules do exist,
* a recursive configuration object is returned. /**
* * Recursively scan for optional sub modules. If none are found,
* @returns {boolean | Object} The resolved mod configuration. * this function returns a boolean. If optional sub modules do exist,
*/ * a recursive configuration object is returned.
function scanOptionalSubModules(mdls, origin){ *
if(mdls != null){ * @returns {boolean | Object} The resolved mod configuration.
const mods = {} */
function scanOptionalSubModules(mdls, origin){
for(let mdl of mdls){ if(mdls != null){
const type = mdl.getType() const mods = {}
// Optional types.
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ for(let mdl of mdls){
// It is optional. const type = mdl.getType()
if(!mdl.getRequired().isRequired()){ // Optional types.
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
} else { // It is optional.
if(mdl.hasSubModules()){ if(!mdl.getRequired().isRequired()){
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(typeof v === 'object'){ } else {
mods[mdl.getVersionlessID()] = v if(mdl.hasSubModules()){
} const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
} if(typeof v === 'object'){
} mods[mdl.getVersionlessID()] = v
} }
} }
}
if(Object.keys(mods).length > 0){ }
const ret = { }
mods
} if(Object.keys(mods).length > 0){
if(!origin.getRequired().isRequired()){ const ret = {
ret.value = origin.getRequired().isDefault() mods
} }
return ret if(!origin.getRequired().isRequired()){
} ret.value = origin.getRequired().isDefault()
} }
return origin.getRequired().isDefault() return ret
} }
}
/** return origin.getRequired().isDefault()
* Recursively merge an old configuration into a new configuration. }
*
* @param {boolean | Object} o The old configuration value. /**
* @param {boolean | Object} n The new configuration value. * Recursively merge an old configuration into a new configuration.
* @param {boolean} nReq If the new value is a required mod. *
* * @param {boolean | Object} o The old configuration value.
* @returns {boolean | Object} The merged configuration. * @param {boolean | Object} n The new configuration value.
*/ * @param {boolean} nReq If the new value is a required mod.
function mergeModConfiguration(o, n, nReq = false){ *
if(typeof o === 'boolean'){ * @returns {boolean | Object} The merged configuration.
if(typeof n === 'boolean') return o */
else if(typeof n === 'object'){ function mergeModConfiguration(o, n, nReq = false){
if(!nReq){ if(typeof o === 'boolean'){
n.value = o if(typeof n === 'boolean') return o
} else if(typeof n === 'object'){
return n if(!nReq){
} n.value = o
} else if(typeof o === 'object'){ }
if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true return n
else if(typeof n === 'object'){ }
if(!nReq){ } else if(typeof o === 'object'){
n.value = typeof o.value !== 'undefined' ? o.value : true if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true
} else if(typeof n === 'object'){
if(!nReq){
const newMods = Object.keys(n.mods) n.value = typeof o.value !== 'undefined' ? o.value : true
for(let i=0; i<newMods.length; i++){ }
const mod = newMods[i] const newMods = Object.keys(n.mods)
if(o.mods[mod] != null){ for(let i=0; i<newMods.length; i++){
n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod])
} const mod = newMods[i]
} if(o.mods[mod] != null){
n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod])
return n }
} }
}
// If for some reason we haven't been able to merge, return n
// wipe the old value and use the new one. Just to be safe }
return n }
} // If for some reason we haven't been able to merge,
// wipe the old value and use the new one. Just to be safe
function refreshDistributionIndex(remote, onSuccess, onError){ return n
if(remote){ }
DistroManager.pullRemote()
.then(onSuccess) function refreshDistributionIndex(remote, onSuccess, onError){
.catch(onError) if(remote){
} else { DistroManager.pullRemote()
DistroManager.pullLocal() .then(onSuccess)
.then(onSuccess) .catch(onError)
.catch(onError) } else {
} DistroManager.pullLocal()
} .then(onSuccess)
.catch(onError)
async function validateSelectedAccount(){ }
const selectedAcc = ConfigManager.getSelectedAccount() }
if(selectedAcc != null){
const val = await AuthManager.validateSelected() async function validateSelectedAccount(){
if(!val){ const selectedAcc = ConfigManager.getSelectedAccount()
ConfigManager.removeAuthAccount(selectedAcc.uuid) if(selectedAcc != null && selectedAcc.type != 'offline'){
ConfigManager.save() const val = await AuthManager.validateSelected()
const accLen = Object.keys(ConfigManager.getAuthAccounts()).length if(!val){
setOverlayContent( ConfigManager.removeAuthAccount(selectedAcc.uuid)
'Failed to Refresh Login', ConfigManager.save()
`We were unable to refresh the login for <strong>${selectedAcc.displayName}</strong>. Please ${accLen > 0 ? 'select another account or ' : ''} login again.`, const accLen = Object.keys(ConfigManager.getAuthAccounts()).length
'Login', setOverlayContent(
'Select Another Account' '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.`,
setOverlayHandler(() => { 'Login',
'Select Another Account'
const isMicrosoft = selectedAcc.type === 'microsoft' )
setOverlayHandler(() => {
if(isMicrosoft) {
// Empty for now const isMicrosoft = selectedAcc.type === 'microsoft'
} else {
// Mojang if(isMicrosoft) {
// For convenience, pre-populate the username of the account. // Empty for now
document.getElementById('loginUsername').value = selectedAcc.username } else {
validateEmail(selectedAcc.username) // Mojang
} // For convenience, pre-populate the username of the account.
document.getElementById('loginUsername').value = selectedAcc.username
loginOptionsViewOnLoginSuccess = getCurrentView() validateEmail(selectedAcc.username)
loginOptionsViewOnLoginCancel = VIEWS.loginOptions }
if(accLen > 0) { loginOptionsViewOnLoginSuccess = getCurrentView()
loginOptionsViewOnCancel = getCurrentView() loginOptionsViewOnLoginCancel = VIEWS.loginOptions
loginOptionsViewCancelHandler = () => {
if(isMicrosoft) { if(accLen > 0) {
ConfigManager.addMicrosoftAuthAccount( loginOptionsViewOnCancel = getCurrentView()
selectedAcc.uuid, loginOptionsViewCancelHandler = () => {
selectedAcc.accessToken, if(isMicrosoft) {
selectedAcc.username, ConfigManager.addMicrosoftAuthAccount(
selectedAcc.expiresAt, selectedAcc.uuid,
selectedAcc.microsoft.access_token, selectedAcc.accessToken,
selectedAcc.microsoft.refresh_token, selectedAcc.username,
selectedAcc.microsoft.expires_at selectedAcc.expiresAt,
) selectedAcc.microsoft.access_token,
} else { selectedAcc.microsoft.refresh_token,
ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName) selectedAcc.microsoft.expires_at
} )
ConfigManager.save() } else {
validateSelectedAccount() ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
} }
loginOptionsCancelEnabled(true) ConfigManager.save()
} else { validateSelectedAccount()
loginOptionsCancelEnabled(false) }
} loginOptionsCancelEnabled(true)
toggleOverlay(false) } else {
switchView(getCurrentView(), VIEWS.loginOptions) loginOptionsCancelEnabled(false)
}) }
setDismissHandler(() => { toggleOverlay(false)
if(accLen > 1){ switchView(getCurrentView(), VIEWS.loginOptions)
prepareAccountSelectionList() })
$('#overlayContent').fadeOut(250, () => { setDismissHandler(() => {
bindOverlayKeys(true, 'accountSelectContent', true) if(accLen > 1){
$('#accountSelectContent').fadeIn(250) prepareAccountSelectionList()
}) $('#overlayContent').fadeOut(250, () => {
} else { bindOverlayKeys(true, 'accountSelectContent', true)
const accountsObj = ConfigManager.getAuthAccounts() $('#accountSelectContent').fadeIn(250)
const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v]) })
// This function validates the account switch. } else {
setSelectedAccount(accounts[0].uuid) const accountsObj = ConfigManager.getAuthAccounts()
toggleOverlay(false) const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v])
} // This function validates the account switch.
}) setSelectedAccount(accounts[0].uuid)
toggleOverlay(true, accLen > 0) toggleOverlay(false)
} else { }
return true })
} toggleOverlay(true, accLen > 0)
} else { } else {
return true return true
} }
} } else {
return true
/** }
* Temporary function to update the selected account along }
* with the relevent UI elements.
* /**
* @param {string} uuid The UUID of the account. * Temporary function to update the selected account along
*/ * with the relevent UI elements.
function setSelectedAccount(uuid){ *
const authAcc = ConfigManager.setSelectedAccount(uuid) * @param {string} uuid The UUID of the account.
ConfigManager.save() */
updateSelectedAccount(authAcc) function setSelectedAccount(uuid){
validateSelectedAccount() const authAcc = ConfigManager.setSelectedAccount(uuid)
} ConfigManager.save()
updateSelectedAccount(authAcc)
// Synchronous Listener if (ConfigManager.getAuthAccounts[uuid].type !== 'offline'){
document.addEventListener('readystatechange', function(){ validateSelectedAccount()
}
if (document.readyState === 'interactive' || document.readyState === 'complete'){ }
if(rscShouldLoad){
rscShouldLoad = false // Synchronous Listener
if(!fatalStartupError){ document.addEventListener('readystatechange', function(){
const data = DistroManager.getDistribution()
showMainUI(data) if (document.readyState === 'interactive' || document.readyState === 'complete'){
} else { if(rscShouldLoad){
showFatalStartupError() rscShouldLoad = false
} if(!fatalStartupError){
} const data = DistroManager.getDistribution()
} showMainUI(data)
} else {
}, false) showFatalStartupError()
}
// Actions that must be performed after the distribution index is downloaded. }
ipcRenderer.on('distributionIndexDone', (event, res) => { }
if(res) {
const data = DistroManager.getDistribution() }, false)
syncModConfigurations(data)
if(document.readyState === 'interactive' || document.readyState === 'complete'){ // Actions that must be performed after the distribution index is downloaded.
showMainUI(data) ipcRenderer.on('distributionIndexDone', (event, res) => {
} else { if(res) {
rscShouldLoad = true const data = DistroManager.getDistribution()
} syncModConfigurations(data)
} else { if(document.readyState === 'interactive' || document.readyState === 'complete'){
fatalStartupError = true showMainUI(data)
if(document.readyState === 'interactive' || document.readyState === 'complete'){ } else {
showFatalStartupError() rscShouldLoad = true
} else { }
rscShouldLoad = true } else {
} fatalStartupError = true
} if(document.readyState === 'interactive' || document.readyState === 'complete'){
}) showFatalStartupError()
} else {
rscShouldLoad = true
}
}
})

22
app/dynmap.ejs Normal file
View File

@ -0,0 +1,22 @@
<div id="dynmapContainer" style="display: none;">
<div class="FloatingButtonContainer" id="center">
<div class="bot_wrapper">
<div id="content">
<button id="dynmapDoneButton"> <!-- Rename all elements to dynmapEtc -->
<div id="newsButtonAlert" style="display: none;"></div>
<svg id="newsButtonSVG" viewBox="0 0 24.87 13.97">
<defs>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
&#10;<span id="newsButtonText">MAP</span>
</button>
</div>
</div>
</div>
<div id="iframecontainer">
<iframe id="dynmapiframe" src="https://minemap.gregbrzezinski.com" frameborder="0"></iframe>
</div>
<script src="./assets/js/scripts/dynmap.js"></script>
</div>

View File

@ -26,15 +26,24 @@
<div id="settingsTooltip">Settings</div> <div id="settingsTooltip">Settings</div>
</button> </button>
</div> </div>
<!-- map integration button
<div class="mediaContainer" id="mapMediaContainer">
<button class="mediaButton" id="mapMediaButton">
<svg id="settingsSVG" class="mediaSVG" viewBox="0 0 141.36 137.43">
<path d="M70.70475616319865,83.36934004916053 a15.320781354859122,15.320781354859122 0 1 1 14.454501310561755,-15.296030496450625 A14.850515045097694,14.850515045097694 0 0 1 70.70475616319865,83.36934004916053 M123.25082856443602,55.425620905968366 h-12.375429204248078 A45.54157947163293,45.54157947163293 0 0 0 107.21227231573047,46.243052436416285 l8.613298726156664,-9.108315894326587 a9.727087354538993,9.727087354538993 0 0 0 0,-13.167456673319956 l-3.465120177189462,-3.6631270444574313 a8.489544434114185,8.489544434114185 0 0 0 -12.375429204248078,0 l-8.613298726156664,9.108315894326587 A40.442902639482725,40.442902639482725 0 0 0 81.99114759747292,25.427580514871032 V12.532383284044531 a9.108315894326587,9.108315894326587 0 0 0 -8.811305593424633,-9.306322761594556 h-4.950171681699231 a9.108315894326587,9.108315894326587 0 0 0 -8.811305593424633,9.306322761594556 v12.895197230826497 a40.17064319698927,40.17064319698927 0 0 0 -9.331073620003052,4.0591407789933704 l-8.613298726156664,-9.108315894326587 a8.489544434114185,8.489544434114185 0 0 0 -12.375429204248078,0 L25.58394128451018,23.967279868769744 a9.727087354538993,9.727087354538993 0 0 0 0,13.167456673319956 L34.19724001066683,46.243052436416285 a45.07131316187151,45.07131316187151 0 0 0 -3.6631270444574313,9.083565035918088 h-12.375429204248078 a9.083565035918088,9.083565035918088 0 0 0 -8.811305593424633,9.306322761594556 v5.197680265784193 a9.108315894326587,9.108315894326587 0 0 0 8.811305593424633,9.306322761594556 h11.979415469712139 a45.69008462208391,45.69008462208391 0 0 0 4.0591407789933704,10.642869115653347 l-8.613298726156664,9.108315894326587 a9.727087354538993,9.727087354538993 0 0 0 0,13.167456673319956 l3.465120177189462,3.6631270444574313 a8.489544434114185,8.489544434114185 0 0 0 12.375429204248078,0 l8.613298726156664,-9.108315894326587 a40.49240435629971,40.49240435629971 0 0 0 9.331073620003052,4.0591407789933704 v12.895197230826497 a9.083565035918088,9.083565035918088 0 0 0 8.811305593424633,9.306322761594556 h4.950171681699231 A9.083565035918088,9.083565035918088 0 0 0 81.99114759747292,123.68848839660077 V110.79329116577425 a40.78941465720167,40.78941465720167 0 0 0 9.331073620003052,-4.0591407789933704 l8.613298726156664,9.108315894326587 a8.489544434114185,8.489544434114185 0 0 0 12.375429204248078,0 l3.465120177189462,-3.6631270444574313 a9.727087354538993,9.727087354538993 0 0 0 0,-13.167456673319956 l-8.613298726156664,-9.108315894326587 a45.665333763675406,45.665333763675406 0 0 0 4.034389920584874,-10.642869115653347 h12.004166328120636 a9.108315894326587,9.108315894326587 0 0 0 8.811305593424633,-9.306322761594556 v-5.197680265784193 a9.083565035918088,9.083565035918088 0 0 0 -8.811305593424633,-9.306322761594556 " id="svg_3" class=""/>
</svg>
<div id="settingsTooltip">Map</div>
</button>
</div>
-->
</div> </div>
<div class="mediaDivider"></div> <div class="mediaDivider"></div>
<div id="externalMedia"> <div id="externalMedia">
<div class="mediaContainer"> <div class="mediaContainer">
<a href="https://github.com/dscalzi/HeliosLauncher" class="mediaURL" id="linkURL"> <a href="https://discord.gg/JwKaVJG" class="mediaURL" id="discordURL">
<svg id="linkSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500"> <svg id="discordSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g> <g>
<path d="M75.37,65.51a3.85,3.85,0,0,0-1.73.42,8.22,8.22,0,0,1,.94,3.76A8.36,8.36,0,0,1,66.23,78H46.37a8.35,8.35,0,1,1,0-16.7h9.18a21.51,21.51,0,0,1,6.65-8.72H46.37a17.07,17.07,0,1,0,0,34.15H66.23A17,17,0,0,0,82.77,65.51Z"/> <path d="M81.23,78.48a6.14,6.14,0,1,1,6.14-6.14,6.14,6.14,0,0,1-6.14,6.14M60,78.48a6.14,6.14,0,1,1,6.14-6.14A6.14,6.14,0,0,1,60,78.48M104.41,73c-.92-7.7-8.24-22.9-8.24-22.9A43,43,0,0,0,88,45.59a17.88,17.88,0,0,0-8.38-1.27l-.13,1.06a23.52,23.52,0,0,1,5.8,1.95,87.59,87.59,0,0,1,8.17,4.87s-10.32-5.63-22.27-5.63a51.32,51.32,0,0,0-23.2,5.63,87.84,87.84,0,0,1,8.17-4.87,23.57,23.57,0,0,1,5.8-1.95l-.13-1.06a17.88,17.88,0,0,0-8.38,1.27,42.84,42.84,0,0,0-8.21,4.56S37.87,65.35,37,73s-.37,11.54-.37,11.54,4.22,5.68,9.9,7.14,7.7,1.47,7.7,1.47l3.75-4.68a21.22,21.22,0,0,1-4.65-2A24.47,24.47,0,0,1,47.93,82S61.16,88.4,70.68,88.4c10,0,22.75-6.44,22.75-6.44a24.56,24.56,0,0,1-5.35,4.56,21.22,21.22,0,0,1-4.65,2l3.75,4.68s2,0,7.7-1.47,9.89-7.14,9.89-7.14.55-3.85-.37-11.54"/>
<path d="M66,73.88a3.85,3.85,0,0,0,1.73-.42,8.22,8.22,0,0,1-.94-3.76,8.36,8.36,0,0,1,8.35-8.35H95A8.35,8.35,0,1,1,95,78H85.8a21.51,21.51,0,0,1-6.65,8.72H95a17.07,17.07,0,0,0,0-34.15H75.13A17,17,0,0,0,58.59,73.88Z"/>
</g> </g>
</svg> </svg>
</a> </a>
@ -49,39 +58,11 @@
</a> </a>
</div> </div>
<div class="mediaContainer"> <div class="mediaContainer">
<a href="#" class="mediaURL" id="instagramURL" disabled> <a href="https://github.com/dscalzi/HeliosLauncher" class="mediaURL" id="linkURL">
<svg id="instagramSVG" class="mediaSVG" viewBox="0 0 5040 5040"> <svg id="linkSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<defs>
<radialGradient id="instaFill" cx="30%" cy="107%" r="150%">
<stop offset="0%" stop-color="#fdf497"/>
<stop offset="5%" stop-color="#fdf497"/>
<stop offset="45%" stop-color="#fd5949"/>
<stop offset="60%" stop-color="#d6249f"/>
<stop offset="90%" stop-color="#285AEB"/>
</radialGradient>
</defs>
<g> <g>
<path d="M1390 5024 c-163 -9 -239 -19 -315 -38 -281 -70 -477 -177 -660 -361 -184 -184 -292 -380 -361 -660 -43 -171 -53 -456 -53 -1445 0 -989 10 -1274 53 -1445 69 -280 177 -476 361 -660 184 -184 380 -292 660 -361 171 -43 456 -53 1445 -53 989 0 1274 10 1445 53 280 69 476 177 660 361 184 184 292 380 361 660 43 171 53 456 53 1445 0 989 -10 1274 -53 1445 -69 280 -177 476 -361 660 -184 184 -380 292 -660 361 -174 44 -454 53 -1470 52 -599 0 -960 -5 -1105 -14z m2230 -473 c58 -6 141 -18 185 -27 397 -78 638 -318 719 -714 37 -183 41 -309 41 -1290 0 -981 -4 -1107 -41 -1290 -81 -395 -319 -633 -714 -714 -183 -37 -309 -41 -1290 -41 -981 0 -1107 4 -1290 41 -397 81 -636 322 -714 719 -33 166 -38 296 -43 1100 -5 796 3 1203 27 1380 67 489 338 758 830 825 47 7 162 15 255 20 250 12 1907 4 2035 -9z"/> <path d="M75.37,65.51a3.85,3.85,0,0,0-1.73.42,8.22,8.22,0,0,1,.94,3.76A8.36,8.36,0,0,1,66.23,78H46.37a8.35,8.35,0,1,1,0-16.7h9.18a21.51,21.51,0,0,1,6.65-8.72H46.37a17.07,17.07,0,1,0,0,34.15H66.23A17,17,0,0,0,82.77,65.51Z"/>
<path d="M2355 3819 c-307 -42 -561 -172 -780 -400 -244 -253 -359 -543 -359 -899 0 -361 116 -648 367 -907 262 -269 563 -397 937 -397 374 0 675 128 937 397 251 259 367 546 367 907 0 361 -116 648 -367 907 -197 203 -422 326 -690 378 -101 20 -317 27 -412 14z m400 -509 c275 -88 470 -284 557 -560 20 -65 23 -95 23 -230 0 -135 -3 -165 -23 -230 -88 -278 -284 -474 -562 -562 -65 -20 -95 -23 -230 -23 -135 0 -165 3 -230 23 -278 88 -474 284 -562 562 -20 65 -23 95 -23 230 0 135 3 165 23 230 73 230 219 403 427 507 134 67 212 83 390 79 111 -3 155 -8 210 -26z"/> <path d="M66,73.88a3.85,3.85,0,0,0,1.73-.42,8.22,8.22,0,0,1-.94-3.76,8.36,8.36,0,0,1,8.35-8.35H95A8.35,8.35,0,1,1,95,78H85.8a21.51,21.51,0,0,1-6.65,8.72H95a17.07,17.07,0,0,0,0-34.15H75.13A17,17,0,0,0,58.59,73.88Z"/>
<path d="M3750 1473 c-29 -11 -66 -38 -106 -77 -70 -71 -94 -126 -94 -221 0 -95 24 -150 94 -221 72 -71 126 -94 225 -94 168 0 311 143 311 311 0 99 -23 154 -94 225 -43 42 -76 66 -110 77 -61 21 -166 21 -226 0z"/>
</g>
</svg>
</a>
</div>
<div class="mediaContainer">
<a href="#" class="mediaURL" id="youtubeURL" disabled>
<svg id="youtubeSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g>
<path d="M84.8,69.52,65.88,79.76V59.27Zm23.65.59c0-5.14-.79-17.63-3.94-20.57S99,45.86,73.37,45.86s-28,.73-31.14,3.68S38.29,65,38.29,70.11s.79,17.63,3.94,20.57,5.52,3.68,31.14,3.68,28-.74,31.14-3.68,3.94-15.42,3.94-20.57"/>
</g>
</svg>
</a>
</div>
<div class="mediaContainer">
<a href="https://discord.gg/zNWUXdt" class="mediaURL" id="discordURL">
<svg id="discordSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g>
<path d="M81.23,78.48a6.14,6.14,0,1,1,6.14-6.14,6.14,6.14,0,0,1-6.14,6.14M60,78.48a6.14,6.14,0,1,1,6.14-6.14A6.14,6.14,0,0,1,60,78.48M104.41,73c-.92-7.7-8.24-22.9-8.24-22.9A43,43,0,0,0,88,45.59a17.88,17.88,0,0,0-8.38-1.27l-.13,1.06a23.52,23.52,0,0,1,5.8,1.95,87.59,87.59,0,0,1,8.17,4.87s-10.32-5.63-22.27-5.63a51.32,51.32,0,0,0-23.2,5.63,87.84,87.84,0,0,1,8.17-4.87,23.57,23.57,0,0,1,5.8-1.95l-.13-1.06a17.88,17.88,0,0,0-8.38,1.27,42.84,42.84,0,0,0-8.21,4.56S37.87,65.35,37,73s-.37,11.54-.37,11.54,4.22,5.68,9.9,7.14,7.7,1.47,7.7,1.47l3.75-4.68a21.22,21.22,0,0,1-4.65-2A24.47,24.47,0,0,1,47.93,82S61.16,88.4,70.68,88.4c10,0,22.75-6.44,22.75-6.44a24.56,24.56,0,0,1-5.35,4.56,21.22,21.22,0,0,1-4.65,2l3.75,4.68s2,0,7.7-1.47,9.89-7.14,9.89-7.14.55-3.85-.37-11.54"/>
</g> </g>
</svg> </svg>
</a> </a>
@ -125,18 +106,13 @@
<div class="bot_wrapper"> <div class="bot_wrapper">
<div id="content"> <div id="content">
<button id="newsButton"> <button id="newsButton">
<!--<img src="assets/images/icons/arrow.svg" id="newsButtonSVG"/>-->
<div id="newsButtonAlert" style="display: none;"></div> <div id="newsButtonAlert" style="display: none;"></div>
<svg id="newsButtonSVG" viewBox="0 0 24.87 13.97"> <svg xmlns="http://www.w3.org/2000/svg" id="mapsvg" viewBox="0 0 24 24" width="20" height="20"><path d="M12,0A12,12,0,1,0,24,12,12.013,12.013,0,0,0,12,0ZM22,12a9.938,9.938,0,0,1-1.662,5.508l-1.192-1.193A.5.5,0,0,1,19,15.962V15a3,3,0,0,0-3-3H13a1,1,0,0,1-1-1v-.5a.5.5,0,0,1,.5-.5A2.5,2.5,0,0,0,15,7.5v-1a.5.5,0,0,1,.5-.5h1.379a2.516,2.516,0,0,0,1.767-.732l.377-.377A9.969,9.969,0,0,1,22,12Zm-19.951.963,3.158,3.158A2.978,2.978,0,0,0,7.329,17H10a1,1,0,0,1,1,1v3.949A10.016,10.016,0,0,1,2.049,12.963ZM13,21.949V18a3,3,0,0,0-3-3H7.329a1,1,0,0,1-.708-.293L2.163,10.249A9.978,9.978,0,0,1,17.456,3.63l-.224.224A.507.507,0,0,1,16.879,4H15.5A2.5,2.5,0,0,0,13,6.5v1a.5.5,0,0,1-.5.5A2.5,2.5,0,0,0,10,10.5V11a3,3,0,0,0,3,3h3a1,1,0,0,1,1,1v.962a2.516,2.516,0,0,0,.732,1.767l1.337,1.337A9.971,9.971,0,0,1,13,21.949Z"/></svg>
<defs> &#10;<span id="newsButtonText">MAP</span>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
&#10;<span id="newsButtonText">NEWS</span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div id="right"> <div id="right">
<div class="bot_wrapper"> <div class="bot_wrapper">
@ -159,7 +135,10 @@
</div> </div>
</div> </div>
<div id="newsContainer"> <div id="newsContainer">
<div id="newsContent" article="-1" style="display: none;"> <div id="iframecontainer">
<!--<iframe id="dynmapiframe" src="https://minemap.gregbrzezinski.com" frameborder="0"></iframe>-->
</div>
<!-- <div id="newsContent" article="-1" style="display: none;">
<div id="newsStatusContainer"> <div id="newsStatusContainer">
<div id="newsStatusContent"> <div id="newsStatusContent">
<div id="newsTitleContainer"> <div id="newsTitleContainer">
@ -198,7 +177,7 @@
<div id="newsArticleContainer"> <div id="newsArticleContainer">
<div id="newsArticleContent"> <div id="newsArticleContent">
<div id="newsArticleContentScrollable"> <div id="newsArticleContentScrollable">
<!-- Article Content -->
</div> </div>
</div> </div>
</div> </div>
@ -214,7 +193,7 @@
<div id="newsErrorNone" style="display: none;"> <div id="newsErrorNone" style="display: none;">
<span id="nENoneSpan" class="newsErrorContent">No News</span> <span id="nENoneSpan" class="newsErrorContent">No News</span>
</div> </div>
</div> </div>-->
</div> </div>
<script src="./assets/js/scripts/landing.js"></script> <script src="./assets/js/scripts/landing.js"></script>
</div> </div>

31
app/loginOffline.ejs Normal file
View File

@ -0,0 +1,31 @@
<div id="loginOfflineContainer" style="display: none;">
<div id="loginOfflineCancelContainer" > <!--Delete style="" as temp fix-->
<button id="loginOfflineCancelButton">
<div id="loginOfflineCancelIcon">X</div>
<span id="loginOfflineCancelText">Cancel</span>
</button>
</div>
<div id="loginContent">
<form id="loginForm" style="margin-top: 2.5em;">
<span id="loginSubheader">OFFLINE LOGIN</span>
<div class="loginFieldContainer">
<input id="loginOfflineUsername" class="loginField" type="text" placeholder="USERNAME"/>
</div>
<button id="loginOfflineButton" class="loginButton" enabled>
<div id="loginOfflineButtonContent">
LOGIN
<svg id="loginSVG" viewBox="0 0 24.87 13.97">
<defs>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;transition: 0.25s ease;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
<div class="circle-loader">
<div class="checkmark draw"></div>
</div>
</div>
</button>
</form>
</div>
<script src="./assets/js/scripts/loginOffline.js"></script>
</div>

View File

@ -1,34 +1,39 @@
<div id="loginOptionsContainer" style="display: none;"> <div id="loginOptionsContainer" style="display: none;">
<div id="loginOptionsContent"> <div id="loginOptionsContent">
<div class="loginOptionsMainContent"> <div class="loginOptionsMainContent">
<h2>Login Options</h2> <h2>Login Options</h2>
<div class="loginOptionActions"> <div class="loginOptionActions">
<div class="loginOptionButtonContainer"> <div class="loginOptionButtonContainer">
<button id="loginOptionMicrosoft" class="loginOptionButton"> <button id="loginOptionMicrosoft" class="loginOptionButton">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 23 23"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 23 23">
<path fill="#f35325" d="M1 1h10v10H1z" /> <path fill="#f35325" d="M1 1h10v10H1z" />
<path fill="#81bc06" d="M12 1h10v10H12z" /> <path fill="#81bc06" d="M12 1h10v10H12z" />
<path fill="#05a6f0" d="M1 12h10v10H1z" /> <path fill="#05a6f0" d="M1 12h10v10H1z" />
<path fill="#ffba08" d="M12 12h10v10H12z" /> <path fill="#ffba08" d="M12 12h10v10H12z" />
</svg> </svg>
<span>Login with Microsoft</span> <span>Login with Microsoft</span>
</button> </button>
</div> </div>
<div class="loginOptionButtonContainer"> <div class="loginOptionButtonContainer">
<button id="loginOptionMojang" class="loginOptionButton"> <button id="loginOptionMojang" class="loginOptionButton">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667">
<path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" /> <path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" />
<path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" /> <path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" />
<path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" /> <path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" />
</svg> </svg>
<span>Login with Mojang</span> <span>Login with Mojang</span>
</button> </button>
</div> </div>
</div> <div class="loginOptionButtonContainer">
<div id="loginOptionCancelContainer" style="display: none;"> <button id="loginOptionOffline" class="loginOptionButton">
<button id="loginOptionCancelButton">Cancel</button> <span>Proceed Offline</span>
</div> </button>
</div> </div>
</div> </div>
<script src="./assets/js/scripts/loginOptions.js"></script> <!--<div id="loginOptionCancelContainer" style="display: none;">
<button id="loginOptionCancelButton">Cancel</button>
</div>-->
</div>
</div>
<script src="./assets/js/scripts/loginOptions.js"></script>
</div> </div>

View File

@ -68,6 +68,21 @@
<!-- Mojang auth accounts populated here. --> <!-- Mojang auth accounts populated here. -->
</div> </div>
</div> </div>
<div class="settingsAuthAccountTypeContainer">
<div class="settingsAuthAccountTypeHeader">
<div class="settingsAuthAccountTypeHeaderLeft">
<svg width="22px" height="22px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Home"><path d="M62,21v39c0,1.1-0.9,2-2,2H4c-1.1,0-2-0.9-2-2V21c0-0.7,0.4-1.4,1-1.7l28-17c0.6-0.4,1.4-0.4,2.1,0l28,17 C61.6,19.6,62,20.3,62,21z" fill="#3F51B5"/><g><path d="M44,42v18c0,1.1-0.9,2-2,2H22c-1.1,0-2-0.9-2-2V42c0-6.6,5.4-12,12-12S44,35.4,44,42z" fill="#FFC10A"/></g></g><g id="Camera"/><g id="Mail"/><g id="Print"/><g id="Save"/><g id="Folder"/><g id="Search"/><g id="User"/><g id="Pin"/><g id="Calendar"/><g id="Gallery"/><g id="time"/><g id="Pin_1_"/><g id="Setting"/><g id="Player"/><g id="Lock"/><g id="Trash_Can"/><g id="Notification"/><g id="Record"/><g id="Shopping_Bag"/></svg>
<span>Offline</span>
</div>
<div class="settingsAuthAccountTypeHeaderRight">
<button class="settingsAddAuthAccount" id="settingsAddOfflineAccount">+ Add Offline Account</button>
</div>
</div>
<div class="settingsCurrentAccounts" id="settingsCurrentOfflineAccounts">
<!-- Offline auth accounts populated here. -->
</div>
</div>
</div> </div>
<div id="settingsTabMinecraft" class="settingsTab" style="display: none;"> <div id="settingsTabMinecraft" class="settingsTab" style="display: none;">
<div class="settingsTabHeader"> <div class="settingsTabHeader">

16678
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,9 @@
"request": "^2.88.2", "request": "^2.88.2",
"semver": "^7.3.7", "semver": "^7.3.7",
"tar-fs": "^2.1.1", "tar-fs": "^2.1.1",
"winreg": "^1.2.4" "winreg": "^1.2.4",
"7zip-bin": "^5.2.0"
}, },
"devDependencies": { "devDependencies": {
"electron": "^19.0.10", "electron": "^19.0.10",