// 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(); }; // Autofocus on start textIn.focus(); // Key Listener document.addEventListener("keydown", e => { const text = textCur.textContent; switch (e.key) { case "Backspace": // Delete one character to the left if (cursorPosition === 0) return; // Cursor is in the middle of the input if (cursorPosition <= textCur.textContent.length-1) { const a = text.substring(0, cursorPosition-1); const b = text.substring(cursorPosition, text.length); textCur.textContent=a+b; // Cursor is at the end of input } else textCur.textContent = text.substring(0,text.length-1); // Update cursor visuals cursorPosition--; updateCursor(); break; case "ArrowLeft": // Go one cursor position to the left if (cursorPosition > 0) { cursorPosition--; updateCursor(); } break; case "ArrowRight": // Go one cursor position to the right if (cursorPosition < textCur.textContent.length) { cursorPosition++; updateCursor(); } break; case "Delete": // Delete one character to the left if (cursorPosition >= textCur.textContent.length) return; // No input given yet if (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": // Send out command / line break outputText({user: text}); // Refocus textIn.focus(); break; case "ArrowUp": // Go back one command in the history if (history.index > 0) { history.index--; textCur.textContent = history.list[history.index]; cursorPosition = textCur.textContent.length; } // Update cursor position updateCursor(); break; case "ArrowDown": // Go forward one command in the history if (history.index < history.list.length-1) { history.index++; textCur.textContent = history.list[history.index]; cursorPosition = textCur.textContent.length; // Reached end of history } else if (history.index < history.list.length) { history.index++; textCur.textContent = ""; cursorPosition = 0; } // Update cursor position updateCursor(); break; case "Tab": // Auto complete break; } }); // Text output or user input handler function outputText(params) { // Unpack parameters const { user, output, preNext } = params; // Temporarily remove core elements 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; } // Fixate current input to page tbDiv.appendChild(createText(user)); tbDiv.innerHTML += `
`; cursorYOffset++; } // Replace text on last pretext if (preNext !== undefined) { const tbc = tbDiv.children; tbc[tbc.length - 1].innerHTML = preNext; } // Run as command if it's user input and display 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 core elements back on top of div tbDiv.appendChild(textCur); tbDiv.appendChild(textIn); // Reset variables and position cursor 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);