Set up the source tree for the typescript rewrite. Nothing really here just yet, just filler
This commit is contained in:
parent
f99e8bdb2e
commit
f244ae412f
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,3 +1,3 @@
|
|||
node_modules/
|
||||
dist/*.js
|
||||
dist/*.js.LICENSE.txt
|
||||
dist/
|
||||
.parcel-cache/
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"edge": "17",
|
||||
"firefox": "60",
|
||||
"chrome": "67",
|
||||
"safari": "11.1"
|
||||
},
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.6.5"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
19
package.json
19
package.json
|
|
@ -4,21 +4,22 @@
|
|||
"description": "kill me",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"serve": "serve dist/"
|
||||
"build": "parcel build --dist-dir dist src/html/index.html",
|
||||
"serve": "parcel src/html/index.html"
|
||||
},
|
||||
"author": "Elijah R",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"nanoevents": "^7.0.1",
|
||||
"serve": "^14.2.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.2",
|
||||
"nanoevents": "^7.0.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@parcel/watcher": "~2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"babel-loader": "^9.1.2"
|
||||
"parcel": "^2.11.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 408 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
|
@ -1,13 +0,0 @@
|
|||
export default function doCaptcha(sitekey) {
|
||||
return new Promise((res, rej) => {
|
||||
const modal = new bootstrap.Modal(document.getElementById('hcaptchaModal'));
|
||||
modal.show();
|
||||
hcaptcha.render("captcha-box", {
|
||||
sitekey: sitekey,
|
||||
callback: (c) => {
|
||||
modal.hide();
|
||||
res(c);
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
export const config = {
|
||||
serverAddresses: [
|
||||
"wss://computernewb.com/collab-vm/vm0",
|
||||
"wss://computernewb.com/collab-vm/vm1",
|
||||
"wss://computernewb.com/collab-vm/vm2",
|
||||
"wss://computernewb.com/collab-vm/vm3",
|
||||
"wss://computernewb.com/collab-vm/vm4",
|
||||
"wss://computernewb.com/collab-vm/vm5",
|
||||
"wss://computernewb.com/collab-vm/vm6",
|
||||
"wss://computernewb.com/collab-vm/vm7",
|
||||
"wss://computernewb.com/collab-vm/vm8",
|
||||
"wss://computernewb.com/collab-vm/vm9",
|
||||
"wss://computernewb.com/collab-vm/eventvm",
|
||||
],
|
||||
chatSound: "https://computernewb.com/collab-vm/notify.ogg",
|
||||
// What XSS implementation the server uses
|
||||
// 0: No XSS (cvm1.2.11)
|
||||
// 1: Internal fork style (cvm1.ts, global opcode 21)
|
||||
// 2: yellows111/collab-vm-server style (per-user opcode 21)
|
||||
xssImplementation: 1,
|
||||
}
|
||||
|
|
@ -4,10 +4,10 @@
|
|||
<title>Control Collaborative Virtual Machines!</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link href="style.css" rel="stylesheet" type="text/css"/>
|
||||
<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="../css/style.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="../../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
|
||||
<script src="https://kit.fontawesome.com/7add23c1ae.js" crossorigin="anonymous"></script>
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<link rel="icon" href="../assets/favicon.ico">
|
||||
<meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/>
|
||||
<!-- Opengraph shit -->
|
||||
<meta property="og:type" content="website"/>
|
||||
|
|
@ -109,25 +109,6 @@
|
|||
<li class="nav-item">
|
||||
<a href="https://computernewb.com/collab-vm/user-vm" class="nav-link"><i class="fa-solid fa-user"></i> UserVM</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a href="#" class="nav-link dropdown-toggle role="button" data-bs-toggle="dropdown" id="navbarDropdown"><i class="fa-solid fa-globe"></i> Languages</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="lang/ar/"><img src="../vncresolver/flags/sa.png"> اللغة العربية (جزئية)</a></li>
|
||||
<li><a class="dropdown-item" href="lang/de/"><img src="../vncresolver/flags/de.png"> Deutsch</a></li>
|
||||
<li><a class="dropdown-item" href="lang/en"><img src="../vncresolver/flags/gb.png"> English</a></li>
|
||||
<li><a class="dropdown-item" href="https://computernewb.com/collab-vm/lang/es/"><img src="https://computernewb.com/vncresolver/flags/es.png"> Español</a></li>
|
||||
<li><a class="dropdown-item" href="lang/jp/"><img src="../vncresolver/flags/jp.png"> 日本語</a></li>
|
||||
<li><a class="dropdown-item" href="lang/pl/"><img src="../vncresolver/flags/pl.png"> Polski</a></li>
|
||||
<li><a class="dropdown-item" href="lang/ru/"><img src="../vncresolver/flags/ru.png"> Русский</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<!--<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-globe"> Languages</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="ru/">Русский</a></li>
|
||||
</ul>
|
||||
</li>-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -194,7 +175,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<script src="main.js" type="application/javascript"></script>
|
||||
<script src="../../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="../ts/main.ts" type="application/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
908
src/index.js
908
src/index.js
|
|
@ -1,908 +0,0 @@
|
|||
import { guacutils } from "./protocol";
|
||||
import { config } from "./common";
|
||||
import { GetKeysym } from "./keyboard";
|
||||
import { createNanoEvents } from "nanoevents";
|
||||
import { makeperms } from "./permissions";
|
||||
import doCaptcha from "./captcha";
|
||||
// None = -1
|
||||
// Has turn = 0
|
||||
// In queue = <queue position>
|
||||
var turn = -1;
|
||||
var perms = makeperms(0, config);
|
||||
var rank = 0;
|
||||
var connected = false;
|
||||
const vms = [];
|
||||
const users = [];
|
||||
const buttons = {
|
||||
home: window.document.getElementById("homeBtn"),
|
||||
takeTurn: window.document.getElementById("takeTurnBtn"),
|
||||
changeUsername: window.document.getElementById("changeUsernameBtn"),
|
||||
voteReset: window.document.getElementById("voteResetButton"),
|
||||
screenshot: window.document.getElementById("screenshotButton"),
|
||||
// Staff
|
||||
restore: window.document.getElementById("restoreBtn"),
|
||||
reboot: window.document.getElementById("rebootBtn"),
|
||||
clearQueue: window.document.getElementById("clearQueueBtn"),
|
||||
bypassTurn: window.document.getElementById("bypassTurnBtn"),
|
||||
endTurn: window.document.getElementById("endTurnBtn"),
|
||||
qemuMonitor: window.document.getElementById("qemuMonitorBtn"),
|
||||
qemuMonitorSend: window.document.getElementById("qemuMonitorSendBtn"),
|
||||
sendChat: window.document.getElementById("sendChatBtn"),
|
||||
ctrlAltDel: window.document.getElementById("ctrlAltDelBtn"),
|
||||
forceVoteYes: window.document.getElementById("forceVoteYesBtn"),
|
||||
forceVoteNo: window.document.getElementById("forceVoteNoBtn"),
|
||||
}
|
||||
var hasTurn = false;
|
||||
var vm;
|
||||
var voteinterval;
|
||||
var turninterval;
|
||||
const chatsound = new Audio(config.chatSound);
|
||||
// Elements
|
||||
const turnstatus = window.document.getElementById("turnstatus");
|
||||
const vmlist = window.document.getElementById("vmlist");
|
||||
const vmview = window.document.getElementById("vmview");
|
||||
const display = window.document.getElementById("display");
|
||||
const displayCtx = display.getContext("2d");
|
||||
const chatList = window.document.getElementById("chatList");
|
||||
const userlist = window.document.getElementById("userlist");
|
||||
const usernameSpan = window.document.getElementById("username");
|
||||
const onlineusercount = window.document.getElementById("onlineusercount");
|
||||
const chatinput = window.document.getElementById("chat-input");
|
||||
const voteresetpanel = document.getElementById("voteResetPanel");
|
||||
const voteyesbtn = document.getElementById("voteYesBtn");
|
||||
const votenobtn = document.getElementById("voteNoBtn");
|
||||
const voteyeslabel = document.getElementById("voteYesLabel");
|
||||
const votenolabel = document.getElementById("voteNoLabel");
|
||||
const votetime = document.getElementById("votetime");
|
||||
const staffbtns = document.getElementById("staffbtns");
|
||||
const qemuMonitorInput = document.getElementById("qemuMonitorInput");
|
||||
const qemuMonitorOutput = document.getElementById("qemuMonitorOutput");
|
||||
const xssCheckbox = document.getElementById("xssCheckbox");
|
||||
const xssCheckboxContainer = document.getElementById("xssCheckboxContainer");
|
||||
const forceVotePanel = document.getElementById("forceVotePanel");
|
||||
// needed to scroll to bottom
|
||||
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 {
|
||||
eventemitter = createNanoEvents();
|
||||
socket;
|
||||
node;
|
||||
#url;
|
||||
#captcha = false;
|
||||
captchaToken;
|
||||
isMainSocket;
|
||||
shouldReconnect = true;
|
||||
constructor(url, isMainSocket) {
|
||||
this.#url = url;
|
||||
this.isMainSocket = isMainSocket;
|
||||
}
|
||||
connect(hcaptchatoken) {
|
||||
this.captchaToken = hcaptchatoken;
|
||||
return new Promise((res, rej) => {
|
||||
try {
|
||||
this.socket = new WebSocket(this.#url, "guacamole");
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
this.socket.addEventListener('message', (e) => this.#onMessage(e));
|
||||
this.socket.addEventListener('open', () => res(true), {once: true});
|
||||
this.socket.addEventListener('close', (e) => { if(!e.wasClean) res(false); }, {once: true});
|
||||
})
|
||||
|
||||
}
|
||||
#onClose() {
|
||||
cleanup();
|
||||
if(this.shouldReconnect) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
connected = await this.connect(this.captchaToken);
|
||||
} catch {
|
||||
this.#onClose();
|
||||
}
|
||||
this.connectToVM(this.node);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
disconnect() {
|
||||
this.socket.send(guacutils.encode(["disconnect"]));
|
||||
this.socket.close();
|
||||
}
|
||||
getUrl() {
|
||||
return this.#url;
|
||||
}
|
||||
connectToVM(node) {
|
||||
return new Promise(async (res, rej) => {
|
||||
this.socket.addEventListener('close', () => this.#onClose());
|
||||
this.node = node;
|
||||
if (this.captchaToken !== null) {
|
||||
await new Promise((reso, reje) => {
|
||||
var unbind = this.eventemitter.on('captcha', (result) => {
|
||||
unbind();
|
||||
if (result === true) {
|
||||
reso();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
reje();
|
||||
}
|
||||
});
|
||||
this.socket.send(guacutils.encode(["captcha", this.captchaToken]));
|
||||
});
|
||||
}
|
||||
var savedUsername = window.localStorage.getItem("username");
|
||||
if (savedUsername === null)
|
||||
this.socket.send(guacutils.encode(["rename"]));
|
||||
else this.socket.send(guacutils.encode(["rename", savedUsername]));
|
||||
var unbind = this.eventemitter.on('connect', () => {
|
||||
unbind();
|
||||
res();
|
||||
});
|
||||
var failunbind = this.eventemitter.on('connectfail', () => {
|
||||
failunbind();
|
||||
rej();
|
||||
});
|
||||
this.socket.send(guacutils.encode(["connect", node]));
|
||||
var pass = window.localStorage.getItem("password_"+this.#url);
|
||||
if (pass)
|
||||
this.admin.login(pass);
|
||||
});
|
||||
}
|
||||
async #onMessage(event) {
|
||||
var msgArr = guacutils.decode(event.data);
|
||||
window.cvmEvents.emit(msgArr[0], msgArr.slice(1));
|
||||
switch (msgArr[0]) {
|
||||
case "nop":
|
||||
this.socket.send("3.nop;");
|
||||
break;
|
||||
case "connect":
|
||||
switch (msgArr[1]) {
|
||||
case "0":
|
||||
this.eventemitter.emit('connectfail');
|
||||
break;
|
||||
case "1":
|
||||
this.eventemitter.emit('connect');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "captcha":
|
||||
switch (msgArr[1]) {
|
||||
case "0":
|
||||
this.#captcha = msgArr[2];
|
||||
console.log(this.#captcha);
|
||||
break;
|
||||
case "1":
|
||||
this.eventemitter.emit('captcha', true);
|
||||
break;
|
||||
case "2":
|
||||
this.eventemitter.emit('captcha', false);
|
||||
}
|
||||
case "chat":
|
||||
if (!connected || !this.isMainSocket) return;
|
||||
for (var i = 1; i < msgArr.length; i += 2) {
|
||||
chatMessage(msgArr[i], msgArr[i+1])
|
||||
}
|
||||
chatsound.play();
|
||||
chatListDiv.scrollTop = chatListDiv.scrollHeight;
|
||||
break;
|
||||
case "list":
|
||||
var list = [];
|
||||
for (var i = 1; i < msgArr.length; i+=3) {
|
||||
list.push({
|
||||
url: this.#url,
|
||||
id: msgArr[i],
|
||||
name: msgArr[i+1],
|
||||
thumb: msgArr[i+2],
|
||||
captcha: this.#captcha,
|
||||
|
||||
});
|
||||
}
|
||||
this.eventemitter.emit('list', list);
|
||||
break;
|
||||
case "size":
|
||||
if (!connected || msgArr[1] !== "0") return;
|
||||
display.width = msgArr[2];
|
||||
display.height = msgArr[3];
|
||||
break;
|
||||
case "png":
|
||||
if (!connected || msgArr[2] !== "0") return;
|
||||
var img = new Image(display.width, display.height);
|
||||
img.addEventListener('load', () => {
|
||||
displayCtx.drawImage(img, msgArr[3], msgArr[4]);
|
||||
});
|
||||
img.src = "data:image/png;base64," + msgArr[5];
|
||||
break;
|
||||
case "rename":
|
||||
if (msgArr[1] === "0") {
|
||||
switch (msgArr[2]) {
|
||||
case "1":
|
||||
alert("That username is already taken");
|
||||
break;
|
||||
case "2":
|
||||
alert("Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and it must be between 3 and 20 characters.");
|
||||
break;
|
||||
case "3":
|
||||
alert("That username has been blacklisted.");
|
||||
break;
|
||||
}
|
||||
if (!connected || !this.isMainSocket) return;
|
||||
var u = users.find(u => u.username === window.username);
|
||||
if (u) {
|
||||
u.username = msgArr[3];
|
||||
u.element.children[0].innerHTML = msgArr[3];
|
||||
}
|
||||
window.username = msgArr[3];
|
||||
usernameSpan.innerText = msgArr[3];
|
||||
window.localStorage.setItem("username", msgArr[3]);
|
||||
return;
|
||||
}
|
||||
var user = users.find(u => u.username == msgArr[2]);
|
||||
if (user === undefined) break;
|
||||
user.username = msgArr[3];
|
||||
user.element.children[0].innerHTML = msgArr[3];
|
||||
break;
|
||||
case "adduser":
|
||||
if (!connected || !this.isMainSocket) return;
|
||||
for (var i = 2; i < msgArr.length; i += 2) {
|
||||
this.addUser(msgArr[i], msgArr[i+1]);
|
||||
}
|
||||
onlineusercount.innerText = users.length;
|
||||
break;
|
||||
case "remuser":
|
||||
if (!connected || !this.isMainSocket) return;
|
||||
for (var i = 2; i < msgArr.length; i++) {
|
||||
var user = users.find(u => u.username == msgArr[i]);
|
||||
users.splice(users.indexOf(user), 1);
|
||||
userlist.removeChild(user.element);
|
||||
}
|
||||
onlineusercount.innerText = users.length;
|
||||
break;
|
||||
|
||||
case "turn":
|
||||
// Reset all turn data
|
||||
users.forEach((curr) => {
|
||||
curr.turn = -1;
|
||||
curr.element.classList = "";
|
||||
});
|
||||
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> Take Turn";
|
||||
turn = -1;
|
||||
if (!msgArr.includes(username))
|
||||
turnstatus.innerText = "";
|
||||
display.className = "";
|
||||
clearInterval(turninterval);
|
||||
// Get the number of users queued for a turn
|
||||
var queuedUsers = Number(msgArr[2]);
|
||||
if (queuedUsers === 0) return;
|
||||
var currentTurnUsername = msgArr[3];
|
||||
// Get the user who has the turn and highlight them
|
||||
var currentTurnUser = users.find(u => u.username === currentTurnUsername);
|
||||
currentTurnUser.element.classList = "table-primary";
|
||||
currentTurnUser.turn = 0;
|
||||
if (currentTurnUsername === window.username) {
|
||||
turn = 0;
|
||||
var secs = Math.floor(parseInt(msgArr[1]) / 1000);
|
||||
var turnUpdate = () => {
|
||||
secs--;
|
||||
if (secs === 0)
|
||||
clearInterval(turninterval);
|
||||
turnstatus.innerText = `Turn expires in ${secs} seconds.`;
|
||||
}
|
||||
turnUpdate();
|
||||
turninterval = setInterval(turnUpdate, 1000);
|
||||
display.className = "focused";
|
||||
}
|
||||
// Highlight all waiting users and set their status
|
||||
if (queuedUsers > 1) {
|
||||
for (var i = 1; i < queuedUsers; i++) {
|
||||
if (window.username === msgArr[i+3]) {
|
||||
turn = i;
|
||||
var secs = Math.floor(parseInt(msgArr[msgArr.length-1]) / 1000);
|
||||
var turnUpdate = () => {
|
||||
secs--;
|
||||
if (secs === 0)
|
||||
clearInterval(turninterval);
|
||||
turnstatus.innerText = `Waiting for turn in ${secs} seconds.`;
|
||||
}
|
||||
turninterval = setInterval(turnUpdate, 1000);
|
||||
turnUpdate();
|
||||
display.className = "waiting";
|
||||
};
|
||||
var user = users.find(u => u.username === msgArr[i+3]);
|
||||
user.turn = i;
|
||||
user.element.classList = "table-warning";
|
||||
}
|
||||
}
|
||||
if (turn === -1) {
|
||||
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> Take Turn";
|
||||
} else {
|
||||
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> End Turn";
|
||||
}
|
||||
this.reloadUsers();
|
||||
break;
|
||||
case "vote":
|
||||
switch (msgArr[1]) {
|
||||
case "0":
|
||||
// Vote started
|
||||
case "1":
|
||||
// Vote updated
|
||||
voteresetpanel.style.display = "block";
|
||||
voteyeslabel.innerText = msgArr[3];
|
||||
votenolabel.innerText = msgArr[4];
|
||||
if (voteinterval)
|
||||
clearInterval(voteinterval);
|
||||
var timeToEnd = Math.floor(parseInt(msgArr[2]) / 1000);
|
||||
var updateVote = () => {
|
||||
timeToEnd--;
|
||||
if (timeToEnd === 0)
|
||||
clearInterval(voteinterval);
|
||||
votetime.innerText = timeToEnd;
|
||||
}
|
||||
voteinterval = setInterval(updateVote, 1000);
|
||||
updateVote();
|
||||
break;
|
||||
case "2":
|
||||
// Vote ended
|
||||
voteresetpanel.style.display = "none";
|
||||
break;
|
||||
case "3":
|
||||
// too soon dumbass
|
||||
window.alert(`Please wait ${msgArr[2]} seconds before starting another vote.`);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "admin":
|
||||
switch (msgArr[1]) {
|
||||
case "0":
|
||||
// Login
|
||||
switch (msgArr[2]) {
|
||||
case "0":
|
||||
this.eventemitter.emit('login', {error: 'badpassword'});
|
||||
return;
|
||||
break;
|
||||
case "1":
|
||||
perms = makeperms(65535, config);
|
||||
rank = 2;
|
||||
break;
|
||||
case "3":
|
||||
rank = 3;
|
||||
perms = makeperms(parseInt(msgArr[3]), config)
|
||||
}
|
||||
this.eventemitter.emit('login', {perms: perms, rank: rank});
|
||||
usernameSpan.classList.remove("text-light");
|
||||
switch (rank) {
|
||||
case 2:
|
||||
usernameSpan.classList.add("text-danger");
|
||||
break;
|
||||
case 3:
|
||||
usernameSpan.classList.add("text-success");
|
||||
break;
|
||||
}
|
||||
// Disabled for now until we figure out the issue of uservm
|
||||
//window.localStorage.setItem("password_"+this.#url, password);
|
||||
staffbtns.style.display = "block";
|
||||
if (perms.restore) buttons.restore.style.display = "inline-block";
|
||||
if (perms.reboot) buttons.reboot.style.display = "inline-block";
|
||||
if (perms.bypassturn) {
|
||||
buttons.bypassTurn.style.display = "inline-block";
|
||||
buttons.clearQueue.style.display = "inline-block";
|
||||
buttons.endTurn.style.display = "inline-block";
|
||||
}
|
||||
if (rank === 2) buttons.qemuMonitor.style.display = "inline-block";
|
||||
if ((config.xssImplementation === 2 && perms.xss) || (rank === 2 && config.xssImplementation === 1)) {
|
||||
xssCheckboxContainer.style.display = "inline-block";
|
||||
}
|
||||
if (perms.forcevote) forceVotePanel.style.display = "block";
|
||||
users.forEach((u) => userModOptions(u.username, u.element, u.element.children[0]));
|
||||
break;
|
||||
case "19":
|
||||
// Got IP
|
||||
this.eventemitter.emit('ip', {username: msgArr[2], ip: msgArr[3]});
|
||||
break;
|
||||
case "2":
|
||||
// QEMU output
|
||||
qemuMonitorOutput.innerHTML += `> ${msgArr[2]}\n`;
|
||||
qemuMonitorOutput.scrollTop = qemuMonitorOutput.scrollHeight;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
addUser(name, urank) {
|
||||
var olduser = users.find(u => u.username === name);
|
||||
if (olduser !== undefined) {
|
||||
users.splice(users.indexOf(olduser), 1);
|
||||
userlist.removeChild(olduser.element);
|
||||
}
|
||||
var user = {
|
||||
username: name,
|
||||
rank: Number(urank),
|
||||
turn: -1
|
||||
};
|
||||
users.push(user);
|
||||
var tr = document.createElement("tr");
|
||||
var td = document.createElement("td");
|
||||
td.innerHTML = name;
|
||||
switch (user.rank) {
|
||||
case 2:
|
||||
td.style.color = "#FF0000";
|
||||
break;
|
||||
case 3:
|
||||
td.style.color = "#00FF00";
|
||||
break;
|
||||
}
|
||||
tr.appendChild(td);
|
||||
user.element = tr;
|
||||
if (rank !== 0) userModOptions(user.username, tr, td);
|
||||
userlist.appendChild(tr);
|
||||
}
|
||||
reloadUsers() {
|
||||
// Sort the user list by turn status
|
||||
users.sort((a, b) => {
|
||||
if (a.turn === b.turn) return 0;
|
||||
if (a.turn === -1) return 1;
|
||||
if (b.turn === -1) return -1;
|
||||
if (a.turn < b.turn) return -1;
|
||||
else return 1;
|
||||
});
|
||||
users.forEach((u) => {
|
||||
userlist.removeChild(u.element);
|
||||
userlist.appendChild(u.element);
|
||||
});
|
||||
}
|
||||
async list() {
|
||||
return new Promise((res, rej) => {
|
||||
var unbind = this.eventemitter.on('list', (e) => {
|
||||
unbind();
|
||||
res(e);
|
||||
})
|
||||
this.socket.send("4.list;");
|
||||
});
|
||||
}
|
||||
chat(msg) {
|
||||
this.socket.send(guacutils.encode(["chat", msg]));
|
||||
}
|
||||
rename(username) {
|
||||
this.socket.send(guacutils.encode(["rename", username]));
|
||||
}
|
||||
turn() {
|
||||
if (turn === -1) {
|
||||
this.socket.send(guacutils.encode(["turn", "1"]))
|
||||
} else {
|
||||
this.socket.send(guacutils.encode(["turn", "0"]));
|
||||
}
|
||||
}
|
||||
mouse(x, y, mask) {
|
||||
this.socket.send(guacutils.encode(["mouse", x, y, mask]));
|
||||
}
|
||||
key(keysym, down) {
|
||||
this.socket.send(guacutils.encode(["key", keysym, down ? "1" : "0"]));
|
||||
}
|
||||
mousewheelhandler(e) {
|
||||
// gutted from guac source code
|
||||
var delta = e.deltaY || -e.wheelDeltaY || -e.wheelDelta;
|
||||
if (!delta) return;
|
||||
if (e.deltaMode === 1)
|
||||
delta = e.deltaY * 40;
|
||||
// Convert to pixels if delta was pages
|
||||
else if (e.deltaMode === 2)
|
||||
delta = e.deltaY * 640;
|
||||
// Up
|
||||
while (delta <= -120) {
|
||||
this.mousestate.scrollup = true;
|
||||
this.sendmouse();
|
||||
this.mousestate.scrollup = false;
|
||||
this.sendmouse();
|
||||
delta += 120;
|
||||
}
|
||||
// Down
|
||||
while (delta >= 120) {
|
||||
this.mousestate.scrolldown = true;
|
||||
this.sendmouse();
|
||||
this.mousestate.scrolldown = false;
|
||||
this.sendmouse();
|
||||
delta -= 120;
|
||||
}
|
||||
}
|
||||
mousestate = {
|
||||
left: false,
|
||||
middle: false,
|
||||
right: false,
|
||||
scrolldown: false,
|
||||
scrollup: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
}
|
||||
makemousemask() {
|
||||
var mask = 0;
|
||||
if (this.mousestate.left) mask |= 1;
|
||||
if (this.mousestate.middle) mask |= 2;
|
||||
if (this.mousestate.right) mask |= 4;
|
||||
if (this.mousestate.scrollup) mask |= 8;
|
||||
if (this.mousestate.scrolldown) mask |= 16;
|
||||
return mask;
|
||||
}
|
||||
mouseevent(e, down) {
|
||||
if (down !== undefined) {switch (e.button) {
|
||||
case 0:
|
||||
this.mousestate.left = down;
|
||||
break;
|
||||
case 1:
|
||||
this.mousestate.middle = down;
|
||||
break;
|
||||
case 2:
|
||||
this.mousestate.right = down;
|
||||
break;
|
||||
}}
|
||||
this.mousestate.x = e.offsetX;
|
||||
this.mousestate.y = e.offsetY;
|
||||
this.sendmouse();
|
||||
}
|
||||
sendmouse() {
|
||||
var mask = this.makemousemask();
|
||||
this.mouse(this.mousestate.x, this.mousestate.y, mask);
|
||||
}
|
||||
keyevent(e, down) {
|
||||
e.preventDefault();
|
||||
var keysym = GetKeysym(e.keyCode, e.keyIdentifier, e.key, e.location);
|
||||
if (keysym === undefined) return;
|
||||
this.key(keysym, down);
|
||||
}
|
||||
voteReset(reset) {
|
||||
this.socket.send(guacutils.encode(["vote", reset ? "1" : "0"]));
|
||||
}
|
||||
admin = {
|
||||
login: (password) => {
|
||||
return new Promise((res, rej) => {
|
||||
var unbind = this.eventemitter.on('login', (args) => {
|
||||
unbind();
|
||||
if (args.error) rej(error);
|
||||
res(args);
|
||||
})
|
||||
this.socket.send(guacutils.encode(["admin", "2", password]));
|
||||
});
|
||||
},
|
||||
adminInstruction: (...args) => { // Compatibility
|
||||
args.unshift("admin");
|
||||
this.socket.send(guacutils.encode(args));
|
||||
},
|
||||
restore: () => this.socket.send(guacutils.encode(["admin", "8", this.node])),
|
||||
reboot: () => this.socket.send(guacutils.encode(["admin", "10", this.node])),
|
||||
clearQueue: () => this.socket.send(guacutils.encode(["admin", "17", this.node])),
|
||||
bypassTurn: () => this.socket.send(guacutils.encode(["admin", "20"])),
|
||||
endTurn: (user) => this.socket.send(guacutils.encode(["admin", "16", user])),
|
||||
ban: (user) => this.socket.send(guacutils.encode(["admin", "12", user])),
|
||||
kick: (user) => this.socket.send(guacutils.encode(["admin", "15", user])),
|
||||
renameUser: (user, newname) => this.socket.send(guacutils.encode(["admin", "18", user, newname])),
|
||||
mute: (user, mutestate) => this.socket.send(guacutils.encode(["admin", "14", user, mutestate])),
|
||||
getip: (user) => {
|
||||
if (users.find(u => u.username === user) === undefined) return;
|
||||
return new Promise((res, rej) => {
|
||||
var unbind = this.eventemitter.on('ip', (args) => {
|
||||
if (args.username !== user) return;
|
||||
unbind();
|
||||
res(args.ip);
|
||||
});
|
||||
this.socket.send(guacutils.encode(["admin", "19", user]));
|
||||
});
|
||||
},
|
||||
qemuMonitor: (cmd) => this.socket.send(guacutils.encode(["admin", "5", this.node, cmd])),
|
||||
globalXss: (msg) => {
|
||||
switch (config.xssImplementation) {
|
||||
case 1:
|
||||
this.socket.send(guacutils.encode(["admin", "21", msg]));
|
||||
break;
|
||||
case 2:
|
||||
users.forEach((u) => this.socket.send(guacutils.encode(["admin", "21", u.username, msg])));
|
||||
break;
|
||||
}
|
||||
},
|
||||
userXss: (user, msg) => {
|
||||
if (config.xssImplementation !== 2 || !users.find(u => u.username === user)) return;
|
||||
this.socket.send(guacutils.encode(["admin", "21", user, msg]));
|
||||
},
|
||||
forceVote: (result) => {
|
||||
this.socket.send(guacutils.encode(["admin", "13", result ? "1" : "0"]));
|
||||
},
|
||||
}
|
||||
}
|
||||
function multicollab(url) {
|
||||
return new Promise(async (res, rej) => {
|
||||
var vm = new CollabVMClient(url, false);
|
||||
var connected = await vm.connect();
|
||||
if(!connected) return res(false);
|
||||
var list = await vm.list();
|
||||
vm.disconnect();
|
||||
list.forEach((curr) => {
|
||||
var id = curr.id;
|
||||
var name = curr.name;
|
||||
vms.push(curr);
|
||||
var div = document.createElement("div");
|
||||
div.classList = "col-sm-5 col-md-3";
|
||||
var card = document.createElement("div");
|
||||
card.classList = "card bg-dark text-light";
|
||||
card.setAttribute("data-cvm-node", id);
|
||||
card.addEventListener("click", () => openVM(url, id));
|
||||
var img = document.createElement("img");
|
||||
img.src = "data:image/png;base64," + curr.thumb;
|
||||
img.classList = "card-img-top";
|
||||
var bdy = document.createElement("div");
|
||||
bdy.classList = "card-body";
|
||||
var desc = document.createElement("h5");
|
||||
desc.innerHTML = name;
|
||||
bdy.appendChild(desc);
|
||||
card.appendChild(img);
|
||||
card.appendChild(bdy);
|
||||
div.appendChild(card);
|
||||
curr.element = div;
|
||||
reloadVMList();
|
||||
});
|
||||
res(true);
|
||||
});
|
||||
}
|
||||
function reloadVMList() {
|
||||
vms.sort(function(a, b) {
|
||||
return a.id > b.id ? 1 : -1;
|
||||
});
|
||||
vmlist.children[0].innerHTML = "";
|
||||
vms.forEach((v) => vmlist.children[0].appendChild(v.element));
|
||||
}
|
||||
function chatMessage(user, msg) {
|
||||
var tr = document.createElement("tr");
|
||||
var td = document.createElement("td");
|
||||
if (user == "" || user === undefined)
|
||||
td.innerHTML = msg;
|
||||
else {
|
||||
var u = users.find(u => u.username === user);
|
||||
var userclass;
|
||||
if (u) switch (u.rank) {
|
||||
case 2:
|
||||
userclass = "text-danger";
|
||||
break;
|
||||
case 3:
|
||||
userclass = "text-success";
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
userclass = "text-light";
|
||||
break;
|
||||
}
|
||||
else userclass = "text-light";
|
||||
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);
|
||||
chatList.appendChild(tr);
|
||||
}
|
||||
function userModOptions(user, tr, td) {
|
||||
tr.classList.add("dropdown");
|
||||
td.classList.add("dropdown-toggle");
|
||||
td.setAttribute("data-bs-toggle", "dropdown");
|
||||
td.setAttribute("role", "button");
|
||||
td.setAttribute("aria-expanded", "false");
|
||||
var ul = document.createElement("ul");
|
||||
ul.classList = "dropdown-menu dropdown-menu-dark table-dark text-light";
|
||||
|
||||
if (perms.bypassturn) addUserDropdownItem(ul, "End Turn", () => vm.admin.endTurn(user));
|
||||
if (perms.ban) addUserDropdownItem(ul, "Ban", () => vm.admin.ban(user));
|
||||
if (perms.kick) addUserDropdownItem(ul, "Kick", () => vm.admin.kick(user));
|
||||
if (perms.rename) addUserDropdownItem(ul, "Rename", () => {
|
||||
var newname = window.prompt(`Enter new username for ${user}`);
|
||||
if (newname == null) return;
|
||||
vm.admin.renameUser(user, newname)
|
||||
});
|
||||
if (perms.mute) {
|
||||
addUserDropdownItem(ul, "Temporary Mute", () => vm.admin.mute(user, 0));
|
||||
addUserDropdownItem(ul, "Indefinite Mute", () => vm.admin.mute(user, 1));
|
||||
addUserDropdownItem(ul, "Unmute", () => vm.admin.mute(user, 2));
|
||||
}
|
||||
if (perms.grabip) addUserDropdownItem(ul, "Get IP", async () => {
|
||||
var ip = await vm.admin.getip(user);
|
||||
alert(ip);
|
||||
});
|
||||
if (config.xssImplementation === 2 && perms.xss) addUserDropdownItem(ul, "Direct Message (XSS)", () => {
|
||||
var msg = window.prompt("Enter message to send");
|
||||
if (!msg) return;
|
||||
vm.admin.userXss(user, msg);
|
||||
});
|
||||
tr.appendChild(ul);
|
||||
}
|
||||
function addUserDropdownItem(ul, text, func) {
|
||||
var li = document.createElement("li");
|
||||
var a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.classList.add("dropdown-item");
|
||||
a.innerHTML = text;
|
||||
a.addEventListener('click', func);
|
||||
li.appendChild(a);
|
||||
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) {
|
||||
if (connected) return;
|
||||
connected = true;
|
||||
var _vm = vms.find(v => v.url === url);
|
||||
var token = null;
|
||||
if (_vm.captcha !== false) {
|
||||
token = await doCaptcha(vm.captcha);
|
||||
}
|
||||
window.location.href = "#" + node;
|
||||
window.VMName = node;
|
||||
vm = new CollabVMClient(url, true);
|
||||
await vm.connect(token);
|
||||
await vm.connectToVM(node);
|
||||
vmlist.style.display = "none";
|
||||
vmview.style.display = "block";
|
||||
addListener(display, 'mousemove', 'displayMove', (e) => vm.mouseevent(e, undefined));
|
||||
addListener(display, 'mousedown', 'displayDown', (e) => vm.mouseevent(e, true));
|
||||
addListener(display, 'mouseup', 'displayUp', (e) => vm.mouseevent(e, false));
|
||||
addListener(display, 'wheel', 'displayWheel', (e) => {vm.mousewheelhandler(e);e.preventDefault();return false;}); // BUG: mousewheelhandler seems to be broken!
|
||||
addListener(display, 'contextmenu', 'displayContextMenu', (e) => e.preventDefault());
|
||||
addListener(display, 'click', 'displayClick', () => { if (turn === -1) vm.turn(); });
|
||||
addListener(display, 'keydown', 'displayKeyDown', (e) => vm.keyevent(e, true));
|
||||
addListener(display, 'keyup', 'displayKeyUp', (e) => vm.keyevent(e, false));
|
||||
}
|
||||
function screenshotVM() {
|
||||
return new Promise((res, rej) => {
|
||||
display.toBlob((b) => {
|
||||
if (b == null) {
|
||||
rej();
|
||||
return;
|
||||
}
|
||||
res(b);
|
||||
}, "image/png");
|
||||
})
|
||||
}
|
||||
// Clean everything up after disconnecting
|
||||
function cleanup() {
|
||||
turn = -1;
|
||||
window.username = null;
|
||||
rank = 0;
|
||||
hasTurn = false;
|
||||
if (turninterval) clearInterval(turninterval);
|
||||
if (voteinterval)
|
||||
clearInterval(voteinterval);
|
||||
users.splice(0);
|
||||
userlist.innerHTML = "";
|
||||
Array.prototype.slice.call(staffbtns.children).forEach((curr) => curr.style.display = "none");
|
||||
staffbtns.style.display = "none";
|
||||
usernameSpan.classList = "input-group-text bg-dark text-light";
|
||||
display.height = 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 () => {
|
||||
var blob = await screenshotVM();
|
||||
var url = URL.createObjectURL(blob);
|
||||
window.open(url, "_blank");
|
||||
});
|
||||
chatinput.addEventListener("keypress", (e) => {
|
||||
if (e.key == "Enter") sendChat();
|
||||
});
|
||||
buttons.sendChat.addEventListener('click', () => sendChat());
|
||||
function sendChat() {
|
||||
if (xssCheckbox.checked)
|
||||
vm.admin.globalXss(chatinput.value);
|
||||
else
|
||||
vm.chat(chatinput.value);
|
||||
chatinput.value = "";
|
||||
}
|
||||
buttons.changeUsername.addEventListener('click', () => {
|
||||
var newuser = window.prompt("Enter new username", window.username);
|
||||
if (newuser == null) return;
|
||||
vm.rename(newuser);
|
||||
});
|
||||
buttons.takeTurn.addEventListener('click', () => vm.turn());
|
||||
buttons.voteReset.addEventListener('click', () => vm.voteReset(true));
|
||||
buttons.ctrlAltDel.addEventListener('click', () => {
|
||||
// Ctrl
|
||||
vm.key(0xffe3, true);
|
||||
// Alt
|
||||
vm.key(0xffe9, true);
|
||||
// Del
|
||||
vm.key(0xffff, true);
|
||||
// Ctrl
|
||||
vm.key(0xffe3, false);
|
||||
// Alt
|
||||
vm.key(0xffe9, false);
|
||||
// Del
|
||||
vm.key(0xffff, false);
|
||||
});
|
||||
voteyesbtn.addEventListener('click', () => vm.voteReset(true));
|
||||
votenobtn.addEventListener('click', () => vm.voteReset(false));
|
||||
// Staff buttons
|
||||
buttons.restore.addEventListener('click', () => {if (window.confirm("Do you really want to restore the VM?")) vm.admin.restore()});
|
||||
buttons.reboot.addEventListener('click', () => vm.admin.reboot());
|
||||
buttons.clearQueue.addEventListener('click', () => vm.admin.clearQueue());
|
||||
buttons.bypassTurn.addEventListener('click', () => vm.admin.bypassTurn());
|
||||
buttons.endTurn.addEventListener('click', () => vm.admin.endTurn(users[0]));
|
||||
buttons.forceVoteYes.addEventListener('click', () => vm.admin.forceVote(true));
|
||||
buttons.forceVoteNo.addEventListener('click', () => vm.admin.forceVote(false));
|
||||
// QEMU Monitor Shit
|
||||
function sendQEMUCommand() {
|
||||
if (!qemuMonitorInput.value) return;
|
||||
vm.admin.qemuMonitor(qemuMonitorInput.value);
|
||||
qemuMonitorInput.value = "";
|
||||
}
|
||||
qemuMonitorInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === "Enter") sendQEMUCommand();
|
||||
});
|
||||
buttons.qemuMonitorSend.addEventListener('click', () => sendQEMUCommand());
|
||||
|
||||
// Login
|
||||
var usernameClick = false;
|
||||
usernameSpan.addEventListener('click', () => {
|
||||
if (!usernameClick) {
|
||||
usernameClick = true;
|
||||
setInterval(() => {usernameClick = false;}, 1000);
|
||||
return;
|
||||
}
|
||||
var pass = window.prompt("🔑");
|
||||
if (!pass) return;
|
||||
vm.admin.login(pass);
|
||||
});
|
||||
|
||||
// Load all vms
|
||||
(async () => {
|
||||
var p = [];
|
||||
config.serverAddresses.forEach(v => p.push(multicollab(v)));
|
||||
await Promise.all(p);
|
||||
var vm = vms.find(v => v.id === window.location.hash.substring(1));
|
||||
if (vm)
|
||||
openVM(vm.url, vm.id);
|
||||
})();
|
||||
// Export some stuff
|
||||
window.screenshotVM = screenshotVM;
|
||||
window.multicollab = multicollab;
|
||||
window.getPerms = () => perms;
|
||||
window.getRank = () => rank;
|
||||
window.GetAdmin = () => vm.admin;
|
||||
window.cvmEvents = createNanoEvents();
|
||||
window.VMName = null;
|
||||
|
||||
// Welcome modal
|
||||
var noWelcomeModal = window.localStorage.getItem("no-welcome-modal");
|
||||
if (noWelcomeModal !== "1") {
|
||||
var welcomeModalDismissBtn = document.getElementById("welcomeModalDismiss");
|
||||
var welcomeModal = new bootstrap.Modal(document.getElementById("welcomeModal"));
|
||||
welcomeModalDismissBtn.addEventListener("click", () => {
|
||||
window.localStorage.setItem("no-welcome-modal", 1);
|
||||
});
|
||||
welcomeModalDismissBtn.disabled = true;
|
||||
welcomeModal.show();
|
||||
setTimeout(() => {
|
||||
welcomeModalDismissBtn.disabled = false;
|
||||
}, 5000);
|
||||
}
|
||||
280
src/keyboard.js
280
src/keyboard.js
|
|
@ -1,280 +0,0 @@
|
|||
// Pulled a bunch of functions out of the guac source code to get a keysym
|
||||
// and then a wrapper
|
||||
// shitty but it works so /shrug
|
||||
export function GetKeysym(keyCode, keyIdentifier, key, location) {
|
||||
var keysym = keysym_from_key_identifier(key, location)
|
||||
|| keysym_from_keycode(keyCode, location);
|
||||
|
||||
if (!keysym && key_identifier_sane(keyCode, keyIdentifier))
|
||||
keysym = keysym_from_key_identifier(keyIdentifier, location);
|
||||
|
||||
return keysym;
|
||||
}
|
||||
|
||||
|
||||
function keysym_from_key_identifier(identifier, location) {
|
||||
|
||||
if (!identifier)
|
||||
return null;
|
||||
|
||||
var typedCharacter;
|
||||
|
||||
// If identifier is U+xxxx, decode Unicode character
|
||||
var unicodePrefixLocation = identifier.indexOf("U+");
|
||||
if (unicodePrefixLocation >= 0) {
|
||||
var hex = identifier.substring(unicodePrefixLocation+2);
|
||||
typedCharacter = String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
|
||||
// If single character, use that as typed character
|
||||
else if (identifier.length === 1)
|
||||
typedCharacter = identifier;
|
||||
|
||||
// Otherwise, look up corresponding keysym
|
||||
else
|
||||
return get_keysym(keyidentifier_keysym[identifier], location);
|
||||
|
||||
// Get codepoint
|
||||
var codepoint = typedCharacter.charCodeAt(0);
|
||||
return keysym_from_charcode(codepoint);
|
||||
|
||||
}
|
||||
|
||||
function get_keysym(keysyms, location) {
|
||||
|
||||
if (!keysyms)
|
||||
return null;
|
||||
|
||||
return keysyms[location] || keysyms[0];
|
||||
}
|
||||
|
||||
function keysym_from_charcode(codepoint) {
|
||||
|
||||
// Keysyms for control characters
|
||||
if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;
|
||||
|
||||
// Keysyms for ASCII chars
|
||||
if (codepoint >= 0x0000 && codepoint <= 0x00FF)
|
||||
return codepoint;
|
||||
|
||||
// Keysyms for Unicode
|
||||
if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)
|
||||
return 0x01000000 | codepoint;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function isControlCharacter(codepoint) {
|
||||
return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);
|
||||
}
|
||||
|
||||
function keysym_from_keycode(keyCode, location) {
|
||||
return get_keysym(keycodeKeysyms[keyCode], location);
|
||||
}
|
||||
|
||||
function key_identifier_sane(keyCode, keyIdentifier) {
|
||||
|
||||
// Missing identifier is not sane
|
||||
if (!keyIdentifier)
|
||||
return false;
|
||||
|
||||
// Assume non-Unicode keyIdentifier values are sane
|
||||
var unicodePrefixLocation = keyIdentifier.indexOf("U+");
|
||||
if (unicodePrefixLocation === -1)
|
||||
return true;
|
||||
|
||||
// If the Unicode codepoint isn't identical to the keyCode,
|
||||
// then the identifier is likely correct
|
||||
var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16);
|
||||
if (keyCode !== codepoint)
|
||||
return true;
|
||||
|
||||
// The keyCodes for A-Z and 0-9 are actually identical to their
|
||||
// Unicode codepoints
|
||||
if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57))
|
||||
return true;
|
||||
|
||||
// The keyIdentifier does NOT appear sane
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
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 = {
|
||||
"Again": [0xFF66],
|
||||
"AllCandidates": [0xFF3D],
|
||||
"Alphanumeric": [0xFF30],
|
||||
"Alt": [0xFFE9, 0xFFE9, 0xFE03],
|
||||
"Attn": [0xFD0E],
|
||||
"AltGraph": [0xFE03],
|
||||
"ArrowDown": [0xFF54],
|
||||
"ArrowLeft": [0xFF51],
|
||||
"ArrowRight": [0xFF53],
|
||||
"ArrowUp": [0xFF52],
|
||||
"Backspace": [0xFF08],
|
||||
"CapsLock": [0xFFE5],
|
||||
"Cancel": [0xFF69],
|
||||
"Clear": [0xFF0B],
|
||||
"Convert": [0xFF21],
|
||||
"Copy": [0xFD15],
|
||||
"Crsel": [0xFD1C],
|
||||
"CrSel": [0xFD1C],
|
||||
"CodeInput": [0xFF37],
|
||||
"Compose": [0xFF20],
|
||||
"Control": [0xFFE3, 0xFFE3, 0xFFE4],
|
||||
"ContextMenu": [0xFF67],
|
||||
"DeadGrave": [0xFE50],
|
||||
"DeadAcute": [0xFE51],
|
||||
"DeadCircumflex": [0xFE52],
|
||||
"DeadTilde": [0xFE53],
|
||||
"DeadMacron": [0xFE54],
|
||||
"DeadBreve": [0xFE55],
|
||||
"DeadAboveDot": [0xFE56],
|
||||
"DeadUmlaut": [0xFE57],
|
||||
"DeadAboveRing": [0xFE58],
|
||||
"DeadDoubleacute": [0xFE59],
|
||||
"DeadCaron": [0xFE5A],
|
||||
"DeadCedilla": [0xFE5B],
|
||||
"DeadOgonek": [0xFE5C],
|
||||
"DeadIota": [0xFE5D],
|
||||
"DeadVoicedSound": [0xFE5E],
|
||||
"DeadSemivoicedSound": [0xFE5F],
|
||||
"Delete": [0xFFFF],
|
||||
"Down": [0xFF54],
|
||||
"End": [0xFF57],
|
||||
"Enter": [0xFF0D],
|
||||
"EraseEof": [0xFD06],
|
||||
"Escape": [0xFF1B],
|
||||
"Execute": [0xFF62],
|
||||
"Exsel": [0xFD1D],
|
||||
"ExSel": [0xFD1D],
|
||||
"F1": [0xFFBE],
|
||||
"F2": [0xFFBF],
|
||||
"F3": [0xFFC0],
|
||||
"F4": [0xFFC1],
|
||||
"F5": [0xFFC2],
|
||||
"F6": [0xFFC3],
|
||||
"F7": [0xFFC4],
|
||||
"F8": [0xFFC5],
|
||||
"F9": [0xFFC6],
|
||||
"F10": [0xFFC7],
|
||||
"F11": [0xFFC8],
|
||||
"F12": [0xFFC9],
|
||||
"F13": [0xFFCA],
|
||||
"F14": [0xFFCB],
|
||||
"F15": [0xFFCC],
|
||||
"F16": [0xFFCD],
|
||||
"F17": [0xFFCE],
|
||||
"F18": [0xFFCF],
|
||||
"F19": [0xFFD0],
|
||||
"F20": [0xFFD1],
|
||||
"F21": [0xFFD2],
|
||||
"F22": [0xFFD3],
|
||||
"F23": [0xFFD4],
|
||||
"F24": [0xFFD5],
|
||||
"Find": [0xFF68],
|
||||
"GroupFirst": [0xFE0C],
|
||||
"GroupLast": [0xFE0E],
|
||||
"GroupNext": [0xFE08],
|
||||
"GroupPrevious": [0xFE0A],
|
||||
"FullWidth": null,
|
||||
"HalfWidth": null,
|
||||
"HangulMode": [0xFF31],
|
||||
"Hankaku": [0xFF29],
|
||||
"HanjaMode": [0xFF34],
|
||||
"Help": [0xFF6A],
|
||||
"Hiragana": [0xFF25],
|
||||
"HiraganaKatakana": [0xFF27],
|
||||
"Home": [0xFF50],
|
||||
"Hyper": [0xFFED, 0xFFED, 0xFFEE],
|
||||
"Insert": [0xFF63],
|
||||
"JapaneseHiragana": [0xFF25],
|
||||
"JapaneseKatakana": [0xFF26],
|
||||
"JapaneseRomaji": [0xFF24],
|
||||
"JunjaMode": [0xFF38],
|
||||
"KanaMode": [0xFF2D],
|
||||
"KanjiMode": [0xFF21],
|
||||
"Katakana": [0xFF26],
|
||||
"Left": [0xFF51],
|
||||
"Meta": [0xFFE7, 0xFFE7, 0xFFE8],
|
||||
"ModeChange": [0xFF7E],
|
||||
"NumLock": [0xFF7F],
|
||||
"PageDown": [0xFF56],
|
||||
"PageUp": [0xFF55],
|
||||
"Pause": [0xFF13],
|
||||
"Play": [0xFD16],
|
||||
"PreviousCandidate": [0xFF3E],
|
||||
"PrintScreen": [0xFD1D],
|
||||
"Redo": [0xFF66],
|
||||
"Right": [0xFF53],
|
||||
"RomanCharacters": null,
|
||||
"Scroll": [0xFF14],
|
||||
"Select": [0xFF60],
|
||||
"Separator": [0xFFAC],
|
||||
"Shift": [0xFFE1, 0xFFE1, 0xFFE2],
|
||||
"SingleCandidate": [0xFF3C],
|
||||
"Super": [0xFFEB, 0xFFEB, 0xFFEC],
|
||||
"Tab": [0xFF09],
|
||||
"Up": [0xFF52],
|
||||
"Undo": [0xFF65],
|
||||
"Win": [0xFFEB],
|
||||
"Zenkaku": [0xFF28],
|
||||
"ZenkakuHankaku": [0xFF2A]
|
||||
};
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
export function makeperms(mask, config) {
|
||||
const perms = {
|
||||
restore: false,
|
||||
reboot: false,
|
||||
ban: false,
|
||||
forcevote: false,
|
||||
mute: false,
|
||||
kick: false,
|
||||
bypassturn: false,
|
||||
rename: false,
|
||||
grabip: false,
|
||||
xss: false
|
||||
};
|
||||
if ((mask & 1) !== 0) perms.restore = true;
|
||||
if ((mask & 2) !== 0) perms.reboot = true;
|
||||
if ((mask & 4) !== 0) perms.ban = true;
|
||||
if ((mask & 8) !== 0) perms.forcevote = true;
|
||||
if ((mask & 16) !== 0) perms.mute = true;
|
||||
if ((mask & 32) !== 0) perms.kick = true;
|
||||
if ((mask & 64) !== 0) perms.bypassturn = true;
|
||||
if ((mask & 128) !== 0) perms.rename = true;
|
||||
if ((mask & 256) !== 0) perms.grabip = true;
|
||||
if (config.xssImplementation === 2 && (mask & 512) !== 0) perms.xss = true;
|
||||
return perms;
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
export const guacutils = {
|
||||
decode: (string) => {
|
||||
let pos = -1;
|
||||
let sections = [];
|
||||
|
||||
for(;;) {
|
||||
let len = string.indexOf('.', pos + 1);
|
||||
|
||||
if(len === -1)
|
||||
break;
|
||||
|
||||
pos = parseInt(string.slice(pos + 1, len)) + len + 1;
|
||||
|
||||
// don't allow funky protocol length
|
||||
if(pos > string.length)
|
||||
return [];
|
||||
|
||||
sections.push(string.slice(len + 1, pos));
|
||||
|
||||
|
||||
const sep = string.slice(pos, pos + 1);
|
||||
|
||||
if(sep === ',')
|
||||
continue;
|
||||
else if(sep === ';')
|
||||
break;
|
||||
else
|
||||
// Invalid data.
|
||||
return [];
|
||||
}
|
||||
|
||||
return sections;
|
||||
},
|
||||
|
||||
encode: (string) => {
|
||||
let command = '';
|
||||
|
||||
for(var i = 0; i < string.length; i++) {
|
||||
let current = string[i];
|
||||
command += current.toString().length + '.' + current;
|
||||
command += ( i < string.length - 1 ? ',' : ';');
|
||||
}
|
||||
return command;
|
||||
}
|
||||
};
|
||||
0
src/ts/main.ts
Normal file
0
src/ts/main.ts
Normal file
3
src/ts/protocol/CollabVMClient.ts
Normal file
3
src/ts/protocol/CollabVMClient.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default class CollabVMClient {
|
||||
|
||||
}
|
||||
109
tsconfig.json
Normal file
109
tsconfig.json
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "ES2022", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index.js",
|
||||
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env'
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
mode: "production"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user