2023-01-17 18:15:49 +00:00
|
|
|
// Handle server connection
|
|
|
|
document.body.onunload = () => {
|
|
|
|
if (user.connected)
|
|
|
|
fetch('/disconnect', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
id: user.id
|
|
|
|
})
|
|
|
|
}).then(res => {});
|
|
|
|
};
|
|
|
|
fetch('/connect').then(res => {
|
|
|
|
if (res.status === 200) {
|
|
|
|
user.connected = true;
|
|
|
|
return res.json();
|
|
|
|
}
|
|
|
|
console.error("Could not connect to server!");
|
|
|
|
}).then(res => {
|
|
|
|
if (res === undefined)
|
|
|
|
return;
|
|
|
|
const { id } = res;
|
|
|
|
user.id = id;
|
|
|
|
});
|
|
|
|
const user = { id: "", name: "", history: [], connected: false };
|
2023-01-18 07:02:41 +00:00
|
|
|
let chatMode = "default";
|
2023-01-17 18:15:49 +00:00
|
|
|
|
2023-01-16 14:21:30 +00:00
|
|
|
let pretext = "root@baipyr.us:~# ";
|
2023-01-13 11:37:25 +00:00
|
|
|
|
|
|
|
// Initiating variables
|
|
|
|
const length = pretext.length+1;
|
2022-05-27 16:13:33 +00:00
|
|
|
let cursorPosition = 0;
|
2023-01-12 14:10:13 +00:00
|
|
|
let cursorYOffset = 7;
|
2023-01-17 18:15:49 +00:00
|
|
|
let history = { index: 0, list: [] };
|
2022-05-27 16:13:33 +00:00
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Initiating constants
|
2023-01-05 23:13:56 +00:00
|
|
|
const tbDiv = document.getElementById("textbox");
|
|
|
|
const textIn = document.getElementById("input");
|
2023-01-12 06:25:46 +00:00
|
|
|
const textCur = document.getElementById("current");
|
|
|
|
const cursor = document.getElementById("cursor");
|
2023-01-13 11:37:25 +00:00
|
|
|
// Initial cursor offset
|
2023-01-12 14:10:13 +00:00
|
|
|
cursor.style.transform = `translate(${length-1}ch,${2.222*7-0.4}ch)`;
|
2023-01-05 23:13:56 +00:00
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Handle user input
|
2023-01-12 06:25:46 +00:00
|
|
|
textIn.oninput = () => {
|
2023-01-13 06:27:13 +00:00
|
|
|
const text = textCur.textContent;
|
2023-01-13 11:37:25 +00:00
|
|
|
// If cursor at end of text
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition === text.length)
|
|
|
|
textCur.textContent += textIn.value;
|
|
|
|
else {
|
2023-01-13 11:37:25 +00:00
|
|
|
// Insert input at cursor position
|
2023-01-13 06:27:13 +00:00
|
|
|
const left = text.substring(0, cursorPosition);
|
|
|
|
const right = text.substring(cursorPosition, text.length);
|
|
|
|
textCur.textContent = left + textIn.value + right;
|
|
|
|
}
|
2023-01-13 11:37:25 +00:00
|
|
|
// Increment by input length
|
2023-01-13 06:27:13 +00:00
|
|
|
cursorPosition += textIn.value.length;
|
2023-01-12 06:25:46 +00:00
|
|
|
textIn.value = "";
|
|
|
|
updateCursor();
|
2023-01-05 23:13:56 +00:00
|
|
|
};
|
2023-01-12 06:25:46 +00:00
|
|
|
textIn.focus();
|
2023-01-05 23:13:56 +00:00
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// If no ranged selection was made, focus on input field
|
2023-01-05 23:13:56 +00:00
|
|
|
document.addEventListener("selectionchange", e => {
|
2023-01-12 06:25:46 +00:00
|
|
|
if (e.target !== textIn) {
|
2023-01-05 23:13:56 +00:00
|
|
|
const selection = window.getSelection();
|
|
|
|
if (selection.type === "Caret")
|
2023-01-12 06:25:46 +00:00
|
|
|
textIn.focus();
|
2023-01-05 23:13:56 +00:00
|
|
|
}
|
2022-05-27 16:13:33 +00:00
|
|
|
});
|
|
|
|
|
2023-01-18 07:02:41 +00:00
|
|
|
document.addEventListener("keydown", e => {
|
2023-01-12 06:25:46 +00:00
|
|
|
const text = textCur.textContent;
|
2022-05-27 16:13:33 +00:00
|
|
|
switch (e.key) {
|
|
|
|
case "Backspace":
|
|
|
|
// Ein Zeichen nach links löschen
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition === 0)
|
2022-05-27 16:13:33 +00:00
|
|
|
return;
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition <= textCur.textContent.length-1) {
|
|
|
|
const a = text.substring(0, cursorPosition-1);
|
|
|
|
const b = text.substring(cursorPosition, text.length);
|
2023-01-12 06:25:46 +00:00
|
|
|
textCur.textContent=a+b;
|
2022-05-27 16:13:33 +00:00
|
|
|
} else
|
2023-01-12 06:25:46 +00:00
|
|
|
textCur.textContent = text.substring(0,text.length-1);
|
2022-05-27 16:13:33 +00:00
|
|
|
cursorPosition--;
|
2023-01-12 06:25:46 +00:00
|
|
|
updateCursor();
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "ArrowLeft":
|
|
|
|
// Ein Zeichen nach links
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition > 0) {
|
2022-05-27 16:13:33 +00:00
|
|
|
cursorPosition--;
|
2023-01-12 06:25:46 +00:00
|
|
|
updateCursor();
|
|
|
|
}
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "ArrowRight":
|
|
|
|
// Ein Zeichen nach reckts
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition < textCur.textContent.length) {
|
2022-05-27 16:13:33 +00:00
|
|
|
cursorPosition++;
|
2023-01-12 06:25:46 +00:00
|
|
|
updateCursor();
|
|
|
|
}
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "Delete":
|
|
|
|
// Ein Zeichen nach rechts löschen
|
2023-01-13 06:27:13 +00:00
|
|
|
if (cursorPosition >= textCur.textContent.length)
|
|
|
|
return;
|
|
|
|
if (cursorPosition === 0 && textCur.textContent.length === 0)
|
2022-05-27 16:13:33 +00:00
|
|
|
return;
|
2023-01-13 06:27:13 +00:00
|
|
|
const a = text.substring(0, cursorPosition);
|
|
|
|
const b = text.substring(cursorPosition+1, text.length);
|
2023-01-12 06:25:46 +00:00
|
|
|
textCur.textContent=a+b;
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "Enter":
|
|
|
|
// Befehl absenden / Zeilenumbruch
|
2023-01-18 07:02:41 +00:00
|
|
|
outputText({user: text});
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "ArrowUp":
|
|
|
|
// Einen Befehl zurück in der History
|
2023-01-13 11:57:46 +00:00
|
|
|
if (history.index > 0) {
|
|
|
|
history.index--;
|
|
|
|
textCur.textContent = history.list[history.index];
|
|
|
|
cursorPosition = textCur.textContent.length;
|
|
|
|
}
|
|
|
|
updateCursor();
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "ArrowDown":
|
|
|
|
// Einen Befehl nach vorne in der History
|
2023-01-13 11:57:46 +00:00
|
|
|
if (history.index < history.list.length-1) {
|
|
|
|
history.index++;
|
|
|
|
textCur.textContent = history.list[history.index];
|
|
|
|
cursorPosition = textCur.textContent.length;
|
2023-01-16 10:08:21 +00:00
|
|
|
} else if (history.index < history.list.length) {
|
2023-01-13 11:57:46 +00:00
|
|
|
history.index++;
|
|
|
|
textCur.textContent = "";
|
|
|
|
cursorPosition = 0;
|
|
|
|
}
|
|
|
|
updateCursor();
|
2022-05-27 16:13:33 +00:00
|
|
|
break;
|
|
|
|
case "Tab":
|
|
|
|
// Autocomplete
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-01-18 07:02:41 +00:00
|
|
|
function outputText(params) {
|
|
|
|
const { user, output } = params;
|
|
|
|
|
|
|
|
tbDiv.removeChild(textIn);
|
|
|
|
tbDiv.removeChild(textCur);
|
|
|
|
|
|
|
|
if (user !== undefined) {
|
|
|
|
const inputSet = new Set(user.split(''));
|
|
|
|
if (user !== "" || inputSet.has(" ") && inputSet.size === 1) {
|
|
|
|
history.list.push(user);
|
|
|
|
history.index = history.list.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix current input to page
|
|
|
|
tbDiv.appendChild(createText(user));
|
|
|
|
tbDiv.innerHTML += `<br>`;
|
|
|
|
cursorYOffset++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run command and return output
|
|
|
|
let commandOutput = (output === undefined && user !== undefined) ? runCommand(user) : output;
|
|
|
|
if (commandOutput !== "" && commandOutput !== null) {
|
|
|
|
// If output is given, display it as text with formatting
|
|
|
|
tbDiv.append(createText(commandOutput, false))
|
|
|
|
tbDiv.innerHTML += `<br>`;
|
|
|
|
// Offset cursor by at least one line, and as many more as there are line breaks
|
|
|
|
cursorYOffset += commandOutput.split("<br>").length;
|
|
|
|
}
|
|
|
|
// Add new pretext to terminal
|
|
|
|
if (commandOutput !== null)
|
|
|
|
tbDiv.innerHTML += pretext;
|
|
|
|
// Add elements on top of div
|
|
|
|
tbDiv.appendChild(textCur);
|
|
|
|
tbDiv.appendChild(textIn);
|
|
|
|
// Reset variables
|
|
|
|
textCur.textContent = "";
|
|
|
|
cursorPosition = 0;
|
|
|
|
updateCursor();
|
|
|
|
}
|
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Create an 'a' element with input string as its contents
|
|
|
|
function createText(str, safe=true) {
|
2023-01-12 13:40:53 +00:00
|
|
|
const link = document.createElement("a");
|
|
|
|
link.style.color = "lightgrey";
|
2023-01-13 12:05:59 +00:00
|
|
|
link.style.whiteSpace = "pre";
|
2023-01-13 11:37:25 +00:00
|
|
|
// No "HTML injection"
|
|
|
|
if (safe)
|
|
|
|
link.textContent = str;
|
|
|
|
// "Anything goes" and keep formatting
|
2023-01-13 12:05:59 +00:00
|
|
|
else
|
2023-01-13 11:37:25 +00:00
|
|
|
link.innerHTML = str;
|
2023-01-12 13:40:53 +00:00
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Run input as command if possible
|
2023-01-18 07:02:41 +00:00
|
|
|
function runCommand(input) {
|
2023-01-16 06:15:33 +00:00
|
|
|
// Return on no input
|
|
|
|
if (input === "")
|
|
|
|
return "";
|
|
|
|
|
2023-01-18 07:02:41 +00:00
|
|
|
const modeSplit = chatMode.split(' ');
|
|
|
|
switch (modeSplit[0]) {
|
|
|
|
case "msg":
|
|
|
|
// Direct message
|
|
|
|
fetch('/message', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
id: user.id,
|
|
|
|
message: input,
|
|
|
|
name: modeSplit[1]
|
|
|
|
})
|
|
|
|
}).then(res => {});
|
|
|
|
return "";
|
|
|
|
// case "chat":
|
|
|
|
// // Global messages
|
|
|
|
// break;
|
|
|
|
}
|
|
|
|
|
2023-01-12 13:37:33 +00:00
|
|
|
let output = "";
|
2023-01-16 06:15:33 +00:00
|
|
|
const lowerIn = input.toLowerCase();
|
2023-01-12 13:37:33 +00:00
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Go through properties of window
|
2023-01-12 13:37:33 +00:00
|
|
|
for (const func in window) {
|
|
|
|
const splits = func.split("cmd_");
|
2023-01-13 11:37:25 +00:00
|
|
|
// If property is prefixed with 'cmd_' (a 'command', so an executable function)
|
2023-01-12 13:37:33 +00:00
|
|
|
if (splits.length === 2) {
|
|
|
|
const name = splits[1];
|
2023-01-16 06:15:33 +00:00
|
|
|
const lowerNm = name.toLowerCase();
|
2023-01-13 11:37:25 +00:00
|
|
|
// If command is called without parameters
|
2023-01-16 06:15:33 +00:00
|
|
|
if (lowerIn === lowerNm) {
|
2023-01-18 07:02:41 +00:00
|
|
|
output = window[func]();
|
2023-01-12 13:37:33 +00:00
|
|
|
break;
|
2023-01-13 11:37:25 +00:00
|
|
|
// If command is called with parameters
|
2023-01-16 06:15:33 +00:00
|
|
|
} else if (lowerIn.startsWith(lowerNm + " ")) {
|
2023-01-13 11:37:25 +00:00
|
|
|
// Parameters always follow the command name after first space
|
|
|
|
const params = input.split(" ").filter((e,i)=>i!==0);
|
2023-01-18 07:02:41 +00:00
|
|
|
output = window[func](params);
|
2023-01-12 13:37:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-16 06:15:33 +00:00
|
|
|
// Standard output:
|
2023-01-18 07:02:41 +00:00
|
|
|
if (output === "")
|
2023-01-16 06:15:33 +00:00
|
|
|
output = `${input.split(" ")[0]}: command not found`;
|
2023-01-18 07:02:41 +00:00
|
|
|
if (output === undefined)
|
|
|
|
output = "";
|
2023-01-16 06:15:33 +00:00
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Return command output
|
2023-01-12 13:37:33 +00:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Update cursor position based on coordinates
|
2023-01-12 06:25:46 +00:00
|
|
|
function updateCursor() {
|
|
|
|
let cursor = document.getElementById("cursor");
|
2023-01-18 07:02:41 +00:00
|
|
|
cursor.style.transform = `translate(${pretext.length+cursorPosition}ch, ${2.222*cursorYOffset-0.4}ch)`;
|
2023-01-12 06:25:46 +00:00
|
|
|
}
|
|
|
|
|
2023-01-13 11:37:25 +00:00
|
|
|
// Toggle cursor visibility
|
2022-05-27 16:13:33 +00:00
|
|
|
setInterval(()=>{
|
2023-01-05 23:13:56 +00:00
|
|
|
cursor.style.opacity = !parseInt(cursor.style.opacity)+0;
|
2022-05-27 16:13:33 +00:00
|
|
|
},1000);
|