// Initiating variables const length = pretext.current.length+1; let cursorPosition = 0; let cursorYOffset = 7; let history = { index: 0, list: [] }; // Initiating constants const tbDiv = document.getElementById("textbox"); const textIn = document.getElementById("input"); const textCur = document.getElementById("current"); const cursor = document.getElementById("cursor"); // Set initial pretext const pretextElement = document.createElement('a'); pretextElement.innerHTML = pretext.original; tbDiv.insertBefore(pretextElement, tbDiv.children[1]); // Initial cursor offset cursor.style.transform = `translate(${length-1}ch,${2.222*7-0.4}ch)`; // Handle user input textIn.oninput = () => { const text = textCur.textContent; // If cursor at end of text if (cursorPosition === text.length) textCur.textContent += textIn.value; else { // Insert input at cursor position const left = text.substring(0, cursorPosition); const right = text.substring(cursorPosition, text.length); textCur.textContent = left + textIn.value + right; } // Increment by input length cursorPosition += textIn.value.length; textIn.value = ""; updateCursor(); }; textIn.focus(); // If no ranged selection was made, focus on input field document.addEventListener("selectionchange", e => { if (e.target !== textIn) { const selection = window.getSelection(); if (selection.type === "Caret") textIn.focus(); } }); document.addEventListener("keydown", e => { const text = textCur.textContent; switch (e.key) { case "Backspace": // Ein Zeichen nach links löschen if (cursorPosition === 0) return; if (cursorPosition <= textCur.textContent.length-1) { const a = text.substring(0, cursorPosition-1); const b = text.substring(cursorPosition, text.length); textCur.textContent=a+b; } else textCur.textContent = text.substring(0,text.length-1); cursorPosition--; updateCursor(); break; case "ArrowLeft": // Ein Zeichen nach links if (cursorPosition > 0) { cursorPosition--; updateCursor(); } break; case "ArrowRight": // Ein Zeichen nach reckts if (cursorPosition < textCur.textContent.length) { cursorPosition++; updateCursor(); } break; case "Delete": // Ein Zeichen nach rechts löschen if (cursorPosition >= textCur.textContent.length) return; if (cursorPosition === 0 && textCur.textContent.length === 0) return; const a = text.substring(0, cursorPosition); const b = text.substring(cursorPosition+1, text.length); textCur.textContent=a+b; break; case "Enter": // Befehl absenden / Zeilenumbruch outputText({user: text}); break; case "ArrowUp": // Einen Befehl zurück in der History if (history.index > 0) { history.index--; textCur.textContent = history.list[history.index]; cursorPosition = textCur.textContent.length; } updateCursor(); break; case "ArrowDown": // Einen Befehl nach vorne in der History if (history.index < history.list.length-1) { history.index++; textCur.textContent = history.list[history.index]; cursorPosition = textCur.textContent.length; } else if (history.index < history.list.length) { history.index++; textCur.textContent = ""; cursorPosition = 0; } updateCursor(); break; case "Tab": // Autocomplete break; } }); function outputText(params) { const { user, output, preNext } = params; tbDiv.removeChild(textIn); tbDiv.removeChild(textCur); let inputSet, whitespaceOnly = false; if (user !== undefined) { // Save input into history if it's not empty inputSet = new Set(user.split('')); whitespaceOnly = inputSet.has(" ") && inputSet.size === 1; if (user !== "" && !whitespaceOnly) { history.list.push(user); history.index = history.list.length; } // Fix current input to page tbDiv.appendChild(createText(user)); tbDiv.innerHTML += `
`; cursorYOffset++; } if (preNext !== undefined) { const tbc = tbDiv.children; tbc[tbc.length - 1].innerHTML = preNext; } // Run command and return output const isUserInput = output === undefined && user !== undefined; let commandOutput = (isUserInput && !whitespaceOnly) ? runCommand(user) : (output ? output : ""); if (commandOutput !== "" && commandOutput !== null) { // If output is given, display it as text with formatting tbDiv.append(createText(commandOutput, false)) tbDiv.innerHTML += `
`; // Offset cursor by at least one line, and as many more as there are line breaks cursorYOffset += commandOutput.split("
").length; } // Add new pretext to terminal if (commandOutput !== null) { const prelink = document.createElement('a'); prelink.innerHTML = pretext.current; tbDiv.appendChild(prelink); } // Add elements on top of div tbDiv.appendChild(textCur); tbDiv.appendChild(textIn); // Reset variables if (isUserInput) { textCur.textContent = ""; cursorPosition = 0; } updateCursor(); } // Create an 'a' element with input string as its contents function createText(str, safe=true) { const link = document.createElement("a"); link.style.color = "lightgrey"; link.style.whiteSpace = "pre"; // No "HTML injection" if (safe) link.textContent = str; // "Anything goes" and keep formatting else link.innerHTML = str; return link; } // Rename last-posted pretext to own username function renameToSelf() { const tbc = tbDiv.children; tbc[tbc.length - 3].innerHTML = `${window.localStorage.getItem("name")}#${window.localStorage.getItem("id")}: `; } // Update cursor position based on coordinates function updateCursor() { let cursor = document.getElementById("cursor"); cursor.style.transform = `translate(${pretext.current.length+cursorPosition}ch, ${2.222*cursorYOffset-0.4}ch)`; } // Toggle cursor visibility setInterval(()=>{ cursor.style.opacity = !parseInt(cursor.style.opacity)+0; },1000);