Extract login pages to own files
Fix api Remove login window
This commit is contained in:
parent
fe429ea7d5
commit
213bb1c179
@ -6,13 +6,25 @@ const apiPath = "/schafkopf"
|
||||
|
||||
var useEnglishTexts = false
|
||||
|
||||
const headerKeyPassword = "password";
|
||||
const headerKeyToken = "token";
|
||||
const headerKeyName = "name";
|
||||
const headerKeyMail = "email";
|
||||
|
||||
function webSocketPath() {
|
||||
const prefix = (window.location.protocol === "https:") ? "wss://" : "ws://"
|
||||
return prefix + window.location.host + apiPath + "/session/start"
|
||||
}
|
||||
|
||||
async function performRegisterPlayerRequest(name, password) {
|
||||
return fetch(apiPath + "/player/register/" + name, { method: 'POST', body: password })
|
||||
async function performRegisterPlayerRequest(name, password, email) {
|
||||
return fetch(apiPath + "/player/register", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
[headerKeyName]: name,
|
||||
[headerKeyPassword]: password,
|
||||
[headerKeyMail]: email
|
||||
}
|
||||
})
|
||||
.then(convertServerResponse)
|
||||
}
|
||||
|
||||
@ -44,6 +56,22 @@ async function performGetCurrentTableRequest(token) {
|
||||
.then(convertJsonResponse)
|
||||
}
|
||||
|
||||
async function performRecoveryEmailRequest(name) {
|
||||
return fetch(apiPath + "/player/password/reset", {
|
||||
method: 'POST',
|
||||
headers: { [headerKeyName] : name },
|
||||
})
|
||||
.then(convertServerResponse)
|
||||
}
|
||||
|
||||
async function performResetPasswordRequest(token, password) {
|
||||
return fetch(apiPath + "/player/reset", {
|
||||
method: 'POST',
|
||||
headers: { [headerKeyPassword] : password, [headerKeyToken] : token }
|
||||
})
|
||||
.then(convertServerResponse)
|
||||
}
|
||||
|
||||
async function performCreateTableRequest(token, name, visibility) {
|
||||
const vis = visibility ? "public" : "private";
|
||||
return fetch(apiPath + "/table/create/" + vis + "/" + name, { method: 'POST', body: token })
|
||||
|
@ -4,14 +4,11 @@
|
||||
|
||||
var playerName = ""
|
||||
var debugSessionToken = null
|
||||
const debugMode = false // Does not load session token, to allow multiple players per browser
|
||||
|
||||
|
||||
const offlineText = "Offline"
|
||||
const missingPlayerText = "Leer"
|
||||
|
||||
const elementIdUserName = "user-name"
|
||||
const elementIdUserPssword = "user-pwd"
|
||||
const elementIdPlayerCards = "player-cards"
|
||||
const elementIdLoginWindow = "login-window"
|
||||
const elementIdTopBar = "top-bar"
|
||||
@ -28,22 +25,6 @@ const elementIdActionBar = "action-bar"
|
||||
const elementIdAvailableGamesList = "available-games-list"
|
||||
const elementIdGameSummary = "game-summary"
|
||||
|
||||
const localStorageTokenId = "token"
|
||||
|
||||
function showDebugLogins() {
|
||||
document.getElementById("login-window-inner").innerHTML +=
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('a')\">Player A</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('b')\">Player B</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('c')\">Player C</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('d')\">Player D</button>"
|
||||
}
|
||||
|
||||
function loginDebugUser(name) {
|
||||
document.getElementById(elementIdUserName).value = name
|
||||
document.getElementById(elementIdUserPssword).value = name
|
||||
loginUser()
|
||||
}
|
||||
|
||||
function setDisplayStyle(id, style) {
|
||||
document.getElementById(id).style.display = style
|
||||
}
|
||||
@ -102,41 +83,6 @@ function setPlayerName(name) {
|
||||
playerName = name
|
||||
}
|
||||
|
||||
function getLoginName() {
|
||||
return document.getElementById(elementIdUserName).value
|
||||
}
|
||||
|
||||
function clearLoginName() {
|
||||
document.getElementById(elementIdUserName).value = ""
|
||||
}
|
||||
|
||||
function getLoginPassword() {
|
||||
return document.getElementById(elementIdUserPssword).value
|
||||
}
|
||||
|
||||
function clearLoginPassword() {
|
||||
document.getElementById(elementIdUserPssword).value = ""
|
||||
}
|
||||
|
||||
function getSessionToken() {
|
||||
if (debugMode) {
|
||||
return debugSessionToken
|
||||
}
|
||||
return localStorage.getItem(localStorageTokenId)
|
||||
}
|
||||
|
||||
function setSessionToken(token) {
|
||||
if (debugMode) {
|
||||
debugSessionToken = token
|
||||
return
|
||||
}
|
||||
localStorage.setItem(localStorageTokenId, token)
|
||||
}
|
||||
|
||||
function deleteSessionToken() {
|
||||
localStorage.removeItem(localStorageTokenId)
|
||||
}
|
||||
|
||||
function setLoginError(text) {
|
||||
document.getElementById(elementIdLoginError).innerHTML = text
|
||||
}
|
||||
@ -388,8 +334,8 @@ function textForAction(action) {
|
||||
return "Herz Solo"
|
||||
case "solo-schelln":
|
||||
return "Schelln Solo"
|
||||
}
|
||||
return action
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
function performAction(action) {
|
||||
|
@ -119,19 +119,20 @@ function deletePlayerAccount() {
|
||||
}
|
||||
|
||||
function loadExistingSession() {
|
||||
if (debugMode) {
|
||||
showDebugLogins()
|
||||
}
|
||||
const token = getSessionToken()
|
||||
const token = loadSessionToken()
|
||||
if (token == null) {
|
||||
showBlankLogin()
|
||||
window.location.href = "login.html";
|
||||
return
|
||||
}
|
||||
resumeSessionRequest(token)
|
||||
.then(function(name) {
|
||||
setPlayerName(name)
|
||||
loadCurrentTable(token)
|
||||
}).catch(showLoginWithError)
|
||||
}).catch(function(error) {
|
||||
console.log("Failed to resume session");
|
||||
console.log(error);
|
||||
window.location.href = "login.html";
|
||||
})
|
||||
}
|
||||
|
||||
function loadCurrentTable(token) {
|
||||
|
69
Public/login.css
Normal file
69
Public/login.css
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
#sheephead-logo {
|
||||
color: rgb(0, 255, 0);
|
||||
font-family: monospace, monospace;
|
||||
white-space: pre;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#login-window input {
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#login-window {
|
||||
background-color: var(--standard-background);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: table;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#login-window-vertical-center {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#login-window-inner {
|
||||
background-color: var(--element-background);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 300px;
|
||||
border-radius: 10px;
|
||||
border-style: solid;
|
||||
border-width: thin;
|
||||
border-color: var(--element-border);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.login-buttons {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#login-error {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
label a {
|
||||
color: var(--button-color);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#user-hint {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#field-note {
|
||||
padding-left: 5px;
|
||||
color: gray;
|
||||
}
|
70
Public/login.html
Normal file
70
Public/login.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>Schafkopf</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='style.css'/>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='login.css'/>
|
||||
<script src='api.js'></script>
|
||||
<script src='storage.js'></script>
|
||||
<script src='login.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-window">
|
||||
<div id="login-window-vertical-center">
|
||||
<div id="login-window-inner">
|
||||
<div id="sheephead-logo">\_______/<br>\ - - /<br>\ | /<br>\_/<br><br>Sheephead</div>
|
||||
<label for="usrname">Username<a href="register.html">Create account</a></label>
|
||||
<input type="text" id="user-name" name="usrname" required/>
|
||||
|
||||
<label for="psw">Password<a href="reset.html">Reset password</a></label>
|
||||
<input type="password" id="user-pwd" name="psw" required/>
|
||||
|
||||
<button class="login-buttons standard-button" onclick="loginUser()">Log in</button>
|
||||
<div id="user-hint"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function loginUser() {
|
||||
const username = getLoginName();
|
||||
const password = getLoginPassword();
|
||||
if (username == "") {
|
||||
setTextHint("Please enter your user name");
|
||||
return
|
||||
}
|
||||
if (password == "") {
|
||||
setTextHint("Please enter a password");
|
||||
return
|
||||
}
|
||||
performLoginPlayerRequest(username, password)
|
||||
.then(function(token) {
|
||||
storePlayerNameAndToken(username, token)
|
||||
window.location.href = "schafkopf.html";
|
||||
}).catch(function(error) {
|
||||
console.log("Failed to log in")
|
||||
console.log(error)
|
||||
// TODO: Create better error message for user
|
||||
setTextHint(error);
|
||||
})
|
||||
}
|
||||
|
||||
function showDebugLogins() {
|
||||
document.getElementById("login-window-inner").innerHTML +=
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('a')\">Player A</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('b')\">Player B</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('c')\">Player C</button>" +
|
||||
"<button class=\"login-buttons standard-button\" onclick=\"loginDebugUser('d')\">Player D</button>"
|
||||
}
|
||||
|
||||
function loginDebugUser(name) {
|
||||
setLoginName(name)
|
||||
setLoginPassword(name)
|
||||
loginUser()
|
||||
}
|
||||
|
||||
loadExistingSession()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
64
Public/login.js
Normal file
64
Public/login.js
Normal file
@ -0,0 +1,64 @@
|
||||
// Site-specific elements
|
||||
const elementIdUserName = "user-name";
|
||||
const elementIdUserPassword = "user-pwd";
|
||||
const elementIdUserEmail = "user-email";
|
||||
const elementIdUserHint = "user-hint";
|
||||
|
||||
function getLoginName() {
|
||||
return document.getElementById(elementIdUserName).value;
|
||||
}
|
||||
|
||||
function clearLoginName() {
|
||||
document.getElementById(elementIdUserName).value = "";
|
||||
}
|
||||
|
||||
function setLoginName(name) {
|
||||
document.getElementById(elementIdUserName).value = name;
|
||||
}
|
||||
|
||||
function getLoginPassword() {
|
||||
return document.getElementById(elementIdUserPassword).value;
|
||||
}
|
||||
|
||||
function clearLoginPassword() {
|
||||
document.getElementById(elementIdUserPassword).value = "";
|
||||
}
|
||||
|
||||
function setLoginPassword(password) {
|
||||
document.getElementById(elementIdUserPassword).value = password;
|
||||
}
|
||||
|
||||
function getLoginEmail() {
|
||||
return document.getElementById(elementIdUserEmail).value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a text to indicate an action, warning or other message below the registration field;
|
||||
*/
|
||||
function setTextHint(text) {
|
||||
document.getElementById(elementIdUserHint).innerHTML = text;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to reopen an existing session, either from the login or register page.
|
||||
*
|
||||
* It could be argued that this shouldn't be attempted for the registration page,
|
||||
* but, a user may accidentally go to the registration page through the browser history,
|
||||
* and then it will be convenient to be redirected to the game.
|
||||
*/
|
||||
function loadExistingSession() {
|
||||
const token = loadSessionToken();
|
||||
if (token == null) {
|
||||
console.log("No session token to load")
|
||||
return;
|
||||
}
|
||||
resumeSessionRequest(token)
|
||||
.then(function(name) {
|
||||
storePlayerNameAndToken(name, token);
|
||||
window.location.href = "list.html";
|
||||
}).catch(function(error) {
|
||||
// We don't expect a session to resume if the registration page is shown,
|
||||
// and if the token is expired on the login page we just fail quietly.
|
||||
console.log(error);
|
||||
})
|
||||
}
|
56
Public/recovery.html
Normal file
56
Public/recovery.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Schafkopf</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
<link rel='stylesheet' type='text/css' href='style.css'/>
|
||||
<link rel='stylesheet' type='text/css' href='login.css'/>
|
||||
<script src='api.js'></script>
|
||||
<script src='login.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-window">
|
||||
<div id="login-window-vertical-center">
|
||||
<div id="login-window-inner">
|
||||
<div id="sheephead-logo">\_______/<br>\ - - /<br>\ | /<br>\_/<br><br>Sheephead</div>
|
||||
<label for="psw">New Password</label>
|
||||
<input type="password" id="user-pwd" name="psw" required>
|
||||
|
||||
<button class="login-buttons standard-button" onclick="resetPassword()">Set new password</button>
|
||||
<div id="user-hint"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
function resetPassword() {
|
||||
const password = getLoginPassword()
|
||||
|
||||
if (password == "") {
|
||||
setTextHint("Please enter a password")
|
||||
return
|
||||
}
|
||||
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
if (!urlParams.has('token')) {
|
||||
setTextHint("Please open a valid recovery link")
|
||||
return
|
||||
}
|
||||
const token = urlParams.get('token');
|
||||
if (token == "") {
|
||||
setTextHint("Please open a valid recovery link")
|
||||
return
|
||||
}
|
||||
|
||||
performResetPasswordRequest(token, password)
|
||||
.then(function(value) {
|
||||
window.location.href = "login.html"
|
||||
})
|
||||
.catch(setTextHint)
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
75
Public/register.html
Normal file
75
Public/register.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>Schafkopf</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='style.css'/>
|
||||
<link rel='stylesheet' type='text/css' href='login.css'/>
|
||||
<script src='api.js'></script>
|
||||
<script src='storage.js'></script>
|
||||
<script src='login.js'></script>
|
||||
</head>
|
||||
<body id="login-window">
|
||||
<div id="login-window-vertical-center">
|
||||
<div id="login-window-inner">
|
||||
<div id="sheephead-logo">\_______/<br>\ - - /<br>\ | /<br>\_/<br><br>Sheephead</div>
|
||||
<label for="usrname">Username<span id="field-note">Required</span><a href="login.html">Log in instead</a></label>
|
||||
<input type="text" id="user-name" name="usrname" required autofocus/>
|
||||
|
||||
<label for="psw">Password<span id="field-note">Required</span></label>
|
||||
<input type="password" id="user-pwd" name="psw" required/>
|
||||
|
||||
<label for="email">Email<span id="field-note">Optional, for password reset</span></label>
|
||||
<input type="email" id="user-email" name="email"/>
|
||||
|
||||
<button class="login-buttons standard-button" onclick="registerUser()">Create account</button>
|
||||
<div id="user-hint"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
// Email address validation regex
|
||||
const mailformat = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
|
||||
|
||||
/*
|
||||
* Register a new account.
|
||||
*
|
||||
* The function extracts the fields from the form, and attempts to create the account.
|
||||
* Invalid emails are checked using basic validation.
|
||||
*
|
||||
* In case the registration fails, then we show an error to the user.
|
||||
*/
|
||||
function registerUser() {
|
||||
const username = getLoginName();
|
||||
if (username == "") {
|
||||
setTextHint("Please enter your user name");
|
||||
return;
|
||||
}
|
||||
const password = getLoginPassword();
|
||||
if (password == "") {
|
||||
setTextHint("Please enter a password");
|
||||
return;
|
||||
}
|
||||
const email = getLoginEmail();
|
||||
if (email != "" && !email.match(mailformat)) {
|
||||
setTextHint("Invalid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
performRegisterPlayerRequest(username, password, email)
|
||||
.then(function(token) {
|
||||
storePlayerNameAndToken(username, token);
|
||||
window.location.href = "list.html";
|
||||
}).catch(function(error) {
|
||||
// TODO: Show better error messages to user
|
||||
console.log("Registration failed.");
|
||||
console.log(error);
|
||||
setTextHint(error);
|
||||
})
|
||||
}
|
||||
|
||||
loadExistingSession();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
42
Public/reset.html
Normal file
42
Public/reset.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Schafkopf</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
<link rel='stylesheet' type='text/css' href='style.css'/>
|
||||
<link rel='stylesheet' type='text/css' href='login.css'/>
|
||||
<script src='api.js'></script>
|
||||
<script src='login.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-window">
|
||||
<div id="login-window-vertical-center">
|
||||
<div id="login-window-inner">
|
||||
<div id="sheephead-logo">\_______/<br>\ - - /<br>\ | /<br>\_/<br><br>Sheephead</div>
|
||||
<label for="usrname">Username</span><a href="login.html">Log in instead</a></label>
|
||||
<input type="text" id="user-name" name="usrname" required/>
|
||||
|
||||
<button class="login-buttons standard-button" onclick="resetPassword()">Send recovery email</button>
|
||||
<div id="user-hint"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function resetPassword() {
|
||||
const name = getLoginName()
|
||||
|
||||
if (name == "") {
|
||||
setTextHint("Please enter a name")
|
||||
return
|
||||
}
|
||||
|
||||
performRecoveryEmailRequest(name)
|
||||
.then(function(value) {
|
||||
setTextHint("The email has been sent")
|
||||
})
|
||||
.catch(setTextHint)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -7,6 +7,7 @@
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='style.css'>
|
||||
<script src='elements.js?v=1'></script>
|
||||
<script src='api.js'></script>
|
||||
<script src='storage.js'></script>
|
||||
<script src='game.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
@ -72,23 +73,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="login-window">
|
||||
<div id="login-window-vertical-center">
|
||||
<div id="login-window-inner">
|
||||
<div id="sheephead-logo">\_______/<br> \ - - / <br> \ | / <br> \_/ <br><br>Sheephead</div>
|
||||
<label for="usrname">Username</label>
|
||||
<input type="text" id="user-name" name="usrname" required>
|
||||
|
||||
<label for="psw">Password</label>
|
||||
<input type="password" id="user-pwd" name="psw" required>
|
||||
|
||||
<button class="login-buttons standard-button" onclick="registerUser()">Register</button>
|
||||
<button class="login-buttons standard-button" onclick="loginUser()">Log in</button>
|
||||
<div id="login-error"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
loadExistingSession()
|
||||
</script>
|
||||
|
38
Public/storage.js
Normal file
38
Public/storage.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Local storage element identifiers
|
||||
const localStorageTokenId = "token";
|
||||
const localStoragePlayerName = "name";
|
||||
|
||||
// Can prevent loading of session token, to allow multiple players per browser
|
||||
const debugMode = false
|
||||
|
||||
/*
|
||||
* Store the player name and session token in local storage.
|
||||
* Parameter name: The user name of the player
|
||||
* Parameter token: The session token for the player session
|
||||
*/
|
||||
function storePlayerNameAndToken(name, token) {
|
||||
localStorage.setItem(localStoragePlayerName, name);
|
||||
localStorage.setItem(localStorageTokenId, token);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the last session token from local storage.
|
||||
*/
|
||||
function loadSessionToken() {
|
||||
if (debugMode) {
|
||||
return debugSessionToken
|
||||
}
|
||||
return localStorage.getItem(localStorageTokenId)
|
||||
}
|
||||
|
||||
function storeSessionToken(token) {
|
||||
if (debugMode) {
|
||||
debugSessionToken = token
|
||||
return
|
||||
}
|
||||
localStorage.setItem(localStorageTokenId, token)
|
||||
}
|
||||
|
||||
function deleteSessionToken() {
|
||||
localStorage.removeItem(localStorageTokenId)
|
||||
}
|
@ -37,12 +37,6 @@ input {
|
||||
background-color: var(--standard-background);
|
||||
}
|
||||
|
||||
#login-window input {
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
html * {
|
||||
font-family:-apple-system, BlinkMacSystemFont, "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
|
||||
color: var(--text-color);
|
||||
@ -60,49 +54,6 @@ body {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#login-window {
|
||||
background-color: var(--standard-background);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: table;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#login-window-vertical-center {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#login-window-inner {
|
||||
background-color: var(--element-background);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 300px;
|
||||
border-radius: 10px;
|
||||
border-style: solid;
|
||||
border-width: thin;
|
||||
border-color: var(--element-border);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#sheephead-logo {
|
||||
color: rgb(0, 255, 0);
|
||||
font-family: monospace, monospace;
|
||||
white-space: pre;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-buttons {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#login-error {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#top-bar {
|
||||
height: 50px;
|
||||
background-color: var(--element-background);
|
||||
|
Loading…
Reference in New Issue
Block a user