From dc8bb273cd2aa1aafbc547b3e7b652eda9b81fcf Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Sat, 2 Apr 2022 14:01:37 +0200 Subject: [PATCH] fix html terminal to support linebreaks inside lines --- .../javascript/WebTerminal/HtmlTerminal.js | 43 ++--- .../javascript/WebTerminal/terminal_tests.mjs | 14 +- 00_Common/javascript/common.mjs | 70 +++++++-- 00_Utilities/build-index.js | 3 - 96_Word/javascript/word.js | 148 ------------------ 5 files changed, 89 insertions(+), 189 deletions(-) delete mode 100644 96_Word/javascript/word.js diff --git a/00_Common/javascript/WebTerminal/HtmlTerminal.js b/00_Common/javascript/WebTerminal/HtmlTerminal.js index 4a095c87..c8d529d9 100644 --- a/00_Common/javascript/WebTerminal/HtmlTerminal.js +++ b/00_Common/javascript/WebTerminal/HtmlTerminal.js @@ -96,9 +96,18 @@ class HtmlTerminal { this.#inputCallback = undefined; } else if (e.keyCode === 8 /* BACKSPACE */) { this.#$prompt.innerText = this.#$prompt.innerText.slice(0, -1); + } else if ( + e.keyCode == 16 // "Shift" + || e.keyCode == 17 // "Control" + || e.keyCode == 20 // "CapsLock" + || !e.key.match(/^[a-z0-9!"ยง#$%&'()*+,.\/:;<=>?@\[\] ^_`{|}~-]$/i) + ) { + // ignore non-visible characters + return e; } else { this.#$prompt.innerHtml = ''; - this.#$prompt.innerText = this.#$prompt.innerText + e.key; + const key = e.shiftKey ? e.key.toUpperCase() : e.key; + this.#$prompt.innerText = this.#$prompt.innerText + key; } } @@ -135,27 +144,23 @@ class HtmlTerminal { * @param {string} text */ write(text) { - if (text.match(/^\n*$/)) { - // empty new line - text.match(/\n/g).forEach(() => { - const $br = document.createElement("br"); - this.$output.appendChild($br); - }); - } else if (text && text.length && text.includes("\n")) { + if (!text || text.length <= 0) { + // empty line + this.$output.appendChild(document.createElement("br")); + } else if (text.endsWith("\n")) { + // single line with linebrank + const $lineNode = this.#newLine(text); + this.$output.appendChild(this.#newLine(text)); + this.$output.appendChild(document.createElement("br")); + } else if (text.includes("\n")) { + // multible lines const lines = text.split("\n"); lines.forEach((line) => { - if (line.length === 0 || line.match(/^\s*$/)) { - this.$output.appendChild(document.createElement("br")); - } else { - const $lineNode = this.#newLine(line); - this.$output.appendChild($lineNode); - //this.$node.appendChild(document.createElement("br")); - } + this.write(line); }); - } else if (text && text.length) { - // simple line - const $lineNode = this.#newLine(text); - this.$output.appendChild($lineNode); + } else { + // single line + this.$output.appendChild(this.#newLine(text)); } // scroll to the buttom of the page diff --git a/00_Common/javascript/WebTerminal/terminal_tests.mjs b/00_Common/javascript/WebTerminal/terminal_tests.mjs index 804de3f2..d2470c95 100644 --- a/00_Common/javascript/WebTerminal/terminal_tests.mjs +++ b/00_Common/javascript/WebTerminal/terminal_tests.mjs @@ -3,21 +3,21 @@ import { print, println, tab, input } from '../common.mjs'; async function main() { - println(tab(20), "Minimal node.js terminal 2"); - println(""); + println(tab(30), "Minimal node.js terminal emulator"); + println(); println(tab(0), "tab 0"); println(tab(5), "tab 5"); println(tab(10), "tab 10"); println(tab(15), "tab 15"); println(tab(20), "tab 20"); println(tab(25), "tab 25"); - println(""); - println("1234567890", " ", "ABCDEFGHIJKLMNOPRSTUVWXYZ"); - println(""); + println(); + println("1234567890", " _ ", "ABCDEFGHIJKLMNOPRSTUVWXYZ"); + println(); print("\nHallo"); print(" "); print("Welt!\n"); println(""); - print("Line 1\nLine 2\nLine 3\nLine 4"); - println(""); + println("Line 1\nLine 2\nLine 3\nLine 4"); + println("----------------------------------------------"); const value = await input("input"); println(`input value was "${value}"`); diff --git a/00_Common/javascript/common.mjs b/00_Common/javascript/common.mjs index 3fcc4212..765f5cc2 100644 --- a/00_Common/javascript/common.mjs +++ b/00_Common/javascript/common.mjs @@ -1,21 +1,67 @@ - +/** + * Print multible strings to the terminal. + * Strings get concatinated (add together) without any space betweent them. + * There will be no newline at the end! + * If you want a linebrak at the end use `println`. + * + * This function is normally used if you want to put something on the screen + * and later add some content to the same line. + * For normal output (similar to `console.log`) use `println`! + * + * @param {...string} messages - the strings to print to the terminal. + */ export function print(...messages) { - process.stdout.write(messages.join("")); + process.stdout.write(messages.join("")); } +/** + * Add multible strings as a new line to the terminal. + * Strings get concatinated (add together) without any space betweent them. + * There will be a newline at the end! + * If you want the terminal to stay active on the current line use `print`. + * + * @param {...any} messages - the strings to print to the terminal. + */ export function println(...messages) { - process.stdout.write(messages.join("") + "\n"); + process.stdout.write(messages.join("") + "\n"); } -export function tab(count) { - return " ".repeat(count); +/** + * Create an empty string with a given length + * + * @param {number} length - the length of the string in space-characters. + * @returns {string} returns a string containing only ampty spaces with a length of `count`. + */ +export function tab(length) { + return " ".repeat(length); } -export async function input(message = "") { - process.stdout.write(message + ' '); - return new Promise(resolve => { - process.stdin.on('data', (input) => { - resolve(input.toString().replace('\n', '')); - }); - }); +/** + * Read input from the keyboard and return it as a string. + * TODO: to would be very helpfull to only allow a certain class of input (numbers, letters) + * TODO: also we could convert all inputs to uppercase (where it makes sence). + * + * @param {string=''} message - a message or question to print befor the input. + * @returns {Promise} - returns the entered text as a string + * @async + */ +export async function input(message = '') { + /* First we need to print the mesage + * We append a space by default to seperate the message from the imput. + * TODO: If the message already contains a space at the end this is not needed! */ + process.stdout.write(message + ' '); + + return new Promise(resolve => { + process.stdin.on('data', (input) => { + /* onData returns a Buffer. + * First we need to convert it into a string. */ + const data = input.toString(); + + /* The result fo onData is a string ending with an `\n`. + * We just need the actual content so let's remove the newline at the end: */ + const content = data[data.length] === '\n' ? data.slice(0, -1) : data; + + resolve(content); + }); + }); } diff --git a/00_Utilities/build-index.js b/00_Utilities/build-index.js index 20af363e..1ed862ef 100644 --- a/00_Utilities/build-index.js +++ b/00_Utilities/build-index.js @@ -103,10 +103,7 @@ function findJSFilesInFolder(folder) { ...htmlFiles, ...mjsFiles ].filter(file => !IGNORE_FILES.includes(file)); - console.log(entries); - - if (entries.length == 0) { throw new Error(`Game "${folder}" is missing a HTML or node.js file in the folder "${folder}/${JAVASCRIPT_FOLDER}"`); } diff --git a/96_Word/javascript/word.js b/96_Word/javascript/word.js deleted file mode 100644 index 3d7e3a5a..00000000 --- a/96_Word/javascript/word.js +++ /dev/null @@ -1,148 +0,0 @@ -// WORD -// -// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) -// - -function print(str) -{ - document.getElementById("output").appendChild(document.createTextNode(str)); -} - -function input() -{ - - return new Promise(function (resolve) { - const input_element = document.createElement("INPUT"); - - print("? "); - input_element.setAttribute("type", "text"); - input_element.setAttribute("length", "50"); - document.getElementById("output").appendChild(input_element); - input_element.focus(); - input_element.addEventListener("keydown", function (event) { - if (event.keyCode === 13) { - const input_str = input_element.value; - document.getElementById("output").removeChild(input_element); - print(input_str); - print("\n"); - resolve(input_str); - } - }); - }); -} - -function tab(space) -{ - let str = ""; - while (space-- > 0) - str += " "; - return str; -} - -// These are the words that the game knows about> If you want a bigger challenge you could add more words to the array -const WORDS = ["DINKY", "SMOKE", "WATER", "GLASS", "TRAIN", - "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", - "CLUMP", "DOPEY"]; -const WORD_COUNT = WORDS.length; - -// Main control section -async function main() -{ - print(tab(33) + "WORD\n"); - print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); - print("\n"); - print("\n"); - print("\n"); - print("I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU\n"); - print("CLUES TO HELP YOU GET IT. GOOD LUCK!!\n"); - print("\n"); - print("\n"); - outer: while (1) { - print("\n"); - print("\n"); - print("YOU ARE STARTING A NEW GAME...\n"); - - const secretWord = WORDS[Math.floor(Math.random() * WORD_COUNT)]; - - let guessCount = 0; - // This array holds the letters which have been found in the correct position across all guesses - // For instance if the word is "PLAIN" and the guesses so far are - // "SHALL" ("A" correct) and "CLIMB" ("L" correct) then it will hold "-LA--" - const knownLetters = []; - for (let i = 0; i < 5; i++) - knownLetters[i] = "-"; - - let guess = undefined; - while (1) { - print("GUESS A FIVE LETTER WORD"); - guess = (await input()).toUpperCase(); - guessCount++; - if (secretWord === guess) { - // The player has guessed correctly - break; - } - - if (guess.charAt(0) === "?") { - // Player has given up - print("THE SECRET WORD IS " + secretWord + "\n"); - print("\n"); - // Start a new game by going to the start of the outer while loop - continue outer; - } - - if (guess.length !== 5) { - print("YOU MUST GUESS A 5 LETTER WORD. START AGAIN.\n"); - print("\n"); - guessCount--; - continue; - } - - // Two things happen in this double loop: - // 1. Letters which are in both the guessed and secret words are put in the lettersInCommon array - // 2. Letters which are in the correct position in the guessed word are added to the knownLetters array - let lettersInCommonCount = 0; - const lettersInCommon = []; - for (let i = 0; i < 5; i++) {// loop round characters in secret word - let secretWordCharacter = secretWord.charAt(i); - for (let j = 0; j < 5; j++) {// loop round characters in guessed word - let guessedWordCharacter = guess.charAt(j); - if (secretWordCharacter === guessedWordCharacter) { - lettersInCommon[lettersInCommonCount] = guessedWordCharacter; - if (i === j) { - // Letter is in the exact position so add to the known letters array - knownLetters[j] = guessedWordCharacter; - } - lettersInCommonCount++; - } - } - } - - const lettersInCommonText = lettersInCommon.join(""); - print("THERE WERE " + lettersInCommonCount + " MATCHES AND THE COMMON LETTERS WERE... " + lettersInCommonText + "\n"); - - const knownLettersText = knownLetters.join(""); - print("FROM THE EXACT LETTER MATCHES, YOU KNOW............ " + knownLettersText + "\n"); - - if (knownLettersText === secretWord) { - guess = knownLettersText; - break; - } - - if (lettersInCommonCount <= 1) { - print("\n"); - print("IF YOU GIVE UP, TYPE '?' FOR YOUR NEXT GUESS.\n"); - print("\n"); - } - } - - print("YOU HAVE GUESSED THE WORD. IT TOOK " + guessCount + " GUESSES!\n"); - print("\n"); - - print("WANT TO PLAY AGAIN"); - const playAgainResponse = (await input()).toUpperCase(); - if (playAgainResponse !== "YES") - break; - } -} - -main();