/** * This file is licensed under the GPLv3 license (c) Aleksi 'Allexit' Talarmo * See COPYING-GPL and README.md for more information. */ var JavaScriptX; (function (JavaScriptX) { class JSX { constructor() { this.environmentVariables = { "system": "~/.system/" }; } create(terminal, stdio, args) { this.terminal = terminal; this.stdio = stdio; this.filesystem = new FileSystem(); this.currentFolder = this.filesystem.rootFolder; let system = new Folder(".system", true); this.filesystem.rootFolder.addFile(system); system.addFile(new BinaryFile("echo", new JSXCommands.Echo())); system.addFile(new BinaryFile("ls", new JSXCommands.List(this))); system.addFile(new BinaryFile("cd", new JSXCommands.ChangeDirectory(this))); system.addFile(new BinaryFile("mkdir", new JSXCommands.MakeDirectory(this))); system.addFile(new BinaryFile("woman", new JSXCommands.Woman(this))); system.addFile(new BinaryFile("cat", new JSXCommands.Caternate(this))); system.addFile(new BinaryFile("touch", new JSXCommands.Touch(this))); system.addFile(new BinaryFile("jedi", new JSXCommands.JEdi(this))); this.filesystem.rootFolder.addFile(new TextFile("textfile.txt", "Hello, world!")); stdio.println("Welcome to "); stdio.print("JSX", "red"); stdio.print(" version "); stdio.print("0.4.0 beta", "green"); stdio.print(" (K) All rites reversed"); stdio.println("\nTo get started: call a "); stdio.print("woman", "blue"); stdio.print(" for help."); stdio.refresh(); this.takeCommand(); } /** * Inherited from Program-interface. Not to be manually called. */ enable() { this.takeCommand(); } /** * Inherited from Program-interface. Not to be manually called. */ disable() { } /** * Inherited from Program-interface. Not to be manually called. */ onClose() { return true; } /** * Utility function. Writes a line such as 'Anonymous@localhost: ~/' and then reads line. * The result of the readline is used to call commands and such. */ takeCommand() { let name = window.location.hostname || "localhost"; this.stdio.println(`\nAnonymous@${name}`, "yellow"); this.stdio.print(":"); let path = this.currentFolder.getPath(); this.stdio.print(path, "green"); this.stdio.readline({ callback: (result) => { let split = result.split(" "); let command = split[0]; let args = split.slice(1, split.length); for (let v in this.environmentVariables) { if (command == v) { command = this.environmentVariables[v]; break; } let file = this.findPath(this.environmentVariables[v]); if (file instanceof Folder) { for (let i = 0; i < file.files.length; i++) { let f = file.files[i]; if (command == f.name) { command = f.getPath(); break; } } } } for (let i = 0; i < args.length; i++) { let regexp = /\${[\w]*}/g; let arg = args[i]; let result; while ((result = regexp.exec(arg)) != null) { let match = result[0]; let v = match.substr(2, match.length - 3); let toReplace = null; for (let env in this.environmentVariables) { if (env == v) { toReplace = this.environmentVariables[env]; break; } } if (toReplace != null) { args[i] = arg.replace(new RegExp("\\" + match, "g"), toReplace); } } } let file = this.findPath(command); if (file && (command.startsWith("./") || command.startsWith("~/") || command.startsWith("../"))) { if (file instanceof BinaryFile) { file.run(this.terminal, args); return; } else { this.stdio.println(`${file.name} is not executable`); } } else { if (command.startsWith(".")) { this.stdio.println(`File not found`); } else { this.stdio.println(`Command not found`); } } this.takeCommand(); }, prefix: " > ", printAfterDone: true, style: "red" }); this.stdio.refresh(); } /** * Returns the file at the specified path. * Example usage: findPath("~/somefolder/hello/"); */ findPath(path) { let originalPath = path.slice(); let pathPieces = path.split("/"); let relativeFolder = this.currentFolder; let fileFound = null; for (let i = 0; i < pathPieces.length; i++) { let p = pathPieces[i]; if (p == "") { if (relativeFolder == null) { fileFound = null; break; } if (i == pathPieces.length - 1 && relativeFolder instanceof Folder) { fileFound = relativeFolder; break; } fileFound = null; break; } else if (p == "..") { if (i == 0) { if (this.currentFolder.parentFolder == null) { fileFound = null; break; } relativeFolder = this.currentFolder.parentFolder; } else if (relativeFolder == null) { fileFound = null; break; } else if (relativeFolder.parentFolder == null) { fileFound = null; break; } else { relativeFolder = relativeFolder.parentFolder; } if (i == pathPieces.length - 1) { fileFound = relativeFolder; break; } } else if (p == ".") { if (i == 0) { relativeFolder = this.currentFolder; } if (i == pathPieces.length - 1) { fileFound = relativeFolder; break; } continue; } else if (p == "~" && i == 0) { relativeFolder = this.filesystem.rootFolder; if (i == pathPieces.length - 1) { fileFound = relativeFolder; break; } continue; } else if (i == pathPieces.length - 1) { if (relativeFolder == null) { fileFound = null; break; } let idx = relativeFolder.files.map((f) => { return f.name; }) .indexOf(p); let found = relativeFolder.files[idx]; if (idx == -1) { break; } else { fileFound = found; } } else { if (relativeFolder == null) { fileFound = null; break; } let idx = relativeFolder.files.map((f) => { return f.name; }) .indexOf(p); let found = relativeFolder.files[idx]; if (idx == -1) { break; } else if (found instanceof Folder) { relativeFolder = found; } else { break; } } } return fileFound; } } JavaScriptX.JSX = JSX; class FileSystem { constructor() { this.rootFolder = new Folder("~"); } } class File { constructor(name, hidden = false) { this.created = new Date().getTime(); this.name = name; this.hidden = hidden; } /** * Returns the string path of the file. * Example: If the file is in root folder (~) and inside a 'somefolder'-folder, it would return "~/somefolder/" */ getPath() { let path = ""; let curr = this; while (curr != null) { if (curr instanceof Folder || path != "") { path = "/" + path; } path = curr.name + path; curr = curr.parentFolder; } return path; } } /** * File that contains other files. */ class Folder extends File { constructor(name, hidden = false) { super(name, hidden); this.files = []; } /** * Add file to this folder. */ addFile(file) { this.files.push(file); file.parentFolder = this; } } JavaScriptX.Folder = Folder; /** * File that contains JavaScript program which can be run */ class BinaryFile extends File { constructor(name, program, hidden = false) { super(name, hidden); this.program = program; } /** * Launch the program specified in this file. */ run(terminal, args) { terminal.launchProgram(this.program, args); } } JavaScriptX.BinaryFile = BinaryFile; /** * File that contains text */ class TextFile extends File { constructor(name, content = "", hidden = false) { super(name, hidden); this.content = content; } readContent() { return this.content.slice(); } setContent(content) { this.content = content; } } JavaScriptX.TextFile = TextFile; /** * Formats the given string to a certain length and alignment. * Example: formatToLength("hello", 10, "right") -> ".....hello" (where dots represent spaces) */ function formatToLength(text, length, alignment = "left") { if (alignment == "left") { text += " ".repeat(length); return text.substr(0, text.length - (text.length - length)); } else if (alignment == "right") { text = " ".repeat(length) + text; return text.substr(-length); } else if (alignment == "center") { let left = Math.max(0, Math.floor(length / 2 - text.length / 2)); let right = Math.max(0, Math.ceil(length / 2 - text.length / 2)); return " ".repeat(left) + text + " ".repeat(right); } return null; } JavaScriptX.formatToLength = formatToLength; })(JavaScriptX || (JavaScriptX = {}));