diff options
| author | Morgana <Morgana@icolotl.com> | 2026-09-15 11:28:53 -0500 |
|---|---|---|
| committer | Morgana <Morgana@icolotl.com> | 2026-09-15 11:28:53 -0500 |
| commit | 2c76015a7a3f3a08ba205b04729f29df9fb7389f (patch) | |
| tree | e12b09312ab70253d32fb5576f5dd2d76ceda196 /static/script | |
Initial Commit
Diffstat (limited to 'static/script')
| -rw-r--r-- | static/script/main.js | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/static/script/main.js b/static/script/main.js new file mode 100644 index 0000000..171645b --- /dev/null +++ b/static/script/main.js @@ -0,0 +1,197 @@ +BANNER = "\ +/\\\\ I N D E X\n\ +\\\\/ Terminal\n\ +===============\n\n" + +/* +Index Virtual Terminal (IVT) Webclient JS +Copyright (C) 2026 Morgana <morgana@icolotl.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +function newElement(tag, opt, children) { + var elem = document.createElement(tag); + if (opt) { + if (opt.id) { elem.id = opt.id; } + if (opt.cls) { elem.className = opt.cls; } + if (opt.clss) { elem.className = opt.clss.join(" "); } + if (opt.txt) { elem.append(document.createTextNode(opt.txt)); } + if (opt.val) { elem.value = opt.val; } + if (opt.name) { elem.name = opt.name; } + if (opt.label) { elem.htmlFor = opt.label; } + if (opt.href) { elem.href = opt.href; } + if (opt.type) { elem.type = opt.type; } + if (opt.autocomplete) { elem.autocomplete = opt.autocomplete; } + if (opt.term) { + elem.title = opt.term; + elem.addEventListener("click", (event) => { + event.preventDefault(); + conn.process(opt.term); + return false; + }) + } + } + if (children) { + for (child of children) { + elem.append(opt.list ? newElement("li", {}, [child]) : child); + } + } + return elem; +} + +function newElementFull(opt) { + if (opt.raw) { + return document.createTextNode(opt.raw); + } + var tag = opt.tag ?? "span" + var children = opt.children ?? [] + return newElement(tag, opt, children) +} + +class IVTClient { + + constructor() { + this.stdout = document.getElementById("stdout"); + this.stdin = document.getElementById("stdin"); + this.stdinBox = document.getElementById("stdin-box"); + this.stdinBtn = document.getElementById("stdin-btn"); + this.stdinPrompt = document.getElementById("stdin-prompt"); + this.socket = null; + this.username = null; + this.pwdreturn = false; + this.oldprompt = null; + this.prelude = null; + this.printTextCLS(BANNER, "term-bold term-centered"); + this.stdinBox.addEventListener("submit", (event) => { + event.preventDefault(); + if (this.pwdreturn) { + this.socket.send(this.stdin.value); + this.stdin.type = "text"; + this.pwdreturn = false; + this.stdin.value = ""; + this.stdinPrompt.innerText = this.oldprompt; + return false; + } + this.process(this.stdin.value); + this.stdin.value = ""; + return false; + }); + this.socket = new WebSocket("ws://localhost:5417"); + this.socket.addEventListener("open", (event) => { + this.printText("Attempting to connect to INDEX server..."); + this.stdinPrompt.innerText = "CONNECTING>"; + }); + this.socket.addEventListener("error", (event) => { + this.printText("*** A network error has occurred. Please try again later. ***"); + }) + this.socket.addEventListener("close", (event) => { + this.printText("Disconnected from INDEX server."); + this.stdinPrompt.innerText = "DISCONNECTED>"; + this.stdinBox.dataset.offline = ""; + this.stdin.disabled = true; + this.stdinBtn.disabled = true; + }) + this.socket.addEventListener("message", (event) => { + if (this.prelude) { + var fxn = this["on_" + this.prelude + "_prelude"]; + if (fxn) { + fxn.call(this, event.data) + } + this.prelude = null; + return + } + var data = JSON.parse(event.data); + var fxn = this["on_" + data.act + "_msg"]; + if (fxn) { + fxn.call(this, data) + } + }); + } + + on_text_msg(data) { + this.printText(data.text); + } + + on_display_msg(data) { + this.printTextFULL(data.msg); + } + + on_activate_msg(data) { + this.username = data.username; + this.stdin.disabled = false; + this.stdinBtn.disabled = false; + this.stdinPrompt.innerText = `${data.username}@INDEX>`; + delete this.stdinBox.dataset.offline; + this.stdin.focus(); + } + + on_userchange_msg(data) { + this.username = data.username; + this.stdinPrompt.innerText = `${data.username}@INDEX>`; + this.stdin.focus(); + } + + on_password_msg(data) { + this.stdin.type = "password"; + this.pwdreturn = true; + this.oldprompt = this.stdinPrompt.innerText; + this.stdinPrompt.innerText = data.prompt; + this.printText(data.text); + } + + on_action_msg(data) { + if (data.action == "clear") { + this.stdout.innerHTML = ""; + } + } + + on_prelude_msg(data) { + this.prelude = data.action; + } + + async on_music_prelude(data) { + var elem = document.createElement("audio"); + elem.src = window.URL.createObjectURL(data); + elem.controls = true; + elem.autoplay = true; + this.stdout.append(elem); + } + + process(text) { + if (this.pwdreturn) { + return; + } + this.printTextCLS(`${this.stdinPrompt.innerText} ${text}`, "history"); + this.socket.send(text); + } + + printText(text) { + this.stdout.append(newElement("div", { txt: text })); + this.stdout.scrollTop = this.stdout.scrollHeight; + } + + printTextCLS(text, cls) { + this.stdout.append(newElement("div", { txt: text, cls: cls })); + this.stdout.scrollTop = this.stdout.scrollHeight; + } + + printTextFULL(txtobj) { + this.stdout.append(newElement("div", {}, txtobj.map(newElementFull))); + this.stdout.scrollTop = this.stdout.scrollHeight; + } + +} + +let conn = new IVTClient();
\ No newline at end of file |
