Refactored base structure
This commit is contained in:
@ -20,8 +20,7 @@ Distributed, end-to-end encrypted instant messaging."/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="icon" sizes="64x64" href="/favicon.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="/res/styles/styles.css?v=0.0.0-0" type="text/css"/>
|
||||
<script src="/res/js/locutus.js?v=0.0.0-0" type="text/javascript"></script>
|
||||
<script src="/res/js/usimp.js?v=0.0.0-0" type="text/javascript"></script>
|
||||
<script src="/res/js/main.js?v=0.0.0-0" type="module"></script>
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'none';
|
||||
style-src 'self';
|
||||
|
@ -1,220 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
let main, windows;
|
||||
let defaultLocation = '/welcome';
|
||||
|
||||
let token = "";
|
||||
let domain = "";
|
||||
let dest = "";
|
||||
|
||||
function createWelcomeWindow() {
|
||||
let win = document.createElement("div");
|
||||
win.id = "welcome-win";
|
||||
|
||||
win.innerHTML = `
|
||||
<h1>Welcome to Locutus!</h1>
|
||||
<a href="#/login" class="button">Login</a>`;
|
||||
|
||||
while (windows.lastChild) windows.removeChild(windows.lastChild);
|
||||
windows.appendChild(win);
|
||||
}
|
||||
|
||||
function createChatWindow() {
|
||||
let win = document.createElement("div");
|
||||
win.id = "chat-win";
|
||||
|
||||
console.log("Creating chat window");
|
||||
|
||||
win.innerHTML = `
|
||||
<div id="message-win">
|
||||
<p>Test Message</p>
|
||||
</div>
|
||||
<form id="message-form">
|
||||
<input id="message-input" name="message" placeholder="Very important message..." type="text" required>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
||||
let messageForm = win.getElementsByTagName("form")[0];
|
||||
messageForm.addEventListener("submit", (evt) => {
|
||||
evt.preventDefault();
|
||||
let messageInput = messageForm.elements['message-input'];
|
||||
|
||||
let message = JSON.stringify({
|
||||
room_id: "60nc0XXDIYUh6QzX4p0rMpCdzDmxghZLZk8dLuQh628",
|
||||
data: {
|
||||
message: messageInput.value
|
||||
}
|
||||
})
|
||||
sendEvent(message).then((evt => {
|
||||
console.log(evt)
|
||||
}));
|
||||
let messageAdd = document.createElement("p");
|
||||
messageAdd.innerHTML = `You: <strong> ${messageInput.value} </strong>`;
|
||||
win.getElementsByTagName("div")[0].appendChild(messageAdd);
|
||||
messageInput.value = "";
|
||||
|
||||
});
|
||||
|
||||
while (windows.lastChild) windows.removeChild(windows.lastChild);
|
||||
windows.style.visibility = "hidden";
|
||||
subscribeEvent();
|
||||
main.appendChild(win);
|
||||
}
|
||||
|
||||
async function sendEvent(message) {
|
||||
return fetch(`${dest}/_usimp/send_event`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `usimp ${token}`,
|
||||
'From-Domain': domain,
|
||||
'To-Domain': domain,
|
||||
},
|
||||
body: message
|
||||
});
|
||||
}
|
||||
|
||||
async function subscribeEvent() {
|
||||
let response = await fetch(`${dest}/_usimp/subscribe`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'To-Domain': domain,
|
||||
'From-Domain': domain
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
showMessage(response.statusText);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await subscribeEvent();
|
||||
} else {
|
||||
let message = await response.text();
|
||||
addMessage(JSON.parse(message).event.data.message);
|
||||
await subscribeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(message) {
|
||||
let messageContainer = document.getElementById("message-win");
|
||||
let messageP = document.createElement("p");
|
||||
messageP.innerHTML = `Not You: <strong> ${message} </strong>`;
|
||||
messageContainer.appendChild(messageP);
|
||||
}
|
||||
|
||||
function createLoginWindow() {
|
||||
let win = document.createElement("div");
|
||||
win.id = "login-win";
|
||||
|
||||
win.innerHTML = `
|
||||
<h1>Login to USIMP Account</h1>
|
||||
<form>
|
||||
<input name="account" placeholder="Account name" type="text" required/>
|
||||
<input name="domain" placeholder="Domain" type="text" pattern="([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,}" required/>
|
||||
<input name="password" placeholder="Password" type="password" required/>
|
||||
<button type="submit">Login</button>
|
||||
</form>`;
|
||||
|
||||
win.getElementsByTagName("form")[0].addEventListener("submit", (evt) => {
|
||||
evt.preventDefault();
|
||||
let form = evt.target;
|
||||
for (let e of form) e.disabled = true;
|
||||
|
||||
for (let d of form.getElementsByTagName("div")) {
|
||||
form.removeChild(d);
|
||||
}
|
||||
|
||||
function formError(msg) {
|
||||
let div = document.createElement("div");
|
||||
div.classList.add("error");
|
||||
div.innerText = msg;
|
||||
form.appendChild(div);
|
||||
}
|
||||
|
||||
usimp.lookup(form.domain.value)
|
||||
.then(res => {
|
||||
if (res.ok) {
|
||||
res.json()
|
||||
.then(data => {
|
||||
console.log(data["domain"]);
|
||||
let domainServer = usimp.chooseDomainServer(data["domain_servers"]);
|
||||
console.log(domainServer);
|
||||
dest = `http://${domainServer.host}:${domainServer.protocols.http}`;
|
||||
domain = form.elements["domain"].value
|
||||
|
||||
usimp.login(domainServer, data["domain"].id, form.elements["account"].value, form.elements["password"].value)
|
||||
.then(res => res.json())
|
||||
.catch((evt) => {
|
||||
console.log("Serva ded");
|
||||
console.log(evt);
|
||||
})
|
||||
.then(data => {
|
||||
token = data.token;
|
||||
console.log(data.token);
|
||||
createChatWindow();
|
||||
});
|
||||
})
|
||||
.catch(reason => {
|
||||
console.error(reason);
|
||||
formError("Could not communicate with USIMP domain");
|
||||
})
|
||||
} else {
|
||||
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
|
||||
formError("Invalid USIMP domain");
|
||||
}
|
||||
})
|
||||
.catch(reason => {
|
||||
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
|
||||
formError("Invalid USIMP domain");
|
||||
})
|
||||
.finally(() => {
|
||||
for (let e of form) e.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
while (windows.lastChild) windows.removeChild(windows.lastChild);
|
||||
windows.appendChild(win);
|
||||
|
||||
document.getElementsByName("domain")[0].addEventListener("input", (evt) => {
|
||||
evt.target.removeAttribute("invalid");
|
||||
});
|
||||
|
||||
document.getElementsByName("account")[0].focus();
|
||||
}
|
||||
|
||||
function handleHash(hash) {
|
||||
if (hash[0] === '#') hash = hash.substr(1);
|
||||
|
||||
switch (hash) {
|
||||
case "/welcome": createWelcomeWindow(); break;
|
||||
case "/login": createLoginWindow(); break;
|
||||
default:
|
||||
console.warn(`Invalid url hash #${hash}`);
|
||||
history.replaceState(null, null, `#${defaultLocation}`);
|
||||
handleHash(defaultLocation)
|
||||
return;
|
||||
}
|
||||
|
||||
let url = new URL(document.URL);
|
||||
url.hash = hash;
|
||||
location.href = url.toString();
|
||||
}
|
||||
|
||||
function handleUrl(url) {
|
||||
handleHash(new URL(url).hash);
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", (evt) => {
|
||||
main = document.getElementsByTagName("main")[0];
|
||||
windows = document.getElementById("windows");
|
||||
|
||||
// Remove <noscript> tag
|
||||
document.getElementsByTagName("noscript")[0].remove();
|
||||
|
||||
handleUrl(document.URL)
|
||||
});
|
||||
|
||||
window.addEventListener("hashchange", (evt) => {
|
||||
handleUrl(evt.newURL);
|
||||
});
|
9
www/res/js/main.js
Normal file
9
www/res/js/main.js
Normal file
@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
import * as Locutus from "./modules/locutus.js";
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
// Remove <noscript> tag
|
||||
document.getElementsByTagName("noscript")[0].remove();
|
||||
new Locutus.App();
|
||||
});
|
214
www/res/js/modules/locutus.js
Normal file
214
www/res/js/modules/locutus.js
Normal file
@ -0,0 +1,214 @@
|
||||
"use strict";
|
||||
|
||||
import * as USIMP from "./usimp.js";
|
||||
|
||||
export class App {
|
||||
account;
|
||||
defaultLocation;
|
||||
main;
|
||||
windows;
|
||||
session;
|
||||
|
||||
constructor() {
|
||||
if (localStorage.session === undefined) {
|
||||
this.defaultLocation = '/welcome';
|
||||
this.account = null;
|
||||
} else {
|
||||
let session = JSON.parse(localStorage.session);
|
||||
this.defaultLocation = '/';
|
||||
this.account = session.account;
|
||||
}
|
||||
|
||||
this.main = document.getElementsByTagName("main")[0];
|
||||
this.windows = document.getElementById("windows");
|
||||
|
||||
window.addEventListener("hashchange", event => {
|
||||
this.handleUrl(event.newURL);
|
||||
});
|
||||
|
||||
this.handleUrl(document.URL);
|
||||
}
|
||||
|
||||
setHash(hash) {
|
||||
let url = new URL(document.URL);
|
||||
url.hash = hash;
|
||||
location.href = url.toString();
|
||||
}
|
||||
|
||||
handleUrl(url) {
|
||||
this.handleHash(new URL(url).hash);
|
||||
}
|
||||
|
||||
handleHash(hash) {
|
||||
console.log(hash);
|
||||
if (hash[0] === '#') hash = hash.substr(1);
|
||||
|
||||
let defaultCase = () => {
|
||||
history.replaceState(null, null, `#${this.defaultLocation}`);
|
||||
this.handleHash(this.defaultLocation);
|
||||
}
|
||||
|
||||
switch (hash) {
|
||||
case "/":
|
||||
if (!this.session) {
|
||||
return defaultCase();
|
||||
}
|
||||
this.hideWindows();
|
||||
this.showMain();
|
||||
this.removeAllWindows();
|
||||
this.setupMain();
|
||||
break;
|
||||
case "/welcome":
|
||||
this.hideMain();
|
||||
this.showWindows();
|
||||
this.removeAllWindows();
|
||||
this.addWelcomeWindow();
|
||||
break;
|
||||
case "/login":
|
||||
if (this.session) {
|
||||
return defaultCase();
|
||||
}
|
||||
this.hideMain();
|
||||
this.showWindows();
|
||||
this.removeAllWindows();
|
||||
this.addLoginWindow();
|
||||
break;
|
||||
default:
|
||||
console.warn(`Invalid url hash #${hash}`);
|
||||
return defaultCase();
|
||||
}
|
||||
|
||||
//this.setHash(hash);
|
||||
}
|
||||
|
||||
login(accountName, domainName, password) {
|
||||
return USIMP.Domain.fromName(domainName).then(domain => {
|
||||
let session = new USIMP.Session(domain);
|
||||
session.authenticate(accountName, password)
|
||||
.catch((event) => {
|
||||
console.error(`Unable to reach domain server: ${event}`);
|
||||
})
|
||||
.then(() => {
|
||||
this.session = session;
|
||||
this.defaultLocation = "/";
|
||||
this.setHash("/");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
hideMain() {
|
||||
this.main.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
showMain() {
|
||||
this.main.style.visibility = "visible";
|
||||
}
|
||||
|
||||
hideWindows() {
|
||||
this.windows.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
showWindows() {
|
||||
this.windows.style.visibility = "visible";
|
||||
}
|
||||
|
||||
removeAllWindows() {
|
||||
while (this.windows.lastChild) this.windows.removeChild(this.windows.lastChild);
|
||||
}
|
||||
|
||||
addWelcomeWindow() {
|
||||
let win = document.createElement("div");
|
||||
win.classList.add("window-welcome");
|
||||
|
||||
win.innerHTML = `
|
||||
<h1>Welcome to Locutus!</h1>
|
||||
<a href="#/login" class="button">Login</a>`;
|
||||
|
||||
this.windows.appendChild(win);
|
||||
}
|
||||
|
||||
addMessage(message) {
|
||||
let msg = document.createElement("div");
|
||||
msg.classList.add("message");
|
||||
|
||||
msg.innerText = message;
|
||||
|
||||
let chat = this.main.getElementsByClassName("chat-history")[0];
|
||||
chat.appendChild(msg);
|
||||
chat.scrollTop = chat.scrollHeight;
|
||||
}
|
||||
|
||||
addLoginWindow() {
|
||||
let win = document.createElement("div");
|
||||
win.classList.add("window-login");
|
||||
|
||||
win.innerHTML = `
|
||||
<h1>Login to USIMP Account</h1>
|
||||
<form>
|
||||
<input name="account" placeholder="Account name" type="text" required/>
|
||||
<input name="domain" placeholder="Domain" type="text" pattern="([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,}" required/>
|
||||
<input name="password" placeholder="Password" type="password" required/>
|
||||
<button type="submit">Login</button>
|
||||
</form>`;
|
||||
|
||||
win.getElementsByTagName("form")[0].addEventListener("submit", async (evt) => {
|
||||
evt.preventDefault();
|
||||
let form = evt.target;
|
||||
for (let e of form) e.disabled = true;
|
||||
|
||||
for (let d of form.getElementsByTagName("div")) {
|
||||
form.removeChild(d);
|
||||
}
|
||||
|
||||
function formError(msg) {
|
||||
let div = document.createElement("div");
|
||||
div.classList.add("error");
|
||||
div.innerText = msg;
|
||||
form.appendChild(div);
|
||||
}
|
||||
|
||||
this.login(form.account.value, form.domain.value, form.password.value)
|
||||
.catch(reason => {
|
||||
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
|
||||
formError("Invalid USIMP domain");
|
||||
console.error(reason);
|
||||
})
|
||||
.finally(() => {
|
||||
for (let e of form) e.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
this.windows.appendChild(win);
|
||||
|
||||
document.getElementsByName("domain")[0].addEventListener("input", (event) => {
|
||||
event.target.removeAttribute("invalid");
|
||||
});
|
||||
|
||||
document.getElementsByName("account")[0].focus();
|
||||
}
|
||||
|
||||
setupMain() {
|
||||
this.main.innerHTML = `
|
||||
<div class="chat">
|
||||
<div class="chat-history"></div>
|
||||
<div class="chat-input-wrapper">
|
||||
<input name="message" type="text"/>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
let input = document.getElementsByName("message")[0];
|
||||
input.addEventListener("keyup", (event) => {
|
||||
if (event.key === "Enter" && input.value.length > 0) {
|
||||
this.session.sendEvent("60nc0XXDIYUh6QzX4p0rMpCdzDmxghZLZk8dLuQh628", {
|
||||
message: input.value,
|
||||
});
|
||||
this.addMessage(input.value);
|
||||
input.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
this.session.subscribe(response => {
|
||||
this.addMessage(response.event.data.message);
|
||||
});
|
||||
}
|
||||
}
|
204
www/res/js/modules/usimp.js
Normal file
204
www/res/js/modules/usimp.js
Normal file
@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
|
||||
export class Domain {
|
||||
name;
|
||||
id;
|
||||
servers;
|
||||
invalidServers;
|
||||
|
||||
toString() {
|
||||
return `[${this.name}]`;
|
||||
}
|
||||
|
||||
constructor(jsonData, domainServers) {
|
||||
// FIXME check values
|
||||
this.name = jsonData.name;
|
||||
this.id = jsonData.id;
|
||||
this.servers = [];
|
||||
for (let domainServer of domainServers) {
|
||||
this.servers.push(new DomainServer(domainServer));
|
||||
}
|
||||
this.invalidServers = [];
|
||||
}
|
||||
|
||||
static async fromName(domainName) {
|
||||
return fetch(`https://${domainName}/.well-known/usimp.json`, {
|
||||
redirect: "manual",
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw "Invalid response";
|
||||
}
|
||||
}).then(data => {
|
||||
return new Domain(data.domain, data.domain_servers);
|
||||
});
|
||||
}
|
||||
|
||||
chooseDomainServer() {
|
||||
if (this.servers.length === 0) throw Error("No domain servers specified");
|
||||
|
||||
this.servers.filter(srv => this.invalidServers.map(srv => srv.id).includes(srv.id));
|
||||
if (this.servers.length === 0) throw Error("No domain servers reachable");
|
||||
if (this.servers.length === 1) return this.servers[0];
|
||||
|
||||
let priority = this.servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
|
||||
let domainServers = this.servers.filter(srv => srv.priority === priority);
|
||||
if (domainServers.length === 1) return this.servers[0];
|
||||
|
||||
let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
|
||||
let w = Math.floor(Math.random() * totalWeight);
|
||||
|
||||
let accumulator = 0;
|
||||
for (let srv of domainServers) {
|
||||
accumulator += srv.weight;
|
||||
if (w < accumulator) return srv;
|
||||
}
|
||||
|
||||
throw "Domain server selection did not work correctly";
|
||||
}
|
||||
}
|
||||
|
||||
export class DomainServer {
|
||||
host;
|
||||
id;
|
||||
priority;
|
||||
weight;
|
||||
protocols;
|
||||
|
||||
toString() {
|
||||
return `[${this.host}/${this.priority}/${this.weight}]`;
|
||||
}
|
||||
|
||||
constructor(jsonData) {
|
||||
// FIXME check values
|
||||
this.host = jsonData.host;
|
||||
this.id = jsonData.id;
|
||||
this.priority = jsonData.priority;
|
||||
this.weight = jsonData.weight;
|
||||
this.protocols = jsonData.protocols;
|
||||
}
|
||||
}
|
||||
|
||||
export class Account {
|
||||
|
||||
}
|
||||
|
||||
export class Member {
|
||||
|
||||
}
|
||||
|
||||
export class Room {
|
||||
|
||||
}
|
||||
|
||||
export class Event {
|
||||
|
||||
}
|
||||
|
||||
export class Session {
|
||||
domain;
|
||||
server;
|
||||
token;
|
||||
|
||||
httpBaseUrl;
|
||||
wsConnection;
|
||||
numRequests;
|
||||
|
||||
constructor(domain) {
|
||||
this.domain = domain;
|
||||
this.server = domain.chooseDomainServer();
|
||||
this.numRequests = 0;
|
||||
|
||||
const host = this.server.host;
|
||||
const protocols = this.server.protocols;
|
||||
|
||||
/*
|
||||
if ("wss" in protocols) {
|
||||
this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]);
|
||||
} else if ("ws" in protocols) {
|
||||
this.wsConnection = new WebSocket(`ws://${host}:${protocols.ws}/_usimp/websocket`, ["usimp"])
|
||||
}
|
||||
*/
|
||||
|
||||
if ("https" in protocols) {
|
||||
this.httpBaseUrl = `https://${host}:${protocols.https}/_usimp`;
|
||||
} else if ("http" in protocols) {
|
||||
this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`;
|
||||
} else {
|
||||
throw "Domain server provides no supported transport protocols";
|
||||
}
|
||||
}
|
||||
|
||||
send(endpoint, data) {
|
||||
this.numRequests++;
|
||||
if (this.wsConnection) {
|
||||
this.wsConnection.send(JSON.stringify({
|
||||
'request_num': this.numRequests,
|
||||
'data': data
|
||||
}));
|
||||
} else {
|
||||
let headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'To-Domain': this.domain.id,
|
||||
'From-Domain': this.domain.id,
|
||||
};
|
||||
if (this.token) {
|
||||
headers['Authorization'] = `usimp ${this.token}`;
|
||||
}
|
||||
|
||||
return fetch(`${this.httpBaseUrl}/${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify(data),
|
||||
}).then(response => {
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
authenticate(accountName, password) {
|
||||
return this.send("authenticate", {
|
||||
type: "password",
|
||||
name: accountName,
|
||||
password: password,
|
||||
}).then(response => {
|
||||
this.token = response.token;
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
sendEvent(roomId, data) {
|
||||
return this.send("send_event", {
|
||||
room_id: roomId,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
subscribe(func) {
|
||||
this.numRequests++;
|
||||
if (this.wsConnection) {
|
||||
// TODO
|
||||
} else {
|
||||
let headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'To-Domain': this.domain.id,
|
||||
'From-Domain': this.domain.id,
|
||||
};
|
||||
if (this.token) {
|
||||
headers['Authorization'] = `usimp ${this.token}`;
|
||||
}
|
||||
|
||||
fetch(`${this.httpBaseUrl}/subscribe`, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify({}),
|
||||
}).then(response => {
|
||||
return response.json();
|
||||
}).then(response => {
|
||||
func(response);
|
||||
this.subscribe(func);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
let usimp = {};
|
||||
|
||||
usimp.lookup = function (domain_name) {
|
||||
return fetch(`https://${domain_name}/.well-known/usimp.json`, {
|
||||
redirect: "manual",
|
||||
});
|
||||
}
|
||||
|
||||
usimp.chooseDomainServer = function (domainServers, invalidDomainServers = []) {
|
||||
if (domainServers.length === 0) throw Error("No domain servers specified");
|
||||
|
||||
domainServers.filter(srv => invalidDomainServers.map(srv => srv.id).includes(srv.id));
|
||||
if (domainServers.length === 0) throw Error("No domain servers reachable");
|
||||
if (domainServers.length === 1) return domainServers[0];
|
||||
|
||||
let priority = domainServers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
|
||||
domainServers = domainServers.filter(srv => srv.priority === priority);
|
||||
if (domainServers.length === 1) return domainServers[0];
|
||||
|
||||
let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
|
||||
let w = Math.floor(Math.random() * totalWeight);
|
||||
|
||||
let accumulator = 0;
|
||||
for (let srv of domainServers) {
|
||||
accumulator += srv.weight;
|
||||
if (w < accumulator) return srv;
|
||||
}
|
||||
|
||||
console.warn("Domain server selection not worked correctly");
|
||||
return domainServers[0];
|
||||
}
|
||||
|
||||
usimp.login = function (domainServer, domain, account, password) {
|
||||
return fetch(`http://${domainServer.host}:${domainServer.protocols.http}/_usimp/authenticate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'To-Domain': domain,
|
||||
'From-Domain': domain
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: "password",
|
||||
name: account,
|
||||
password: password,
|
||||
}),
|
||||
});
|
||||
}
|
@ -24,17 +24,10 @@ body {
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 800px;
|
||||
height: calc(100% - 2em);
|
||||
padding: 1em;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div#wrapper {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
height: calc(100vh - var(--footer-height));
|
||||
}
|
||||
|
||||
@ -59,23 +52,33 @@ div#windows {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
div#windows > * {
|
||||
div#windows > *,
|
||||
main {
|
||||
backdrop-filter: blur(32px);
|
||||
border: 1px solid var(--bg-win);
|
||||
background: var(--bg-win);
|
||||
border-radius: 4px;
|
||||
padding: 0.5em 1em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div#login-win,
|
||||
div#welcome-win {
|
||||
main {
|
||||
max-width: 800px;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div#windows > * {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
div.window-login,
|
||||
div.window-welcome {
|
||||
max-width: 650px;
|
||||
margin: calc(max(25vh, 8em) - 8em) auto 1em auto;
|
||||
}
|
||||
|
||||
div#login-win h1,
|
||||
div#welcome-win h1 {
|
||||
div.window-login h1,
|
||||
div.window-welcome h1 {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
@ -186,3 +189,44 @@ form button:active,
|
||||
a.button:active {
|
||||
background-color: rgba(224, 224, 224, 0.75);
|
||||
}
|
||||
|
||||
main .chat {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main .chat-history {
|
||||
height: calc(100% - 3em);
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main .chat-input-wrapper {
|
||||
width: 100%;
|
||||
height: 3em;
|
||||
border-top: 1px solid var(--bg-win);
|
||||
padding: 0.5em 1em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main .chat-input-wrapper input {
|
||||
background-color: rgba(0,0,0,0);
|
||||
border: 1px solid var(--bg-win);
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding: 0.25em 1em;
|
||||
}
|
||||
|
||||
main .chat .message {
|
||||
margin: 1em;
|
||||
border: 1px solid var(--bg-win);
|
||||
border-radius: 4px;
|
||||
padding: 0.5em 1em;
|
||||
display: block;
|
||||
width: fit-content;
|
||||
width: -moz-fit-content;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
Reference in New Issue
Block a user