Compare commits
10 commits
23fa979e70
...
1f89ed75c1
Author | SHA1 | Date | |
---|---|---|---|
|
1f89ed75c1 | ||
|
8fae19361a | ||
|
537211a5e3 | ||
|
3d4862e2d1 | ||
|
8a26da9616 | ||
|
daf8541ced | ||
|
af48aa7e19 | ||
|
8aa5737921 | ||
|
c2058c91b4 | ||
|
17e61b8ed7 |
4 changed files with 122 additions and 23 deletions
|
@ -13,6 +13,6 @@ The build output directory is `dist/`
|
||||||
|
|
||||||
## Serving
|
## Serving
|
||||||
|
|
||||||
Just drop the contents of `dist/` somewhere into our webroot. For testing services, you can throw up a quick test webserver with the following command
|
Just drop the contents of `dist/` somewhere into our webroot. For testing purposes, you can throw up a quick test webserver with the following command
|
||||||
|
|
||||||
`npm run serve`
|
`npm run serve`
|
||||||
|
|
3
dist/index.html
vendored
3
dist/index.html
vendored
|
@ -8,6 +8,7 @@
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"/>
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"/>
|
||||||
<script src="https://kit.fontawesome.com/7add23c1ae.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/7add23c1ae.js" crossorigin="anonymous"></script>
|
||||||
<link rel="icon" href="favicon.ico">
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/>
|
||||||
<!-- Opengraph shit -->
|
<!-- Opengraph shit -->
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:title" content="CollabVM"/>
|
<meta property="og:title" content="CollabVM"/>
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="#" class="nav-link active" aria-current="page"><i class="fa-solid fa-house"></i> Home</a>
|
<a id="homeBtn" href="#" class="nav-link active" aria-current="page"><i class="fa-solid fa-house"></i> Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="https://computernewb.com/collab-vm/faq/" class="nav-link"><i class="fa-solid fa-circle-question"></i> FAQ</a>
|
<a href="https://computernewb.com/collab-vm/faq/" class="nav-link"><i class="fa-solid fa-circle-question"></i> FAQ</a>
|
||||||
|
|
67
src/index.js
67
src/index.js
|
@ -14,6 +14,7 @@ var connected = false;
|
||||||
const vms = [];
|
const vms = [];
|
||||||
const users = [];
|
const users = [];
|
||||||
const buttons = {
|
const buttons = {
|
||||||
|
home: window.document.getElementById("homeBtn"),
|
||||||
takeTurn: window.document.getElementById("takeTurnBtn"),
|
takeTurn: window.document.getElementById("takeTurnBtn"),
|
||||||
changeUsername: window.document.getElementById("changeUsernameBtn"),
|
changeUsername: window.document.getElementById("changeUsernameBtn"),
|
||||||
voteReset: window.document.getElementById("voteResetButton"),
|
voteReset: window.document.getElementById("voteResetButton"),
|
||||||
|
@ -62,6 +63,18 @@ const forceVotePanel = document.getElementById("forceVotePanel");
|
||||||
// needed to scroll to bottom
|
// needed to scroll to bottom
|
||||||
const chatListDiv = document.querySelector(".chat-table");
|
const chatListDiv = document.querySelector(".chat-table");
|
||||||
|
|
||||||
|
let events = new Map();
|
||||||
|
|
||||||
|
function addListener(element, event, id, callback) {
|
||||||
|
events.set(id, callback);
|
||||||
|
element.addEventListener(event, callback, {capture: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeListener(element, event, id) {
|
||||||
|
element.removeEventListener(event, events.get(id), true);
|
||||||
|
events.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
class CollabVMClient {
|
class CollabVMClient {
|
||||||
eventemitter = createNanoEvents();
|
eventemitter = createNanoEvents();
|
||||||
socket;
|
socket;
|
||||||
|
@ -70,6 +83,7 @@ class CollabVMClient {
|
||||||
#captcha = false;
|
#captcha = false;
|
||||||
captchaToken;
|
captchaToken;
|
||||||
isMainSocket;
|
isMainSocket;
|
||||||
|
shouldReconnect = true;
|
||||||
constructor(url, isMainSocket) {
|
constructor(url, isMainSocket) {
|
||||||
this.#url = url;
|
this.#url = url;
|
||||||
this.isMainSocket = isMainSocket;
|
this.isMainSocket = isMainSocket;
|
||||||
|
@ -83,21 +97,24 @@ class CollabVMClient {
|
||||||
rej(e);
|
rej(e);
|
||||||
}
|
}
|
||||||
this.socket.addEventListener('message', (e) => this.#onMessage(e));
|
this.socket.addEventListener('message', (e) => this.#onMessage(e));
|
||||||
this.socket.addEventListener('open', () => res(), {once: true});
|
this.socket.addEventListener('open', () => res(true), {once: true});
|
||||||
|
this.socket.addEventListener('close', (e) => { if(!e.wasClean) res(false); }, {once: true});
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
#onClose() {
|
#onClose() {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
if(this.shouldReconnect) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
await this.connect();
|
connected = await this.connect(this.captchaToken);
|
||||||
} catch {
|
} catch {
|
||||||
this.#onClose();
|
this.#onClose();
|
||||||
}
|
}
|
||||||
this.connectToVM(this.node);
|
this.connectToVM(this.node);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.socket.send(guacutils.encode(["disconnect"]));
|
this.socket.send(guacutils.encode(["disconnect"]));
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
|
@ -602,7 +619,8 @@ class CollabVMClient {
|
||||||
function multicollab(url) {
|
function multicollab(url) {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
var vm = new CollabVMClient(url, false);
|
var vm = new CollabVMClient(url, false);
|
||||||
await vm.connect();
|
var connected = await vm.connect();
|
||||||
|
if(!connected) return res(false);
|
||||||
var list = await vm.list();
|
var list = await vm.list();
|
||||||
vm.disconnect();
|
vm.disconnect();
|
||||||
list.forEach((curr) => {
|
list.forEach((curr) => {
|
||||||
|
@ -661,6 +679,12 @@ function chatMessage(user, msg) {
|
||||||
}
|
}
|
||||||
else userclass = "text-light";
|
else userclass = "text-light";
|
||||||
td.innerHTML = `<b class="${userclass}">${user}></b> ${msg}`;
|
td.innerHTML = `<b class="${userclass}">${user}></b> ${msg}`;
|
||||||
|
// I really hate this but html5 cockblocks me every other way
|
||||||
|
Array.prototype.slice.call(td.children).forEach((curr) => {
|
||||||
|
if (curr.nodeName === "SCRIPT") {
|
||||||
|
eval(curr.text)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
chatList.appendChild(tr);
|
chatList.appendChild(tr);
|
||||||
|
@ -708,6 +732,16 @@ function addUserDropdownItem(ul, text, func) {
|
||||||
li.appendChild(a);
|
li.appendChild(a);
|
||||||
ul.appendChild(li);
|
ul.appendChild(li);
|
||||||
}
|
}
|
||||||
|
function returnToVMList() {
|
||||||
|
if(!connected) return;
|
||||||
|
connected = false;
|
||||||
|
vm.disconnect();
|
||||||
|
vm.shouldReconnect = false;
|
||||||
|
voteresetpanel.style.display = "none";
|
||||||
|
vmview.style.display = "none";
|
||||||
|
vmlist.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
async function openVM(url, node) {
|
async function openVM(url, node) {
|
||||||
if (connected) return;
|
if (connected) return;
|
||||||
connected = true;
|
connected = true;
|
||||||
|
@ -723,16 +757,14 @@ async function openVM(url, node) {
|
||||||
await vm.connectToVM(node);
|
await vm.connectToVM(node);
|
||||||
vmlist.style.display = "none";
|
vmlist.style.display = "none";
|
||||||
vmview.style.display = "block";
|
vmview.style.display = "block";
|
||||||
display.addEventListener('mousemove', (e) => vm.mouseevent(e, undefined), {capture: true})
|
addListener(display, 'mousemove', 'displayMove', (e) => vm.mouseevent(e, undefined));
|
||||||
display.addEventListener('mousedown', (e) => vm.mouseevent(e, true), {capture: true});
|
addListener(display, 'mousedown', 'displayDown', (e) => vm.mouseevent(e, true));
|
||||||
display.addEventListener('mouseup', (e) => vm.mouseevent(e, false), {capture: true});
|
addListener(display, 'mouseup', 'displayUp', (e) => vm.mouseevent(e, false));
|
||||||
display.addEventListener('wheel', (e) => {vm.mousewheelhandler(e);e.preventDefault();return false;}, {capture: true});
|
addListener(display, 'wheel', 'displayWheel', (e) => {vm.mousewheelhandler(e);e.preventDefault();return false;}); // BUG: mousewheelhandler seems to be broken!
|
||||||
display.addEventListener('contextmenu', (e) => e.preventDefault());
|
addListener(display, 'contextmenu', 'displayContextMenu', (e) => e.preventDefault());
|
||||||
display.addEventListener('click', () => {
|
addListener(display, 'click', 'displayClick', () => { if (turn === -1) vm.turn(); });
|
||||||
if (turn === -1) vm.turn();
|
addListener(display, 'keydown', 'displayKeyDown', (e) => vm.keyevent(e, true));
|
||||||
}, {capture: true});
|
addListener(display, 'keyup', 'displayKeyUp', (e) => vm.keyevent(e, false));
|
||||||
display.addEventListener('keydown', (e) => vm.keyevent(e, true), {capture: true});
|
|
||||||
display.addEventListener('keyup', (e) => vm.keyevent(e, false), {capture: true});
|
|
||||||
}
|
}
|
||||||
function screenshotVM() {
|
function screenshotVM() {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
|
@ -761,7 +793,16 @@ function cleanup() {
|
||||||
usernameSpan.classList = "input-group-text bg-dark text-light";
|
usernameSpan.classList = "input-group-text bg-dark text-light";
|
||||||
display.height = 0;
|
display.height = 0;
|
||||||
display.width = 0;
|
display.width = 0;
|
||||||
|
removeListener(display, 'mousemove', 'displayMove');
|
||||||
|
removeListener(display, 'mousedown', 'displayDown');
|
||||||
|
removeListener(display, 'mouseup', 'displayUp');
|
||||||
|
removeListener(display, 'wheel', 'displayWheel');
|
||||||
|
removeListener(display, 'contextmenu', 'displayContextMenu');
|
||||||
|
removeListener(display, 'click', 'displayClick');
|
||||||
|
removeListener(display, 'keydown', 'displayKeyDown');
|
||||||
|
removeListener(display, 'keyup', 'displayKeyUp');
|
||||||
}
|
}
|
||||||
|
buttons.home.addEventListener('click', async () => returnToVMList());
|
||||||
buttons.screenshot.addEventListener('click', async () => {
|
buttons.screenshot.addEventListener('click', async () => {
|
||||||
var blob = await screenshotVM();
|
var blob = await screenshotVM();
|
||||||
var url = URL.createObjectURL(blob);
|
var url = URL.createObjectURL(blob);
|
||||||
|
|
|
@ -100,6 +100,63 @@ function key_identifier_sane(keyCode, keyIdentifier) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var keycodeKeysyms = {
|
||||||
|
8: [0xFF08], // backspace
|
||||||
|
9: [0xFF09], // tab
|
||||||
|
12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5
|
||||||
|
13: [0xFF0D], // enter
|
||||||
|
16: [0xFFE1, 0xFFE1, 0xFFE2], // shift
|
||||||
|
17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl
|
||||||
|
18: [0xFFE9, 0xFFE9, 0xFE03], // alt
|
||||||
|
19: [0xFF13], // pause/break
|
||||||
|
20: [0xFFE5], // caps lock
|
||||||
|
27: [0xFF1B], // escape
|
||||||
|
32: [0x0020], // space
|
||||||
|
33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9
|
||||||
|
34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3
|
||||||
|
35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1
|
||||||
|
36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7
|
||||||
|
37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4
|
||||||
|
38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8
|
||||||
|
39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6
|
||||||
|
40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2
|
||||||
|
45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0
|
||||||
|
46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal
|
||||||
|
91: [0xFFEB], // left window key (hyper_l)
|
||||||
|
92: [0xFF67], // right window key (menu key?)
|
||||||
|
93: null, // select key
|
||||||
|
96: [0xFFB0], // KP 0
|
||||||
|
97: [0xFFB1], // KP 1
|
||||||
|
98: [0xFFB2], // KP 2
|
||||||
|
99: [0xFFB3], // KP 3
|
||||||
|
100: [0xFFB4], // KP 4
|
||||||
|
101: [0xFFB5], // KP 5
|
||||||
|
102: [0xFFB6], // KP 6
|
||||||
|
103: [0xFFB7], // KP 7
|
||||||
|
104: [0xFFB8], // KP 8
|
||||||
|
105: [0xFFB9], // KP 9
|
||||||
|
106: [0xFFAA], // KP multiply
|
||||||
|
107: [0xFFAB], // KP add
|
||||||
|
109: [0xFFAD], // KP subtract
|
||||||
|
110: [0xFFAE], // KP decimal
|
||||||
|
111: [0xFFAF], // KP divide
|
||||||
|
112: [0xFFBE], // f1
|
||||||
|
113: [0xFFBF], // f2
|
||||||
|
114: [0xFFC0], // f3
|
||||||
|
115: [0xFFC1], // f4
|
||||||
|
116: [0xFFC2], // f5
|
||||||
|
117: [0xFFC3], // f6
|
||||||
|
118: [0xFFC4], // f7
|
||||||
|
119: [0xFFC5], // f8
|
||||||
|
120: [0xFFC6], // f9
|
||||||
|
121: [0xFFC7], // f10
|
||||||
|
122: [0xFFC8], // f11
|
||||||
|
123: [0xFFC9], // f12
|
||||||
|
144: [0xFF7F], // num lock
|
||||||
|
145: [0xFF14], // scroll lock
|
||||||
|
225: [0xFE03] // altgraph (iso_level3_shift)
|
||||||
|
};
|
||||||
|
|
||||||
var keyidentifier_keysym = {
|
var keyidentifier_keysym = {
|
||||||
"Again": [0xFF66],
|
"Again": [0xFF66],
|
||||||
"AllCandidates": [0xFF3D],
|
"AllCandidates": [0xFF3D],
|
||||||
|
|
Loading…
Reference in a new issue