mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Completed installation procedure on wizard
This commit is contained in:
parent
92b1a46e8a
commit
15e8c9e791
110
manager/ejs/managerconnections.ejs
Normal file
110
manager/ejs/managerconnections.ejs
Normal file
@ -0,0 +1,110 @@
|
||||
<style>
|
||||
#manager-connections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 15px;
|
||||
}
|
||||
|
||||
#manager-connections .input-group {
|
||||
color: var(--offwhite);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-connections .input-group>span:nth-child(1) {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#manager-connections .input-group>span:nth-child(2) {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#manager-connections .input-group div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#manager-connections>.instructions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#manager-connections>.buttons-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#manager-connections .client-port input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#manager-connections .backend-port input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#manager-connections .success {
|
||||
content: url("./icons/check-solid-green.svg");
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#manager-connections .error img {
|
||||
content: url("./icons/triangle-exclamation-solid.svg");
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#manager-connections .error span {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
color: var(--red);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="manager-connections">
|
||||
<div class="page-header">
|
||||
Step 2: Port and address settings
|
||||
</div>
|
||||
<div class="input-group client-port">
|
||||
<span>Client port</span>
|
||||
<span>This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely.</span>
|
||||
<div>
|
||||
<input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
|
||||
<img class="success hide">
|
||||
<div class="error hide">
|
||||
<img> <span>Port already in use</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group backend-port">
|
||||
<span>Backend port</span>
|
||||
<span>This port is used to communicate with DCS. It is not necessary to allow this port through your firewall.</span>
|
||||
<div>
|
||||
<input type="number" min="1024" max="65535" value="<%= instance["backendPort"] %>">
|
||||
<img class="success hide">
|
||||
<div class="error hide">
|
||||
<img> <span>Port already in use</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group backend-address">
|
||||
<span>Backend address</span>
|
||||
<span>This is the backend address Olympus will listen on. Unless you know what you are doing, leave it as localhost, even for dedicated server installations.</span>
|
||||
<input type="text" value="<%= instance["backendAddress"] %>">
|
||||
</div>
|
||||
<div class="buttons-footer">
|
||||
<div class="button back">
|
||||
Back
|
||||
</div>
|
||||
<div class="button next">
|
||||
Next
|
||||
</div>
|
||||
<div class="button cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,15 +1,21 @@
|
||||
<style>
|
||||
#manager-installations {
|
||||
#manager-installations .scroll-container {
|
||||
height: 440px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#manager-installations .scrollable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#manager-installations>.option {
|
||||
#manager-installations .scrollable>.option {
|
||||
cursor: pointer;
|
||||
background-color: var(--darkgray);
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
height: 100px;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
@ -20,18 +26,74 @@
|
||||
border-left: 5px solid var(--blue);
|
||||
}
|
||||
|
||||
#manager-installations>.selected {
|
||||
#manager-installations .scrollable>.option * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option.selected {
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option .checkbox {
|
||||
padding-right: 20px;
|
||||
content: url("./icons/square-regular.svg");
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option.selected .checkbox {
|
||||
content: url("./icons/check-solid.svg");
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div>span:nth-child(1) {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div>span:nth-child(2) {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div>span:nth-child(2).installed {
|
||||
font-weight: 600;
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div>span:nth-child(2).error {
|
||||
font-weight: 600;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
#manager-installations .scrollable>.option>div>span:nth-child(3) {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#manager-installations>.instructions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#manager-installations>.buttons-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="manager-installations">
|
||||
<div class="page-header">
|
||||
Step 1: Select DCS Installations
|
||||
</div>
|
||||
<div class="instruction">
|
||||
<div class="instructions">
|
||||
<span>
|
||||
Select the copies of DCS you want to install Olympus to.
|
||||
Select the copy of DCS you want to install Olympus to.
|
||||
</span>
|
||||
<span>
|
||||
For most people, this is your main DCS installation.
|
||||
@ -40,10 +102,33 @@
|
||||
If you are running a dedicated server, you would also install Olympus to this DCS version.
|
||||
</span>
|
||||
</div>
|
||||
<%= instances[0] %>
|
||||
<% for (let i = 0; i < instances.length; i++) {%>
|
||||
<div class="option">
|
||||
<%= instances[i] %>
|
||||
<div class="scroll-container">
|
||||
<div class="scrollable">
|
||||
<% for (let i = 0; i < instances.length; i++) {%>
|
||||
<div class="option" data-folder="<%= instances[i].folder %>">
|
||||
<img class="checkbox">
|
||||
<div>
|
||||
<span><%= instances[i].name %></span>
|
||||
<span class="<%= instances[i].installed? (instances[i].error? 'error': 'installed'): '' %>">
|
||||
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': 'Olympus already installed'): 'Olympus not installed yet' %>
|
||||
</span>
|
||||
|
||||
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="buttons-footer">
|
||||
<div class="button back">
|
||||
Back
|
||||
</div>
|
||||
<div class="button next">
|
||||
Next
|
||||
</div>
|
||||
<div class="button cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
159
manager/ejs/managerinstances.ejs
Normal file
159
manager/ejs/managerinstances.ejs
Normal file
@ -0,0 +1,159 @@
|
||||
<style>
|
||||
#manager-instances .scroll-container {
|
||||
height: 440px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option {
|
||||
cursor: pointer;
|
||||
background-color: var(--darkgray);
|
||||
width: 100%;
|
||||
height: 230px;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding-left: 15px;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
border-left: 5px solid var(--blue);
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option.selected {
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option .checkbox {
|
||||
padding-right: 20px;
|
||||
content: url("./icons/square-regular.svg");
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option.selected .checkbox {
|
||||
content: url("./icons/check-solid.svg");
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div>span:nth-child(1) {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div>span:nth-child(2) {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div>span:nth-child(2).installed {
|
||||
font-weight: 600;
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div>span:nth-child(2).error {
|
||||
font-weight: 600;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
#manager-instances .scrollable>.option>div>span:nth-child(3) {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#manager-instances>.instructions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#manager-instances>.buttons-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#manager-instances .info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-instances .info>div:nth-child(1) {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#manager-instances .info>div:nth-child(2) {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="manager-instances">
|
||||
<div class="page-header">
|
||||
Step 1: Select DCS Installations
|
||||
</div>
|
||||
<div class="instructions">
|
||||
<span>
|
||||
Select the copy of DCS you want to install Olympus to.
|
||||
</span>
|
||||
<span>
|
||||
For most people, this is your main DCS installation.
|
||||
</span>
|
||||
<span>
|
||||
If you are running a dedicated server, you would also install Olympus to this DCS version.
|
||||
</span>
|
||||
</div>
|
||||
<div class="scroll-container">
|
||||
<div class="scrollable">
|
||||
<% for (let i = 0; i < instances.length; i++) {%>
|
||||
<div class="option" data-folder="<%= instances[i].folder %>">
|
||||
<div>
|
||||
<span><%= instances[i].name %></span>
|
||||
<span class="<%= instances[i].installed? (instances[i].error? 'error': ''): '' %>">
|
||||
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': ''): '' %>
|
||||
</span>
|
||||
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
|
||||
<div class="info">
|
||||
<div>Client port</div>
|
||||
<div> <%= instances[i].clientPort %> </div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div>Backend port</div>
|
||||
<div> <%= instances[i].backendPort %> </div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div>Backend address</div>
|
||||
<div> <%= instances[i].backendAddress %> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons-footer">
|
||||
<div class="button back">
|
||||
Back
|
||||
</div>
|
||||
<div class="button next">
|
||||
Next
|
||||
</div>
|
||||
<div class="button cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -16,6 +16,11 @@
|
||||
padding-left: 15px;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#manager-menu>.option * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.inverted {
|
||||
@ -28,13 +33,13 @@
|
||||
<div class="page-header">
|
||||
What do you want to do?
|
||||
</div>
|
||||
<div class="option">
|
||||
<div class="option install">
|
||||
Install Olympus
|
||||
</div>
|
||||
<div class="option">
|
||||
<div class="option update">
|
||||
Update/remove Olympus
|
||||
</div>
|
||||
<div class="inverted">
|
||||
<div class="option inverted manage">
|
||||
View and manage instances
|
||||
</div>
|
||||
</div>
|
||||
73
manager/ejs/managerpasswords.ejs
Normal file
73
manager/ejs/managerpasswords.ejs
Normal file
@ -0,0 +1,73 @@
|
||||
<style>
|
||||
#manager-passwords {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 15px;
|
||||
}
|
||||
|
||||
#manager-passwords .input-group {
|
||||
color: var(--offwhite);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-passwords .input-group>span:nth-child(1) {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#manager-passwords .input-group>span:nth-child(2) {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#manager-passwords>.instructions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#manager-passwords>.buttons-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#manager-passwords input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="manager-passwords">
|
||||
<div class="page-header">
|
||||
Step 3: Passwords
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span>Enter your passwords to access Olympus</span>
|
||||
<span>By using the passwords below, you can access different roles in Olympus.</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group game-master">
|
||||
<span>Game Master Password</span>
|
||||
<span>This password is used to access Olympus as Game Master with full priviledges.</span>
|
||||
<input type="password" minlength="8">
|
||||
</div>
|
||||
<div class="input-group blue-commander">
|
||||
<span>Blue Commander Password</span>
|
||||
<span>This password is used to access Olympus as blue coalition Commander.</span>
|
||||
<input type="password" minlength="8">
|
||||
</div>
|
||||
<div class="input-group red-commander">
|
||||
<span>Red Commander Password</span>
|
||||
<span>This password is used to access Olympus as red coalition Commander.</span>
|
||||
<input type="password" minlength="8">
|
||||
</div>
|
||||
<div class="buttons-footer">
|
||||
<div class="button back">
|
||||
Back
|
||||
</div>
|
||||
<div class="button next">
|
||||
Next
|
||||
</div>
|
||||
<div class="button cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
160
manager/ejs/managerresult.ejs
Normal file
160
manager/ejs/managerresult.ejs
Normal file
@ -0,0 +1,160 @@
|
||||
<style>
|
||||
#manager-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 15px;
|
||||
}
|
||||
|
||||
#manager-result img.success {
|
||||
content: url("./icons/check-solid-green.svg");
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#manager-result img.error {
|
||||
content: url("./icons/triangle-exclamation-solid.svg");
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#manager-result img.wait {
|
||||
content: url("./icons/spinner-solid.svg");
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg)
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
#manager-result .summary {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#manager-result .summary.success {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
#manager-result .summary.error {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
#manager-result .info {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--offwhite);
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--offwhite);
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
#manager-result>.result {
|
||||
cursor: pointer;
|
||||
background-color: var(--blue);
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding-left: 15px;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#manager-result>.result>img {
|
||||
margin-left: 5px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#manager-result>.result>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#manager-result>.result>div>span:nth-child(1) {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#manager-result>.result>div>span:nth-child(2) {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
<div id="manager-result">
|
||||
<div class="page-header">
|
||||
Please wait while Olympus is being installed
|
||||
</div>
|
||||
<div class="step hook">
|
||||
Installing hook scripts<img class="wait"><img class="success hide"><img class="error hide">
|
||||
</div>
|
||||
<div class="step mod">
|
||||
Installing mod folder<img class="wait"><img class="success hide"><img class="error hide">
|
||||
</div>
|
||||
<div class="step json">
|
||||
Installing configuration file<img class="wait"><img class="success hide"><img class="error hide">
|
||||
</div>
|
||||
<div class="step config">
|
||||
Applying configuration<img class="wait"><img class="success hide"><img class="error hide">
|
||||
</div>
|
||||
<div class="step shortcuts">
|
||||
Creating shortcuts<img class="wait"><img class="success hide"><img class="error hide">
|
||||
</div>
|
||||
|
||||
<div class="summary success hide">
|
||||
Olympus successfully installed in the following DCS instance
|
||||
</div>
|
||||
|
||||
<div class="summary error hide">
|
||||
An error has occurred while installing Olympus
|
||||
</div>
|
||||
|
||||
<div class="result">
|
||||
<img class="wait"><img class="success hide"><img class="error hide">
|
||||
<div>
|
||||
<span>
|
||||
<%= instance.name %>
|
||||
</span>
|
||||
<span><img src="./icons/folder-open-solid.svg">
|
||||
<%= instance.folder %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info success">
|
||||
You may now start DCS and use Olympus either with the shortcuts or the "View and manage Olympus" entry in the
|
||||
main menu
|
||||
</div>
|
||||
|
||||
<div class="info error">
|
||||
Please make sure DCS is not currently being executed
|
||||
</div>
|
||||
|
||||
<div class="buttons-footer">
|
||||
<div class="button back">
|
||||
Back to main menu
|
||||
</div>
|
||||
<div class="button cancel">
|
||||
Close
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
manager/icons/check-solid-green.svg
Normal file
1
manager/icons/check-solid-green.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#8bff63" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 449 B |
1
manager/icons/spinner-solid.svg
Normal file
1
manager/icons/spinner-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M304 48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zm0 416a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM48 304a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm464-48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM142.9 437A48 48 0 1 0 75 369.1 48 48 0 1 0 142.9 437zm0-294.2A48 48 0 1 0 75 75a48 48 0 1 0 67.9 67.9zM369.1 437A48 48 0 1 0 437 369.1 48 48 0 1 0 369.1 437z"/></svg>
|
||||
|
After Width: | Height: | Size: 609 B |
1
manager/icons/square-check-solid.svg
Normal file
1
manager/icons/square-check-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
|
||||
|
After Width: | Height: | Size: 529 B |
1
manager/icons/square-regular.svg
Normal file
1
manager/icons/square-regular.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M384 80c8.8 0 16 7.2 16 16V416c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16H384zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z"/></svg>
|
||||
|
After Width: | Height: | Size: 484 B |
1
manager/icons/triangle-exclamation-solid.svg
Normal file
1
manager/icons/triangle-exclamation-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#FF5858" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 584 B |
@ -29,6 +29,16 @@
|
||||
</div>
|
||||
<div id="main-div">
|
||||
|
||||
</div>
|
||||
<div id="grayout" class="hide"></div>
|
||||
<div id="popup" class="hide">
|
||||
<img src="./icons/triangle-exclamation-solid.svg">
|
||||
<div class="content">
|
||||
This is an example content
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="button close-popup"> Close </div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
|
||||
|
||||
180
manager/javascripts/dcsinstance.js
Normal file
180
manager/javascripts/dcsinstance.js
Normal file
@ -0,0 +1,180 @@
|
||||
var regedit = require('regedit')
|
||||
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
|
||||
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
const vi = require('win-version-info');
|
||||
const checkPort = require('./net')
|
||||
const dircompare = require('dir-compare');
|
||||
|
||||
class DCSInstance {
|
||||
static instances = null;
|
||||
|
||||
static async getInstances() {
|
||||
if (this.instances === null) {
|
||||
this.instances = await this.findInstances();
|
||||
}
|
||||
return this.instances;
|
||||
}
|
||||
|
||||
static async findInstances() {
|
||||
let promise = new Promise((res, rej) => {
|
||||
regedit.list(shellFoldersKey, function (err, result) {
|
||||
if (err) {
|
||||
rej(err);
|
||||
}
|
||||
else {
|
||||
if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined) {
|
||||
const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value'];
|
||||
const folders = fs.readdirSync(searchpath);
|
||||
var instances = [];
|
||||
|
||||
folders.forEach((folder) => {
|
||||
if (fs.existsSync(path.join(searchpath, folder, "Config", "appsettings.lua")) ||
|
||||
fs.existsSync(path.join(searchpath, folder, "Config", "serversettings.lua"))) {
|
||||
instances.push(new DCSInstance(path.join(searchpath, folder)));
|
||||
}
|
||||
})
|
||||
|
||||
res(instances);
|
||||
} else {
|
||||
console.error("An error occured while trying to fetch the location of the DCS instances.")
|
||||
rej("An error occured while trying to fetch the location of the DCS instances.");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
folder = "";
|
||||
name = "";
|
||||
clientPort = 3000;
|
||||
backendPort = 3001;
|
||||
backendAddress = "localhost";
|
||||
gameMasterPassword = "";
|
||||
blueCommanderPassword = "";
|
||||
redCommanderPassword = "";
|
||||
installed = false;
|
||||
error = false;
|
||||
|
||||
constructor(folder) {
|
||||
this.folder = folder;
|
||||
this.name = path.basename(folder);
|
||||
|
||||
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))){
|
||||
this.installed = true;
|
||||
const options = { compareContent: true };
|
||||
var err1 = true;
|
||||
var err2 = true;
|
||||
var res1;
|
||||
var res2;
|
||||
try {
|
||||
res1 = dircompare.compareSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), options);
|
||||
res2 = dircompare.compareSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), options);
|
||||
err1 = res1.differences !== 0;
|
||||
err2 = res2.differences !== 0;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (err1 || err2) {
|
||||
console.log(res1)
|
||||
console.log(res2)
|
||||
this.error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setClientPort(newPort) {
|
||||
if (await this.checkClientPort(newPort)) {
|
||||
console.log(`Instance ${this.folder} client port set to ${newPort}`)
|
||||
this.clientPort = newPort;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async setBackendPort(newPort) {
|
||||
if (await this.checkBackendPort(newPort)) {
|
||||
console.log(`Instance ${this.folder} client port set to ${newPort}`)
|
||||
this.backendPort = newPort;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
setBackendAddress(newAddress) {
|
||||
this.backendAddress = newAddress;
|
||||
}
|
||||
|
||||
setGameMasterPassword(newPassword) {
|
||||
this.gameMasterPassword = newPassword;
|
||||
}
|
||||
|
||||
setBlueCommanderPassword(newPassword) {
|
||||
this.blueCommanderPassword = newPassword;
|
||||
}
|
||||
|
||||
setRedCommanderPassword(newPassword) {
|
||||
this.redCommanderPassword = newPassword;
|
||||
}
|
||||
|
||||
async checkClientPort(port) {
|
||||
var promise = new Promise((res, rej) => {
|
||||
checkPort(port, async (portFree) => {
|
||||
if (portFree) {
|
||||
portFree = !(await DCSInstance.getInstances()).some((instance) => {
|
||||
if (instance !== this) {
|
||||
if (instance.clientPort === port || instance.backendPort === port) {
|
||||
console.log(`Port ${port} already selected by other instance`);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (instance.backendPort === port) {
|
||||
console.log(`Port ${port} equal to backend port`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log(`Port ${port} currently in use`);
|
||||
}
|
||||
res(portFree);
|
||||
})
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
|
||||
async checkBackendPort(port) {
|
||||
var promise = new Promise((res, rej) => {
|
||||
checkPort(port, async (portFree) => {
|
||||
if (portFree) {
|
||||
portFree = !(await DCSInstance.getInstances()).some((instance) => {
|
||||
if (instance !== this) {
|
||||
if (instance.clientPort === port || instance.backendPort === port) {
|
||||
console.log(`Port ${port} already selected by other instance`);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (instance.clientPort === port) {
|
||||
console.log(`Port ${port} equal to client port`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
} else {
|
||||
console.log(`Port ${port} currently in use`);
|
||||
}
|
||||
res(portFree);
|
||||
})
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DCSInstance;
|
||||
@ -1,115 +1,148 @@
|
||||
const sha256 = require('sha256')
|
||||
const createShortcut = require('create-desktop-shortcuts');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
export function installOlympus(folder) {
|
||||
console.log(`Installing Olympus in ${folder}`);
|
||||
try {
|
||||
fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true });
|
||||
fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"));
|
||||
fs.cpSync(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json"));
|
||||
if (createShortcut({
|
||||
async function fixInstances(instances) {
|
||||
var promise = new Promise((res, rej) => {
|
||||
var instancePromises = instances.map((instance) => {
|
||||
var instancePromise = new Promise((instanceRes, instanceErr) => {
|
||||
installMod(instance.folder)
|
||||
.then(() => installHooks(instance.folder))
|
||||
.then(() => installShortCuts(instance.folder, instance.name))
|
||||
.then(() => instanceRes(true), (err) => { instanceErr(err) })
|
||||
})
|
||||
return instancePromise;
|
||||
});
|
||||
console.log(instancePromises);
|
||||
Promise.all(instancePromises).then(() => res(true), (err) => { rej(err) });
|
||||
})
|
||||
console.log(promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function installMod(folder) {
|
||||
console.log(`Installing mod in ${folder}`)
|
||||
var promise = new Promise((res, rej) => {
|
||||
fs.cp(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }, (err) => {
|
||||
if (err) {
|
||||
console.log(`Error installing mod in ${folder}: ${err}`)
|
||||
rej(err);
|
||||
}
|
||||
else {
|
||||
console.log(`Mod succesfully installed in ${folder}`)
|
||||
res(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function installHooks(folder) {
|
||||
console.log(`Installing hooks in ${folder}`)
|
||||
var promise = new Promise((res, rej) => {
|
||||
fs.cp(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), (err) => {
|
||||
if (err) {
|
||||
console.log(`Error installing hooks in ${folder}: ${err}`)
|
||||
rej(err);
|
||||
}
|
||||
else {
|
||||
console.log(`Hooks succesfully installed in ${folder}`)
|
||||
res(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function installJSON(folder) {
|
||||
console.log(`Installing config in ${folder}`)
|
||||
var promise = new Promise((res, rej) => {
|
||||
fs.cp(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json"), (err) => {
|
||||
if (err) {
|
||||
console.log(`Error installing config in ${folder}: ${err}`)
|
||||
rej(err);
|
||||
}
|
||||
else {
|
||||
console.log(`Config succesfully installed in ${folder}`)
|
||||
res(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function applyConfiguration(folder, instance) {
|
||||
console.log(`Applying configuration to Olympus in ${folder}`);
|
||||
var promise = new Promise((res, rej) => {
|
||||
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
|
||||
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
|
||||
|
||||
config["client"]["port"] = instance.clientPort;
|
||||
config["server"]["port"] = instance.backendPort;
|
||||
config["server"]["address"] = instance.backendAddress;
|
||||
config["authentication"]["gameMasterPassword"] = sha256(instance.gameMasterPassword);
|
||||
config["authentication"]["blueCommanderPassword"] = sha256(instance.blueCommanderPassword);
|
||||
config["authentication"]["redCommanderPassword"] = sha256(instance.redCommanderPassword);
|
||||
|
||||
fs.writeFile(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4), (err) => {
|
||||
if (err) {
|
||||
console.log(`Error applying config in ${folder}: ${err}`)
|
||||
rej(err);
|
||||
}
|
||||
else {
|
||||
console.log(`Config succesfully applied in ${folder}`)
|
||||
res(true);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
rej("File does not exist")
|
||||
}
|
||||
res(true);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function installShortCuts(folder, name) {
|
||||
console.log(`Installing shortcuts for Olympus in ${folder}`);
|
||||
var promise = new Promise((res, rej) => {
|
||||
var res1 = createShortcut({
|
||||
windows: {
|
||||
filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'),
|
||||
outputPath: folder,
|
||||
name: "DCS Olympus Client",
|
||||
name: `DCS Olympus Client (${name})`,
|
||||
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
|
||||
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'),
|
||||
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
|
||||
}
|
||||
}) &&
|
||||
createShortcut({
|
||||
windows: {
|
||||
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
|
||||
outputPath: folder,
|
||||
name: "DCS Olympus Server",
|
||||
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
|
||||
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
|
||||
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
|
||||
}
|
||||
})) {
|
||||
console.log("Shorcuts created succesfully")
|
||||
});
|
||||
|
||||
var res2 = createShortcut({
|
||||
windows: {
|
||||
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
|
||||
outputPath: folder,
|
||||
name: `DCS Olympus Server (${name})`,
|
||||
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
|
||||
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
|
||||
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
|
||||
}
|
||||
});
|
||||
|
||||
if (res1 && res2) {
|
||||
res(true);
|
||||
} else {
|
||||
return false;
|
||||
rej("An error occurred while creating the shortcuts")
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
loadDivs();
|
||||
return true;
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
export function uninstallOlympus(folder) {
|
||||
console.log(`Uninstalling Olympus from ${folder}`);
|
||||
try {
|
||||
fs.rmSync(path.join(folder, "Mods", "Services", "Olympus"), { recursive: true, force: true });
|
||||
fs.rmSync(path.join(folder, "Config", "olympus.json"), {force: true});
|
||||
loadDivs();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
module.exports = {
|
||||
applyConfiguration: applyConfiguration,
|
||||
installJSON: installJSON,
|
||||
installHooks: installHooks,
|
||||
installMod: installMod,
|
||||
installShortCuts, installShortCuts,
|
||||
fixInstances: fixInstances
|
||||
}
|
||||
|
||||
export function applyConfiguration(folder, data) {
|
||||
console.log(`Applying configuration to Olympus from ${folder}`);
|
||||
|
||||
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
|
||||
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
|
||||
|
||||
config["client"]["port"] = data["clientPort"];
|
||||
config["server"]["port"] = data["backendPort"];
|
||||
config["server"]["address"] = data["backendAddress"];
|
||||
config["authentication"]["gameMasterPassword"] = sha256(data["gameMasterPassword"]);
|
||||
config["authentication"]["blueCommanderPassword"] = sha256(data["blueCommanderPassword"]);
|
||||
config["authentication"]["redCommanderPassword"] = sha256(data["redCommanderPassword"]);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function updateOlympus(folder) {
|
||||
console.log(`Updating Olympus in ${folder}`);
|
||||
try {
|
||||
fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true });
|
||||
fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hook", "OlympusHook.lua"));
|
||||
loadDivs();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createDesktopShortcuts(folder) {
|
||||
if (createShortcut({
|
||||
windows: {
|
||||
filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'),
|
||||
name: "DCS Olympus Client",
|
||||
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
|
||||
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'),
|
||||
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
|
||||
}
|
||||
}) && createShortcut({
|
||||
windows: {
|
||||
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
|
||||
name: "DCS Olympus Server",
|
||||
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
|
||||
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
|
||||
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
|
||||
}
|
||||
})) {
|
||||
showPopup("Shortcuts created successfully!")
|
||||
} else {
|
||||
showPopup("And error occurred while creating the shortcuts.")
|
||||
}
|
||||
}
|
||||
69
manager/javascripts/managerconnections.js
Normal file
69
manager/javascripts/managerconnections.js
Normal file
@ -0,0 +1,69 @@
|
||||
const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerConnections extends ManagerPage {
|
||||
onBackClicked;
|
||||
onNextClicked;
|
||||
onCancelClicked;
|
||||
instance;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
render(str) {
|
||||
const element = this.getElement();
|
||||
element.innerHTML = str;
|
||||
|
||||
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
|
||||
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
|
||||
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
|
||||
|
||||
this.element.querySelector(".client-port").querySelector("input").addEventListener("change", async (e) => { this.setClientPort(Number(e.target.value)); })
|
||||
this.element.querySelector(".backend-port").querySelector("input").addEventListener("change", async (e) => { this.setBackendPort(Number(e.target.value)); })
|
||||
this.element.querySelector(".backend-address").querySelector("input").addEventListener("change", async (e) => { this.instance.setBackendAddress(e.target.value); })
|
||||
}
|
||||
|
||||
show(instance) {
|
||||
this.instance = instance;
|
||||
this.options["instance"] = instance;
|
||||
|
||||
ejs.renderFile("./ejs/managerconnections.ejs", this.options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
this.setClientPort(this.instance.clientPort);
|
||||
this.setBackendPort(this.instance.backendPort);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
super.show();
|
||||
}
|
||||
|
||||
async setClientPort(newPort) {
|
||||
const success = await this.instance.setClientPort(newPort);
|
||||
var successEls = this.element.querySelector(".client-port").querySelectorAll(".success");
|
||||
for (let i = 0; i < successEls.length; i++) {
|
||||
successEls[i].classList.toggle("hide", !success);
|
||||
}
|
||||
var errorEls = this.element.querySelector(".client-port").querySelectorAll(".error");
|
||||
for (let i = 0; i < errorEls.length; i++) {
|
||||
errorEls[i].classList.toggle("hide", success);
|
||||
}
|
||||
}
|
||||
|
||||
async setBackendPort(newPort) {
|
||||
const success = await this.instance.setBackendPort(newPort);
|
||||
var successEls = this.element.querySelector(".backend-port").querySelectorAll(".success");
|
||||
for (let i = 0; i < successEls.length; i++) {
|
||||
successEls[i].classList.toggle("hide", !success);
|
||||
}
|
||||
var errorEls = this.element.querySelector(".backend-port").querySelectorAll(".error");
|
||||
for (let i = 0; i < errorEls.length; i++) {
|
||||
errorEls[i].classList.toggle("hide", success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManagerConnections;
|
||||
@ -2,30 +2,53 @@ const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerInstallations extends ManagerPage {
|
||||
onBackClicked;
|
||||
onNextClicked;
|
||||
onCancelClicked;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
ejs.renderFile("./ejs/managerinstallations.ejs", options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(str);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render(str) {
|
||||
const element = this.getElement();
|
||||
element.innerHTML = str;
|
||||
this.element.innerHTML = str;
|
||||
|
||||
var options = element.querySelectorAll(".option");
|
||||
var options = this.element.querySelectorAll(".option");
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
options[i].onclick = (e) => {this.onOptionClicked(e);}
|
||||
}
|
||||
|
||||
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
|
||||
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
|
||||
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
|
||||
}
|
||||
|
||||
onOptionClicked(e) {
|
||||
e.target.classList.toggle("selected")
|
||||
var options = this.element.querySelectorAll(".option");
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
options[i].classList.remove("selected");
|
||||
}
|
||||
|
||||
e.target.classList.add("selected");
|
||||
}
|
||||
|
||||
getSelectedInstance() {
|
||||
return this.options.instances.find((instance) => {
|
||||
const selected = this.element.querySelector(".selected");
|
||||
return selected? selected.dataset.folder === instance.folder: false;
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
ejs.renderFile("./ejs/managerinstallations.ejs", this.options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
super.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
55
manager/javascripts/managerinstances.js
Normal file
55
manager/javascripts/managerinstances.js
Normal file
@ -0,0 +1,55 @@
|
||||
const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerInstances extends ManagerPage {
|
||||
onBackClicked;
|
||||
onNextClicked;
|
||||
onCancelClicked;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
render(str) {
|
||||
this.element.innerHTML = str;
|
||||
|
||||
var options = this.element.querySelectorAll(".option");
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
options[i].onclick = (e) => {this.onOptionClicked(e);}
|
||||
}
|
||||
|
||||
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
|
||||
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
|
||||
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
|
||||
}
|
||||
|
||||
onOptionClicked(e) {
|
||||
var options = this.element.querySelectorAll(".option");
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
options[i].classList.remove("selected");
|
||||
}
|
||||
|
||||
e.target.classList.add("selected");
|
||||
}
|
||||
|
||||
getSelectedInstance() {
|
||||
return this.options.instances.find((instance) => {
|
||||
const selected = this.element.querySelector(".selected");
|
||||
return selected? selected.dataset.folder === instance.folder: false;
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
ejs.renderFile("./ejs/managerinstances.ejs", this.options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
super.show();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManagerInstances;
|
||||
@ -2,6 +2,10 @@ const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerMenu extends ManagerPage {
|
||||
onInstallClicked;
|
||||
onUpdateClicked;
|
||||
onManageClicked;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
@ -9,7 +13,7 @@ class ManagerMenu extends ManagerPage {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(str);
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -17,6 +21,10 @@ class ManagerMenu extends ManagerPage {
|
||||
render(str) {
|
||||
const element = this.getElement();
|
||||
element.innerHTML = str;
|
||||
|
||||
element.querySelector(".install").addEventListener("click", (e) => this.onInstallClicked(e));
|
||||
element.querySelector(".update").addEventListener("click", (e) => this.onUpdateClicked(e))
|
||||
element.querySelector(".manage").addEventListener("click", (e) => this.onManageClicked(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
class ManagerPage {
|
||||
element;
|
||||
options;
|
||||
|
||||
constructor(options) {
|
||||
this.options = options ?? {};
|
||||
this.element = document.createElement('div');
|
||||
this.element.classList.add("manager-page");
|
||||
this.element.classList.add("manager-page", "hide");
|
||||
}
|
||||
|
||||
getElement() {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
show() {
|
||||
this.element.classList.remove("hide");
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.element.classList.add("hide");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManagerPage;
|
||||
42
manager/javascripts/managerpasswords.js
Normal file
42
manager/javascripts/managerpasswords.js
Normal file
@ -0,0 +1,42 @@
|
||||
const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerPasswords extends ManagerPage {
|
||||
onBackClicked;
|
||||
onNextClicked;
|
||||
onCancelClicked;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
render(str) {
|
||||
const element = this.getElement();
|
||||
element.innerHTML = str;
|
||||
|
||||
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
|
||||
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
|
||||
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
|
||||
|
||||
this.element.querySelector(".game-master").querySelector("input").addEventListener("change", async (e) => { this.instance.setGameMasterPassword(e.target.value); })
|
||||
this.element.querySelector(".blue-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setBlueCommanderPassword(e.target.value); })
|
||||
this.element.querySelector(".red-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setRedCommanderPassword(e.target.value); })
|
||||
}
|
||||
|
||||
show(instance) {
|
||||
this.instance = instance;
|
||||
this.options["instance"] = instance;
|
||||
|
||||
ejs.renderFile("./ejs/managerpasswords.ejs", this.options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
super.show();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManagerPasswords;
|
||||
113
manager/javascripts/managerresult.js
Normal file
113
manager/javascripts/managerresult.js
Normal file
@ -0,0 +1,113 @@
|
||||
const { installMod, installHooks, installJSON, applyConfiguration, installShortCuts } = require("./filesystem");
|
||||
const ManagerPage = require("./managerpage");
|
||||
const ejs = require('ejs')
|
||||
|
||||
class ManagerResult extends ManagerPage {
|
||||
onBackClicked;
|
||||
onNextClicked;
|
||||
onCancelClicked;
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
render(str) {
|
||||
const element = this.getElement();
|
||||
element.innerHTML = str;
|
||||
|
||||
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
|
||||
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
|
||||
}
|
||||
|
||||
show(instance) {
|
||||
this.instance = instance;
|
||||
this.options["instance"] = instance;
|
||||
|
||||
ejs.renderFile("./ejs/managerresult.ejs", this.options, {}, (err, str) => {
|
||||
if (!err) {
|
||||
this.render(str);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
super.show();
|
||||
}
|
||||
|
||||
startInstallation() {
|
||||
installHooks(this.instance.folder).then(
|
||||
() => {
|
||||
this.applyStepSuccess(".hook");
|
||||
},
|
||||
(err) => {
|
||||
this.applyStepFailure(".hook");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
).then(() => installMod(this.instance.folder)).then(
|
||||
() => {
|
||||
this.applyStepSuccess(".mod");
|
||||
},
|
||||
(err) => {
|
||||
this.applyStepFailure(".mod");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
).then(() => installJSON(this.instance.folder)).then(
|
||||
() => {
|
||||
this.applyStepSuccess(".json");
|
||||
},
|
||||
(err) => {
|
||||
this.applyStepFailure(".json");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
).then(() => applyConfiguration(this.instance.folder, this.instance)).then(
|
||||
() => {
|
||||
this.applyStepSuccess(".config");
|
||||
},
|
||||
(err) => {
|
||||
this.applyStepFailure(".config");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
).then(() => installShortCuts(this.instance.folder, this.instance.name)).then(
|
||||
() => {
|
||||
this.applyStepSuccess(".shortcuts");
|
||||
},
|
||||
(err) => {
|
||||
this.applyStepFailure(".shortcuts");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
).then(
|
||||
() => {
|
||||
this.element.querySelector(".summary.success").classList.remove("hide");
|
||||
this.element.querySelector(".summary.error").classList.add("hide");
|
||||
this.element.querySelector(".info.success").classList.remove("hide");
|
||||
this.element.querySelector(".info.error").classList.add("hide");
|
||||
this.element.querySelector(".result .success").classList.remove("hide");
|
||||
this.element.querySelector(".result .error").classList.add("hide");
|
||||
this.element.querySelector(".result .wait").classList.add("hide");
|
||||
this.element.querySelector(".page-header").innerText = "Install successfull!";
|
||||
},
|
||||
() => {
|
||||
this.element.querySelector(".summary.success").classList.add("hide");
|
||||
this.element.querySelector(".summary.error").classList.remove("hide");
|
||||
this.element.querySelector(".result .success").classList.add("hide");
|
||||
this.element.querySelector(".result .error").classList.remove("hide");
|
||||
this.element.querySelector(".result .wait").classList.add("hide");
|
||||
this.element.querySelector(".page-header").innerText = "Install error!";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
applyStepSuccess(step) {
|
||||
this.element.querySelector(step).querySelector(".success").classList.remove("hide");
|
||||
this.element.querySelector(step).querySelector(".error").classList.add("hide");
|
||||
this.element.querySelector(step).querySelector(".wait").classList.add("hide");
|
||||
}
|
||||
|
||||
applyStepFailure(step) {
|
||||
this.element.querySelector(step).querySelector(".success").classList.add("hide");
|
||||
this.element.querySelector(step).querySelector(".error").classList.remove("hide");
|
||||
this.element.querySelector(step).querySelector(".wait").classList.add("hide");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManagerResult;
|
||||
@ -1,6 +1,6 @@
|
||||
const portfinder = require('portfinder')
|
||||
|
||||
export function checkPort(port, callback) {
|
||||
function checkPort(port, callback) {
|
||||
portfinder.getPort({ port: port, stopPort: port }, (err, res) => {
|
||||
if (err !== null) {
|
||||
console.error(`Port ${port} already in use`);
|
||||
@ -10,3 +10,5 @@ export function checkPort(port, callback) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = checkPort;
|
||||
|
||||
19
manager/javascripts/popup.js
Normal file
19
manager/javascripts/popup.js
Normal file
@ -0,0 +1,19 @@
|
||||
function showPopup(message, onCloseCallback) {
|
||||
document.getElementById("grayout").classList.remove("hide");
|
||||
document.getElementById("popup").classList.remove("hide");
|
||||
document.getElementById("popup").querySelector(".close-popup").addEventListener("click", (e) => {
|
||||
hidePopup();
|
||||
onCloseCallback();
|
||||
})
|
||||
document.getElementById("popup").querySelector(".content").innerText = message;
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
document.getElementById("grayout").classList.add("hide");
|
||||
document.getElementById("popup").classList.add("hide");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
showPopup: showPopup,
|
||||
hidePopup: hidePopup
|
||||
}
|
||||
@ -1,26 +1,22 @@
|
||||
var regedit = require('regedit')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
const ejs = require('ejs')
|
||||
|
||||
const contextBridge = require('electron').contextBridge;
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
const vi = require('win-version-info');
|
||||
const ManagerMenu = require("./managermenu");
|
||||
const ManagerMenu = require("./managermenu");
|
||||
const ManagerInstallations = require('./managerinstallations');
|
||||
|
||||
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
|
||||
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
|
||||
|
||||
var instanceDivs = [];
|
||||
const DCSInstance = require('./dcsinstance');
|
||||
const ManagerConnections = require('./managerconnections');
|
||||
const ManagerPasswords = require('./managerpasswords');
|
||||
const { showPopup } = require('./popup');
|
||||
const ManagerResult = require('./managerresult');
|
||||
const { fixInstances } = require('./filesystem');
|
||||
const ManagerInstances = require('./managerinstances');
|
||||
|
||||
/* White-listed channels. */
|
||||
const ipc = {
|
||||
'render': {
|
||||
/* From render to main. */
|
||||
'send': [
|
||||
'window:minimize',
|
||||
'window:minimize',
|
||||
'window:maximize',
|
||||
'window:restore',
|
||||
'window:close'
|
||||
@ -64,10 +60,153 @@ contextBridge.exposeInMainWorld(
|
||||
}
|
||||
);
|
||||
|
||||
var managerMenu = new ManagerMenu();
|
||||
var managerInstallations = new ManagerInstallations({instances: ["asd/asd1", "asd/asd2"]});
|
||||
var activeInstance;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
//document.body.appendChild(managerMenu.getElement());
|
||||
async function setup() {
|
||||
var instances = await DCSInstance.getInstances();
|
||||
|
||||
if (instances.some((instance) => {
|
||||
return instance.installed && instance.error;
|
||||
})) {
|
||||
showPopup("One or more Olympus instances are corrupted or need updating. Press Close to fix this.", async () => {
|
||||
fixInstances(instances.filter((instance) => {
|
||||
return instance.installed && instance.error;
|
||||
})).then(
|
||||
() => { location.reload() },
|
||||
() => { showPopup("An error occurred while trying to fix you installations. Please reinstall Olympus manually") }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
var managerMenu = new ManagerMenu();
|
||||
managerMenu.onInstallClicked = (e) => {
|
||||
managerMenu.hide();
|
||||
managerInstallations.show();
|
||||
}
|
||||
managerMenu.onUpdateClicked = (e) => {
|
||||
managerMenu.hide();
|
||||
managerInstances.show();
|
||||
}
|
||||
|
||||
/* Installations */
|
||||
var managerInstallations = new ManagerInstallations({ instances: instances });
|
||||
managerInstallations.onBackClicked = (e) => {
|
||||
managerInstallations.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
managerInstallations.onNextClicked = (e) => {
|
||||
activeInstance = managerInstallations.getSelectedInstance();
|
||||
if (activeInstance) {
|
||||
managerInstallations.hide();
|
||||
managerConnections.show(activeInstance);
|
||||
} else {
|
||||
showPopup("Please select the instance you want to install Olympus into.")
|
||||
}
|
||||
}
|
||||
managerInstallations.onCancelClicked = (e) => {
|
||||
managerInstallations.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
/* Instances */
|
||||
var managerInstances = new ManagerInstances({ instances: instances });
|
||||
managerInstances.onBackClicked = (e) => {
|
||||
managerInstances.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
managerInstances.onNextClicked = (e) => {
|
||||
activeInstance = managerInstances.getSelectedInstance();
|
||||
if (activeInstance) {
|
||||
managerInstances.hide();
|
||||
managerConnections.show(activeInstance);
|
||||
} else {
|
||||
showPopup("Please select the instance you want to manage.")
|
||||
}
|
||||
}
|
||||
managerInstances.onCancelClicked = (e) => {
|
||||
managerInstances.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
/* Connections */
|
||||
var managerConnections = new ManagerConnections();
|
||||
managerConnections.onBackClicked = (e) => {
|
||||
managerConnections.hide();
|
||||
managerInstallations.show();
|
||||
}
|
||||
managerConnections.onNextClicked = async (e) => {
|
||||
if (activeInstance) {
|
||||
if (await activeInstance.checkClientPort(activeInstance.clientPort) && await activeInstance.checkBackendPort(activeInstance.backendPort)) {
|
||||
managerConnections.hide();
|
||||
managerPasswords.show(activeInstance);
|
||||
} else {
|
||||
showPopup("Please make sure the selected ports are not already in use.")
|
||||
}
|
||||
} else {
|
||||
showPopup("An error has occurred, please restart the Olympus Manager.")
|
||||
}
|
||||
}
|
||||
|
||||
managerConnections.onCancelClicked = (e) => {
|
||||
managerConnections.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
/* Passwords */
|
||||
var managerPasswords = new ManagerPasswords();
|
||||
managerPasswords.onBackClicked = (e) => {
|
||||
if (activeInstance) {
|
||||
managerPasswords.hide();
|
||||
managerConnections.show(activeInstance);
|
||||
} else {
|
||||
showPopup("An error has occurred, please restart the Olympus Manager.")
|
||||
}
|
||||
}
|
||||
managerPasswords.onNextClicked = (e) => {
|
||||
if (activeInstance) {
|
||||
if (activeInstance.gameMasterPassword === "" || activeInstance.blueCommanderPassword === "" || activeInstance.redCommanderPassword === "") {
|
||||
showPopup("Please fill all the password inputs.")
|
||||
}
|
||||
else if (activeInstance.gameMasterPassword === activeInstance.blueCommanderPassword || activeInstance.blueCommanderPassword === activeInstance.redCommanderPassword || activeInstance.gameMasterPassword === activeInstance.redCommanderPassword) {
|
||||
showPopup("All the passwords must be different from each other.")
|
||||
} else {
|
||||
managerPasswords.hide();
|
||||
managerResult.show(activeInstance);
|
||||
managerResult.startInstallation();
|
||||
}
|
||||
} else {
|
||||
showPopup("An error has occurred, please restart the Olympus Manager.")
|
||||
}
|
||||
|
||||
}
|
||||
managerPasswords.onCancelClicked = (e) => {
|
||||
managerPasswords.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
/* Result */
|
||||
var managerResult = new ManagerResult();
|
||||
managerResult.onBackClicked = (e) => {
|
||||
managerResult.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
managerResult.onCancelClicked = (e) => {
|
||||
managerResult.hide();
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
document.body.appendChild(managerMenu.getElement());
|
||||
document.body.appendChild(managerInstallations.getElement());
|
||||
document.body.appendChild(managerInstances.getElement());
|
||||
document.body.appendChild(managerConnections.getElement());
|
||||
document.body.appendChild(managerPasswords.getElement());
|
||||
document.body.appendChild(managerResult.getElement());
|
||||
|
||||
managerMenu.show();
|
||||
}
|
||||
|
||||
/* On content loaded */
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
setup();
|
||||
})
|
||||
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"create-desktop-shortcuts": "^1.10.1",
|
||||
"create-windowless-app": "^11.0.0",
|
||||
"dir-compare": "^4.2.0",
|
||||
"ejs": "^3.1.9",
|
||||
"electron": "^28.0.0",
|
||||
"portfinder": "^1.0.32",
|
||||
|
||||
@ -123,7 +123,7 @@ body {
|
||||
}
|
||||
|
||||
.page-header {
|
||||
font-size: 14px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--offwhite);
|
||||
border-bottom: 1px solid var(--offwhite);
|
||||
@ -131,18 +131,109 @@ body {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.instruction {
|
||||
.instructions {
|
||||
color: var(--offwhite);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.instruction>span:first-child {
|
||||
.instructions>span:first-child {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.instruction>span:not(:first-child) {
|
||||
.instructions>span:not(:first-child) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.buttons-footer {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.back {
|
||||
color: var(--background);
|
||||
background-color: var(--offwhite);
|
||||
}
|
||||
|
||||
.next {
|
||||
color: var(--offwhite);
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
.cancel {
|
||||
padding: 10px 5px;
|
||||
color: var(--offwhite);
|
||||
}
|
||||
|
||||
.close-popup {
|
||||
color: var(--offwhite);
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
font-weight: 600;
|
||||
color: var(--background);
|
||||
font-size: 13px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#grayout {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
#popup {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
background-color: var(--background);
|
||||
border-radius: 5px;
|
||||
left: calc(50% - 150px);
|
||||
top: calc(50% - 100px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#popup img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#popup .content {
|
||||
color: var(--offwhite);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#popup .footer {
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
width: 100%;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user