tsc'd and scss'd
This commit is contained in:
parent
aa4d024f6e
commit
0d1f1ffa3f
39
terminal/css/custom.css
Normal file
39
terminal/css/custom.css
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is licensed under the CC-0 license.
|
||||
*/
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #333;
|
||||
color: #eaeaea; }
|
||||
|
||||
h1, p {
|
||||
font-family: helvetica; }
|
||||
|
||||
.terminal-area {
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
/* bring your own prefixes */
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 15px; }
|
||||
.terminal-area .js-terminal {
|
||||
background-color: #202020;
|
||||
color: #cecece; }
|
||||
|
||||
.red {
|
||||
color: #f88; }
|
||||
|
||||
.blue {
|
||||
color: #88f; }
|
||||
|
||||
.green {
|
||||
color: #8f8; }
|
||||
|
||||
.yellow {
|
||||
color: #ff8; }
|
||||
|
||||
/*# sourceMappingURL=custom.css.map */
|
7
terminal/css/custom.css.map
Normal file
7
terminal/css/custom.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA;;EAEE;AAEF,UAAW;EACR,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,gBAAgB,EAAE,IAAI;EACtB,KAAK,EAAE,OAAO;;AAGjB,KAAM;EAEH,WAAW,EAAE,SAAS;;AAGzB,cAAe;EACZ,UAAU,EAAE,MAAM;EAElB,QAAQ,EAAE,KAAK;EACf,GAAG,EAAE,GAAG;EACR,IAAI,EAAE,GAAG;EACT,6BAA6B;EAC7B,SAAS,EAAE,qBAAqB;EAEhC,SAAS,EAAE,IAAI;EAEf,2BAAa;IACV,gBAAgB,EAAE,OAAO;IACzB,KAAK,EAAE,OAAO;;AAQpB,IAAK;EAEF,KAAK,EAAE,IAAI;;AAGd,KAAM;EAEH,KAAK,EAAE,IAAI;;AAId,MAAO;EAEJ,KAAK,EAAE,IAAI;;AAGd,OAAQ;EAEL,KAAK,EAAE,IAAI",
|
||||
"sources": ["../scss/custom.scss"],
|
||||
"names": [],
|
||||
"file": "custom.css"
|
||||
}
|
22
terminal/css/jsterminal.css
Normal file
22
terminal/css/jsterminal.css
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
.js-terminal-style {
|
||||
font-family: monospace;
|
||||
overflow: hidden; }
|
||||
.js-terminal-style p {
|
||||
text-align: left;
|
||||
font-family: inherit;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
margin: 0;
|
||||
line-height: 1em; }
|
||||
|
||||
/*# sourceMappingURL=jsterminal.css.map */
|
7
terminal/css/jsterminal.css.map
Normal file
7
terminal/css/jsterminal.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA;;;;;;;;;EASE;AAEF,kBAAmB;EAChB,WAAW,EAAE,SAAS;EAEtB,QAAQ,EAAE,MAAM;EAEhB,oBAAE;IACC,UAAU,EAAE,IAAI;IAEhB,WAAW,EAAE,OAAO;IACpB,WAAW,EAAE,QAAQ;IACrB,SAAS,EAAE,UAAU;IAErB,MAAM,EAAE,CAAC;IAET,WAAW,EAAE,GAAG",
|
||||
"sources": ["../scss/jsterminal.scss"],
|
||||
"names": [],
|
||||
"file": "jsterminal.css"
|
||||
}
|
299
terminal/js/jash.js
Normal file
299
terminal/js/jash.js
Normal file
@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var Jash;
|
||||
(function (Jash) {
|
||||
class SH {
|
||||
constructor() {
|
||||
this.cursor = { x: 0, y: 0 };
|
||||
this.scroll = 0;
|
||||
this.messages = [];
|
||||
this.defaultStyle = "";
|
||||
this.currentPrintStyle = "";
|
||||
this.listenerId = 0;
|
||||
this.currentReadlines = [];
|
||||
this.currentReadline = "";
|
||||
}
|
||||
/**
|
||||
* Inherited from Program-interface. Not to be manually called.
|
||||
*/
|
||||
create(terminal, stdio, args) {
|
||||
this.terminal = terminal;
|
||||
this.stdio = stdio;
|
||||
this.stdio.setOutput(this);
|
||||
this.stdio.setInput(this);
|
||||
this.stdio.addListener(this);
|
||||
}
|
||||
/**
|
||||
* Inherited from Program-interface. Not to be manually called.
|
||||
*/
|
||||
enable() {
|
||||
this.refresh();
|
||||
if (this.listenerId < 0) {
|
||||
this.stdio.addListener(this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Inherited from Program-interface. Not to be manually called.
|
||||
*/
|
||||
disable() {
|
||||
if (this.listenerId >= 0) {
|
||||
this.stdio.removeListener(this.listenerId);
|
||||
this.listenerId = -1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Inherited from Program-interface. Not to be manually called.
|
||||
*/
|
||||
onClose() {
|
||||
if (this.listenerId >= 0) {
|
||||
this.stdio.removeListener(this.listenerId);
|
||||
this.listenerId = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Inherited from TerminalInput.KeyListener-interface. Not to be manually called.
|
||||
*/
|
||||
onListen(listenerId) {
|
||||
this.listenerId = listenerId;
|
||||
}
|
||||
/**
|
||||
* Inherited from TerminalInput.KeyListener-interface. Not to be manually called.
|
||||
*/
|
||||
handleKeypress(keypress) {
|
||||
}
|
||||
/**
|
||||
* Inherited from TerminalInput.KeyListener-interface. Not to be manually called.
|
||||
*/
|
||||
handleKeydown(keydown) {
|
||||
if (this.currentReadlines.length == 0) {
|
||||
return;
|
||||
}
|
||||
let key = keydown.key;
|
||||
if (key.length == 1) {
|
||||
this.currentReadline += key;
|
||||
if (this.currentReadlines[0].onChangeCallback !== undefined) {
|
||||
this.currentReadlines[0].onChangeCallback(this.currentReadline);
|
||||
}
|
||||
if (key == "/") {
|
||||
keydown.preventDefault();
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (keydown.keyCode) {
|
||||
case (32): {
|
||||
this.currentReadline += " ";
|
||||
if (this.currentReadlines[0].onChangeCallback !== undefined) {
|
||||
this.currentReadlines[0].onChangeCallback(this.currentReadline);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (8): {
|
||||
this.currentReadline = this.currentReadline.substr(0, this.currentReadline.length - 1);
|
||||
if (this.currentReadlines[0].onChangeCallback !== undefined) {
|
||||
this.currentReadlines[0].onChangeCallback(this.currentReadline);
|
||||
}
|
||||
keydown.preventDefault();
|
||||
break;
|
||||
}
|
||||
case (13): {
|
||||
let currReadline = this.currentReadlines[0];
|
||||
if (currReadline.printAfterDone) {
|
||||
let text = currReadline.prefix + this.currentReadline;
|
||||
this.print(text, currReadline.style);
|
||||
}
|
||||
this.currentReadlines.shift().callback(this.currentReadline);
|
||||
this.currentReadline = "";
|
||||
}
|
||||
case (0): {
|
||||
if (keydown.key == "Dead") {
|
||||
this.currentReadline += "~";
|
||||
if (this.currentReadlines[0].onChangeCallback !== undefined) {
|
||||
this.currentReadlines[0].onChangeCallback(this.currentReadline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
/**
|
||||
* Inherited from TerminalInput.KeyListener-interface. Not to be manually called.
|
||||
*/
|
||||
handleKeyup(keyup) {
|
||||
}
|
||||
/**
|
||||
* Inherited from Std.Input-interface. Not to be manually called.
|
||||
*/
|
||||
readline(readline) {
|
||||
this.currentReadline = readline.default || "";
|
||||
this.currentReadlines.push(readline);
|
||||
this.refresh();
|
||||
}
|
||||
/**
|
||||
* Inherited from Std.Output-interface. Not to be manually called.
|
||||
*/
|
||||
print(text, style) {
|
||||
style = style || this.currentPrintStyle || this.defaultStyle;
|
||||
let isAtEnd = (this.messages.length - this.terminal.height - this.scroll) <= 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let char = text[i];
|
||||
if (char == "\n") {
|
||||
if (this.cursor.y != 0 || this.cursor.x != 0) {
|
||||
this.moveCursorNewline();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
this.putCharacter(new JSTerminal.Character(char, style));
|
||||
}
|
||||
if (this.messages.length > this.terminal.height) {
|
||||
this.scroll = this.messages.length - this.terminal.height;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Inherited from Std.Output-interface. Not to be manually called.
|
||||
*/
|
||||
println(text, style) {
|
||||
this.print("\n" + text, style || this.currentPrintStyle || this.defaultStyle);
|
||||
}
|
||||
/**
|
||||
* Inherited from Std.Output-interface. Not to be manually called.
|
||||
*/
|
||||
clear(style) {
|
||||
this.messages = [];
|
||||
this.scroll = 0;
|
||||
}
|
||||
/**
|
||||
* Inherited from Std.Output-interface. Not to be manually called.
|
||||
*/
|
||||
refresh(alsoRenderTerminal = true) {
|
||||
this.terminal.clearScreen(this.defaultStyle);
|
||||
let toDraw = [];
|
||||
let messages = [];
|
||||
for (let i in this.messages) {
|
||||
messages.push(this.messages[i].slice());
|
||||
}
|
||||
if (this.currentReadlines.length > 0 && !this.currentReadlines[0].hidden) {
|
||||
let readline = this.currentReadlines[0];
|
||||
let readlinePart = (readline.prefix || "") + this.currentReadline;
|
||||
let tempCursorPos = { x: this.cursor.x, y: this.cursor.y };
|
||||
for (let i = 0; i < readlinePart.length; i++) {
|
||||
let char = readlinePart[i];
|
||||
if (char == "\n") {
|
||||
this.moveCursorNewline();
|
||||
continue;
|
||||
}
|
||||
this.putCharacterTo(new JSTerminal.Character(char, (readline.style || this.defaultStyle)), messages);
|
||||
}
|
||||
this.cursor = tempCursorPos;
|
||||
}
|
||||
let start = messages.length - 1;
|
||||
for (let i = this.scroll; i < messages.length && toDraw.length < this.terminal.height; i++) {
|
||||
if (i < 0 || messages[i].length == 0) {
|
||||
toDraw.push([]);
|
||||
continue;
|
||||
}
|
||||
let rows = Math.ceil(messages[i].length / this.terminal.width);
|
||||
for (let r = 0; r < rows; r++) {
|
||||
toDraw.push(messages[i].slice(r * this.terminal.width, (r + 1) * this.terminal.width));
|
||||
}
|
||||
}
|
||||
toDraw = toDraw.slice(-this.terminal.height);
|
||||
toDraw.forEach((row, y) => {
|
||||
row.forEach((c, x) => {
|
||||
this.terminal.putTrueChar(c, x, y);
|
||||
});
|
||||
});
|
||||
if (!alsoRenderTerminal) {
|
||||
return;
|
||||
}
|
||||
this.terminal.render();
|
||||
}
|
||||
/**
|
||||
* Utility function for print. Prints out one character at cursor.
|
||||
*/
|
||||
putCharacter(character) {
|
||||
this.putCharacterTo(character, this.messages);
|
||||
}
|
||||
putCharacterTo(character, charArray) {
|
||||
while (this.cursor.y >= charArray.length) {
|
||||
charArray.push([]);
|
||||
}
|
||||
if (this.cursor.x >= charArray[this.cursor.y].length) {
|
||||
charArray[this.cursor.y].push(character);
|
||||
this.moveCursor(1);
|
||||
return;
|
||||
}
|
||||
charArray[this.cursor.y][this.cursor.x] = character;
|
||||
this.moveCursor(1);
|
||||
}
|
||||
/**
|
||||
* Removes a character and moves the cursor backwards.
|
||||
*/
|
||||
removeCharacter() {
|
||||
if (this.messages[this.cursor.y] == []) {
|
||||
let rowToRemove = this.cursor.y;
|
||||
this.moveCursor(-1);
|
||||
this.messages.splice(this.cursor.y, 1);
|
||||
return;
|
||||
}
|
||||
let toRemove = { x: this.cursor.x, y: this.cursor.y };
|
||||
this.moveCursor(-1);
|
||||
this.messages[toRemove.y].splice(toRemove.x - 1, 1);
|
||||
}
|
||||
/**
|
||||
* Set the default style to be used when printing and clearing the screen.
|
||||
*/
|
||||
setDefaultStyle(style) {
|
||||
this.defaultStyle = style;
|
||||
}
|
||||
/**
|
||||
* Set the current 'default' style for printing.
|
||||
* This is only temporary and can be reversed with calling it with a null-parameter.
|
||||
*/
|
||||
setStyle(style) {
|
||||
this.currentPrintStyle = style;
|
||||
}
|
||||
/**
|
||||
* Moves the cursor by 'amount' forwards or backwards.
|
||||
* Handles text wrapping.
|
||||
*/
|
||||
moveCursor(amount) {
|
||||
this.cursor.x += amount;
|
||||
this.cursor.y += Math.floor((this.cursor.x) / (this.terminal.width));
|
||||
this.cursor.x = this.cursor.x % (this.terminal.width);
|
||||
if (this.cursor.x < 0) {
|
||||
this.cursor.x = this.messages[this.cursor.y].length - 1 - this.cursor.x;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Moves the cursor vertically by amount. Works weirdly if cursor.x not handled manually.
|
||||
*/
|
||||
moveCursorVertically(amount) {
|
||||
this.cursor.y += amount;
|
||||
}
|
||||
/*
|
||||
* Moves the cursor down to the next line and sets the x to 0.
|
||||
*/
|
||||
moveCursorNewline() {
|
||||
this.moveCursorVertically(1);
|
||||
this.cursor.x = 0;
|
||||
}
|
||||
/*
|
||||
* Does the opposite as moveCursorNewline
|
||||
*/
|
||||
moveCursorPreviousLine() {
|
||||
this.moveCursorVertically(-1);
|
||||
this.cursor.x = this.terminal.width - 1;
|
||||
}
|
||||
}
|
||||
Jash.SH = SH;
|
||||
})(Jash || (Jash = {}));
|
156
terminal/js/jsterminal.js
Normal file
156
terminal/js/jsterminal.js
Normal file
@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var JSTerminal;
|
||||
(function (JSTerminal) {
|
||||
class TerminalProgram {
|
||||
create(terminal, stdio) { }
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return false; }
|
||||
}
|
||||
class Terminal {
|
||||
constructor(selector, width, height) {
|
||||
this.selector = selector;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.lineElems = [];
|
||||
this.characters = [];
|
||||
for (let i = 0; i < this.height; i++) {
|
||||
let characterLine = [];
|
||||
for (let i2 = 0; i2 < this.width; i2++) {
|
||||
characterLine.push(new Character(" ", ""));
|
||||
}
|
||||
this.characters.push(characterLine);
|
||||
let line = " ".repeat(this.width);
|
||||
let element = $(document.createElement("p"));
|
||||
element.text(line);
|
||||
this.lineElems.push(element);
|
||||
$(selector).append(element);
|
||||
}
|
||||
this.stdio = new Std.IO(this);
|
||||
this.programStack = [];
|
||||
this.programStack.push(new TerminalProgram());
|
||||
}
|
||||
/**
|
||||
* Used to simply clear the terminal screen.
|
||||
* Useful for when you don't use Jash/JSX.
|
||||
*/
|
||||
clearScreen(styles = "") {
|
||||
for (let y = 0; y < this.height; y++) {
|
||||
for (let x = 0; x < this.width; x++) {
|
||||
this.characters[y][x] = new Character(" ", styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Used to write text at a certain coordinate on screen.
|
||||
* Useful for when you want to make your own rendering systems
|
||||
* and don't want to use regular text-based systems.
|
||||
*/
|
||||
writeText(text, x, y, styles = "") {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
this.putChar(text[i], x + i, y, styles);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Utility function for writeText. Can also be used but probably not as useful.
|
||||
* Prints only one char (first from given string).
|
||||
*/
|
||||
putChar(char, x, y, styles = "") {
|
||||
this.putTrueChar(new Character(char, styles), x, y);
|
||||
}
|
||||
/**
|
||||
* Utility function when you might want to print out a true
|
||||
* Character-class. (works similarily to putChar)
|
||||
*/
|
||||
putTrueChar(char, x, y) {
|
||||
if (x >= this.width || y >= this.height) {
|
||||
return;
|
||||
}
|
||||
this.characters[y][x] = char;
|
||||
}
|
||||
/**
|
||||
* Empties the screen and re-draws it. Required to call every time
|
||||
* you want to change the screen.
|
||||
*/
|
||||
render() {
|
||||
for (let y = 0; y < this.characters.length; y++) {
|
||||
let line = this.characters[y];
|
||||
let lineElem = this.lineElems[y];
|
||||
lineElem.empty();
|
||||
let currSpan = null;
|
||||
let currStyle = null;
|
||||
let currText = "";
|
||||
for (let x = 0; x < line.length + 1; x++) {
|
||||
let currChar = line[x];
|
||||
if (x == line.length || currStyle != currChar.styles) {
|
||||
if (currSpan != null) {
|
||||
currSpan.text(currText);
|
||||
currSpan.addClass(currStyle);
|
||||
lineElem.append(currSpan);
|
||||
}
|
||||
if (x >= line.length) {
|
||||
break;
|
||||
}
|
||||
currText = "";
|
||||
currSpan = $(document.createElement("span"));
|
||||
currStyle = currChar.styles;
|
||||
}
|
||||
currText += currChar.char;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Used to launch programs like Jash.
|
||||
*/
|
||||
launchProgram(program, args) {
|
||||
this.programStack.slice(-1).pop().disable();
|
||||
this.programStack.push(program);
|
||||
program.create(this, this.stdio, args);
|
||||
}
|
||||
/**
|
||||
* Used to close programs like Jash.
|
||||
*/
|
||||
closeProgram(program) {
|
||||
let idx = this.programStack.indexOf(program);
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
if (this.programStack[idx].onClose()) {
|
||||
if (idx == this.programStack.length - 1) {
|
||||
this.programStack[this.programStack.length - 2].enable();
|
||||
}
|
||||
this.programStack.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
JSTerminal.Terminal = Terminal;
|
||||
/**
|
||||
* Utility function for creating the terminal.
|
||||
* Initializes the class and sets up some classes you might want to use.
|
||||
*/
|
||||
function createTerminal(selector, width, height) {
|
||||
console.info(`Creating a terminal with ${width}x${height}`);
|
||||
$(selector).addClass("js-terminal-style");
|
||||
$(selector).css('width', width + "ch");
|
||||
$(selector).css('height', height + "em");
|
||||
$(selector).empty();
|
||||
return new Terminal(selector, width, height);
|
||||
}
|
||||
JSTerminal.createTerminal = createTerminal;
|
||||
class Character {
|
||||
constructor(char, styles) {
|
||||
this.char = char[0];
|
||||
this.styles = styles;
|
||||
}
|
||||
}
|
||||
JSTerminal.Character = Character;
|
||||
})(JSTerminal || (JSTerminal = {}));
|
336
terminal/js/jsx.js
Normal file
336
terminal/js/jsx.js
Normal file
@ -0,0 +1,336 @@
|
||||
/**
|
||||
* 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/<filename>"
|
||||
*/
|
||||
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 = {}));
|
416
terminal/js/jsxcommands.js
Normal file
416
terminal/js/jsxcommands.js
Normal file
@ -0,0 +1,416 @@
|
||||
var JSXCommands;
|
||||
(function (JSXCommands) {
|
||||
/**
|
||||
* Echoes the given arguments
|
||||
*/
|
||||
class Echo {
|
||||
create(terminal, stdio, args) {
|
||||
if (args.filter((a) => { return a != ""; }).length == 0) {
|
||||
stdio.println("Nothing echoes... nothing");
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
stdio.println(args.join(" "));
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "Echoes the given arguments" +
|
||||
"\n\nUsage: echo [anything to echo]";
|
||||
}
|
||||
}
|
||||
JSXCommands.Echo = Echo;
|
||||
/**
|
||||
* Lists all files in the given directory or this directory, if path not specified.
|
||||
*/
|
||||
class List {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
let folder = this.jsx.currentFolder;
|
||||
if (args.length > 0) {
|
||||
let pathStr = args.slice(-1)[0];
|
||||
let path = this.jsx.findPath(pathStr);
|
||||
if (path instanceof JavaScriptX.Folder) {
|
||||
args.splice(-1);
|
||||
folder = path;
|
||||
}
|
||||
}
|
||||
let argObj = parseArgsToObject(args);
|
||||
let a = "a" in argObj;
|
||||
if (a) {
|
||||
stdio.println(".", "blue");
|
||||
if (folder.parentFolder != null) {
|
||||
stdio.println("..", "blue");
|
||||
}
|
||||
}
|
||||
let files = folder.files.filter(f => (!f.hidden || a));
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let f = files[i];
|
||||
stdio.println(JavaScriptX.formatToLength(f.name, 20), ((f.hidden) ? "blue" : (f instanceof JavaScriptX.Folder) ? "red" : "yellow"));
|
||||
stdio.print(" " +
|
||||
((f instanceof JavaScriptX.Folder) ? "DIR" : ((f instanceof JavaScriptX.BinaryFile) ? "BIN" : "TXT")));
|
||||
stdio.print(" " + new Date(f.created).toLocaleString());
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "Lists all files in the given directory\nor this directory, if path not specified." +
|
||||
"\n\nUsage: ls [options] [path]" +
|
||||
"\n\nOptions:\n " + JavaScriptX.formatToLength("-a", 10) + "List all files (incl. hidden)";
|
||||
}
|
||||
}
|
||||
JSXCommands.List = List;
|
||||
/**
|
||||
* Changes directory to the specified directory.
|
||||
*/
|
||||
class ChangeDirectory {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
if (args.length == 0) {
|
||||
stdio.println("Insufficent parameters, usage:");
|
||||
stdio.println("cd [path]");
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
let path = this.jsx.findPath(args[0]);
|
||||
let folder = false;
|
||||
if (path) {
|
||||
if (path instanceof JavaScriptX.Folder) {
|
||||
folder = true;
|
||||
}
|
||||
else {
|
||||
stdio.println(`'${args[0]}' is not a folder.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stdio.println(`'${args[0]}' not found.`);
|
||||
}
|
||||
if (folder && path instanceof JavaScriptX.Folder) {
|
||||
this.jsx.currentFolder = path;
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "Changes directory to the specified directory." +
|
||||
"\n\nUsage: cd [path]";
|
||||
}
|
||||
}
|
||||
JSXCommands.ChangeDirectory = ChangeDirectory;
|
||||
/**
|
||||
* Creates a directory at specified location.
|
||||
*/
|
||||
class MakeDirectory {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
if (args.length == 0) {
|
||||
stdio.println("Insufficent parameters, usage:");
|
||||
stdio.println("mkdir [path]");
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
let pathArg = args.splice(-1)[0];
|
||||
let argObj = parseArgsToObject(args);
|
||||
let h = "h" in argObj;
|
||||
let origPathpieces = pathArg.split("/");
|
||||
let curr = [origPathpieces[0]];
|
||||
let i = 0;
|
||||
let createFolder = false;
|
||||
do {
|
||||
let path = this.jsx.findPath(curr.join("/"));
|
||||
if (!path) {
|
||||
if (origPathpieces.length == curr.length) {
|
||||
createFolder = true;
|
||||
}
|
||||
else {
|
||||
stdio.println(`${curr.join("/")} not found!`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (path && origPathpieces.length == curr.length) {
|
||||
stdio.println(`${curr.join("/")} already exists!`);
|
||||
}
|
||||
else if (!(path instanceof JavaScriptX.Folder)) {
|
||||
stdio.println(`${curr.join("/")} is not a folder!`);
|
||||
}
|
||||
if (i < origPathpieces.length) {
|
||||
i += 1;
|
||||
curr.push(origPathpieces[i]);
|
||||
}
|
||||
} while (curr.length < origPathpieces.length + 1);
|
||||
if (createFolder) {
|
||||
let path = origPathpieces.slice(0, -1).join("/");
|
||||
let name = origPathpieces.slice(-1)[0];
|
||||
let relative = this.jsx.findPath(path);
|
||||
if (relative instanceof JavaScriptX.Folder) {
|
||||
relative.addFile(new JavaScriptX.Folder(name, h));
|
||||
}
|
||||
else {
|
||||
stdio.println("An error occoured creating the folder.");
|
||||
}
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "Creates a directory at specified location." +
|
||||
"\n\nUsage: mkdir [options] [path]" +
|
||||
"\n\nOptions:\n " + JavaScriptX.formatToLength("-h", 10) + "Makes the folder hidden.";
|
||||
}
|
||||
}
|
||||
JSXCommands.MakeDirectory = MakeDirectory;
|
||||
/**
|
||||
* The Woman in the JSX equivalent to the UNIX Man.
|
||||
* It's called Woman (not Man) because of the funny wording,
|
||||
* where 'man' sounds like the gender 'man'
|
||||
*/
|
||||
class Woman {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
if (args.length == 0) {
|
||||
this.readManual(stdio, this);
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
let filePath = args.slice(-1)[0];
|
||||
for (let env in this.jsx.environmentVariables) {
|
||||
if (filePath == env) {
|
||||
filePath = env;
|
||||
break;
|
||||
}
|
||||
let envFolder = this.jsx.findPath(this.jsx.environmentVariables[env]);
|
||||
if (envFolder instanceof JavaScriptX.Folder) {
|
||||
for (let i = 0; i < envFolder.files.length; i++) {
|
||||
if (envFolder.files[i].name == filePath) {
|
||||
filePath = envFolder.files[i].getPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let f = this.jsx.findPath(filePath);
|
||||
if (f && f instanceof JavaScriptX.BinaryFile && manualGuard(f.program)) {
|
||||
this.readManual(stdio, f.program);
|
||||
}
|
||||
else {
|
||||
stdio.println("Nothing to womansplain.");
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "The Woman in the JSX equivalent to the UNIX Man. " +
|
||||
"It's called Woman (not Man) because of the funny wording, " +
|
||||
"where 'man' sounds like the gender 'man'" +
|
||||
"\n\nUsage: woman [path]";
|
||||
}
|
||||
readManual(stdio, manual) {
|
||||
stdio.println(manual.getManual());
|
||||
}
|
||||
}
|
||||
JSXCommands.Woman = Woman;
|
||||
class Caternate {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
if (args.length == 0) {
|
||||
stdio.println("Insufficent arguments.");
|
||||
stdio.println("\nUsage: cat [path]");
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
let path = args.splice(-1)[0];
|
||||
let file = this.jsx.findPath(path);
|
||||
if (file instanceof JavaScriptX.BinaryFile) {
|
||||
stdio.println("Unable to read binary files (yet).");
|
||||
}
|
||||
else if (file instanceof JavaScriptX.Folder) {
|
||||
stdio.println(file.name, "blue");
|
||||
stdio.print(" is a DIRectory containing ");
|
||||
stdio.print(file.files.length + "", "red");
|
||||
stdio.print(" files.");
|
||||
}
|
||||
else if (file instanceof JavaScriptX.TextFile) {
|
||||
let contents = file.readContent();
|
||||
stdio.println(contents);
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "[cat]ernate reads data from given file and outputs it." +
|
||||
"\n\nUsage: cat [path]";
|
||||
}
|
||||
readManual(stdio, manual) {
|
||||
stdio.println(manual.getManual());
|
||||
}
|
||||
}
|
||||
JSXCommands.Caternate = Caternate;
|
||||
class Touch {
|
||||
constructor(jsx) {
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
if (args.length == 0) {
|
||||
stdio.println("Insufficent arguments.");
|
||||
stdio.println("\nUsage: touch [path]");
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
let path = args.splice(-1)[0];
|
||||
let file = this.jsx.findPath(path);
|
||||
if (file) {
|
||||
file.created = new Date().getTime();
|
||||
}
|
||||
else {
|
||||
let origPathparts = path.split("/");
|
||||
let curr = [origPathparts[0]];
|
||||
for (let i = 1; i < origPathparts.length; i++) {
|
||||
let currFile = this.jsx.findPath(curr.join("/"));
|
||||
if (!currFile) {
|
||||
stdio.println(`${curr.join("/")} not found.`);
|
||||
terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
curr.push(origPathparts[i]);
|
||||
}
|
||||
let filename = curr.splice(-1);
|
||||
let folder = this.jsx.findPath(curr.join("/"));
|
||||
if (folder instanceof JavaScriptX.Folder) {
|
||||
folder.addFile(new JavaScriptX.TextFile(filename[0]));
|
||||
}
|
||||
else {
|
||||
stdio.println(`${curr.join("/")} is not a folder.`);
|
||||
}
|
||||
}
|
||||
terminal.closeProgram(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "Touch touches the file specified updating it's timestamp. If the file doesn't exist, it's created." +
|
||||
"\n\nUsage: touch [path]";
|
||||
}
|
||||
readManual(stdio, manual) {
|
||||
stdio.println(manual.getManual());
|
||||
}
|
||||
}
|
||||
JSXCommands.Touch = Touch;
|
||||
class JEdi {
|
||||
constructor(jsx) {
|
||||
this.text = [""];
|
||||
this.row = 0;
|
||||
this.jsx = jsx;
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
this.stdio = stdio;
|
||||
this.terminal = terminal;
|
||||
this.doCommand();
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() { return true; }
|
||||
getManual() {
|
||||
return "(J)avascript (Edi)tor is a custom version of the UNIX editor 'ed'." +
|
||||
"\nIt's quite simpler in the way that all of it's commands are whole words" +
|
||||
"\n\nUsage: jedi [(optional) path]";
|
||||
}
|
||||
readManual(stdio, manual) {
|
||||
stdio.println(manual.getManual());
|
||||
}
|
||||
doCommand() {
|
||||
let readline = this.stdio.readline({
|
||||
callback: c => this.exec(c),
|
||||
prefix: "\n> ",
|
||||
printAfterDone: true
|
||||
});
|
||||
}
|
||||
exec(command) {
|
||||
if (command == "exit" || command == "quit") {
|
||||
this.terminal.closeProgram(this);
|
||||
return;
|
||||
}
|
||||
else if (command.split(" ")[0] == "append") {
|
||||
let appended = command.split(" ").slice(1).join(" ");
|
||||
debugger;
|
||||
console.log(appended);
|
||||
let rows = appended.split("\n");
|
||||
console.log(rows);
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
if (this.text.length <= (this.row + i)) {
|
||||
this.text.push("");
|
||||
}
|
||||
this.text[this.row + i] += rows[i];
|
||||
}
|
||||
this.row += rows.length - 1;
|
||||
this.stdio.println(`${this.text[this.row]}`);
|
||||
}
|
||||
this.doCommand();
|
||||
}
|
||||
}
|
||||
JSXCommands.JEdi = JEdi;
|
||||
/**
|
||||
* Parses all given args from '--arg val' into format {arg: val}
|
||||
*/
|
||||
function parseArgsToObject(args, defaults = {}) {
|
||||
let returned = {};
|
||||
for (let key in defaults) {
|
||||
returned[key] = defaults[key];
|
||||
}
|
||||
let currArg = null;
|
||||
args.forEach(arg => {
|
||||
if (arg.startsWith("-")) {
|
||||
if (arg.startsWith("--")) {
|
||||
if (currArg) {
|
||||
returned[currArg] = null;
|
||||
}
|
||||
;
|
||||
currArg = arg.substr(2);
|
||||
return;
|
||||
}
|
||||
if (currArg) {
|
||||
returned[currArg] = null;
|
||||
}
|
||||
;
|
||||
currArg = arg.substr(1);
|
||||
}
|
||||
else {
|
||||
if (currArg) {
|
||||
returned[currArg] = arg;
|
||||
currArg = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (currArg) {
|
||||
returned[currArg] = null;
|
||||
}
|
||||
;
|
||||
return returned;
|
||||
}
|
||||
function manualGuard(arg) {
|
||||
return arg.getManual !== undefined;
|
||||
}
|
||||
})(JSXCommands || (JSXCommands = {}));
|
113
terminal/js/jukebox.js
Normal file
113
terminal/js/jukebox.js
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var Jukebox;
|
||||
(function (Jukebox) {
|
||||
/**
|
||||
* The Jukebox Player is a program which when run sets up a virtual stereo
|
||||
* so you can hear the sounds played by stdio's sound-methods!
|
||||
*/
|
||||
class Player {
|
||||
constructor() {
|
||||
this.soundCounter = 0;
|
||||
this.loadedSounds = {};
|
||||
this.soundChannels = {};
|
||||
}
|
||||
create(terminal, stdio, args) {
|
||||
this.terminal = terminal;
|
||||
stdio.setAudioOutput(this);
|
||||
}
|
||||
enable() { }
|
||||
disable() { }
|
||||
onClose() {
|
||||
return true;
|
||||
}
|
||||
play(channel, sound) {
|
||||
let ch = channel | 0;
|
||||
if (!(ch in this.soundChannels)) {
|
||||
// Create a new channel
|
||||
let audio = $(document.createElement("audio"));
|
||||
audio.attr("autoplay", "true");
|
||||
audio.appendTo($(this.terminal.selector));
|
||||
this.soundChannels[ch] = {
|
||||
element: audio,
|
||||
sound: undefined
|
||||
};
|
||||
}
|
||||
let currChannel = this.soundChannels[ch];
|
||||
let oldSound = currChannel.sound;
|
||||
if (sound !== undefined) {
|
||||
if (!(sound.soundRefrence in this.loadedSounds)) {
|
||||
console.error("Unable to find sound refrence " + sound.soundRefrence);
|
||||
return;
|
||||
}
|
||||
sound.onEndCallback = sound.onEndCallback || (() => { });
|
||||
sound.onStartCallback = sound.onStartCallback || (() => { });
|
||||
if (oldSound) {
|
||||
currChannel.element.prop("currentTime", "0");
|
||||
}
|
||||
currChannel.sound = sound;
|
||||
}
|
||||
if (oldSound != currChannel.sound) {
|
||||
currChannel.element.empty();
|
||||
currChannel.element.trigger("pause");
|
||||
if (oldSound) {
|
||||
oldSound.onEndCallback();
|
||||
}
|
||||
if (currChannel.sound !== undefined) {
|
||||
currChannel.element.append(this.loadedSounds[currChannel.sound.soundRefrence]);
|
||||
}
|
||||
}
|
||||
if (currChannel.sound && currChannel.element) {
|
||||
currChannel.element.prop("volume", Math.min(currChannel.sound.volume, 1));
|
||||
if (currChannel.sound.loops) {
|
||||
currChannel.element.attr("loop", "true");
|
||||
}
|
||||
else {
|
||||
currChannel.element.removeAttr("loop");
|
||||
}
|
||||
currChannel.sound.onStartCallback();
|
||||
currChannel.element.trigger("play");
|
||||
}
|
||||
}
|
||||
pause(channel) {
|
||||
let ch = channel | 0;
|
||||
if (!(ch in this.soundChannels)) {
|
||||
return;
|
||||
}
|
||||
this.soundChannels[ch].sound.onEndCallback();
|
||||
this.soundChannels[ch].element.trigger("pause");
|
||||
}
|
||||
stop(channel) {
|
||||
let ch = channel | 0;
|
||||
if (!(ch in this.soundChannels)) {
|
||||
return;
|
||||
}
|
||||
this.soundChannels[ch].sound.onEndCallback();
|
||||
this.soundChannels[ch].element.prop("currentTime", "0");
|
||||
this.soundChannels[ch].element.trigger("pause");
|
||||
}
|
||||
loadSound(path, onready) {
|
||||
let id = this.soundCounter++ + "";
|
||||
let source = $(document.createElement("source"));
|
||||
source.attr("src", path);
|
||||
source.ready(() => { if (onready) {
|
||||
onready(id);
|
||||
} });
|
||||
source.appendTo($(this.terminal.selector));
|
||||
this.loadedSounds[id] = source;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
Jukebox.Player = Player;
|
||||
function soundGuard(o) {
|
||||
return o.soundRefrence !== undefined;
|
||||
}
|
||||
})(Jukebox || (Jukebox = {}));
|
15
terminal/js/main.js
Normal file
15
terminal/js/main.js
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* This file is an exception. It's licensed CC-0.
|
||||
*/
|
||||
var globJsterm;
|
||||
var globJash;
|
||||
var globJsx;
|
||||
function main() {
|
||||
globJsterm = JSTerminal.createTerminal(".js-terminal", 80, 20);
|
||||
globJsterm.launchProgram(new Jukebox.Player());
|
||||
globJash = new Jash.SH();
|
||||
globJsterm.launchProgram(globJash);
|
||||
globJash.setDefaultStyle("test");
|
||||
globJsterm.launchProgram(globJsx = new JavaScriptX.JSX());
|
||||
//globJsterm.launchProgram(new SoundTest());
|
||||
}
|
63
terminal/js/soundtest.js
Normal file
63
terminal/js/soundtest.js
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* This file is an exception. It's licensed CC-0.
|
||||
*/
|
||||
class SoundTest {
|
||||
create(terminal, stdio) {
|
||||
this.stdio = stdio;
|
||||
stdio.loadSound("audio/gripofnature.ogg", (ref) => {
|
||||
this.sound = {
|
||||
soundRefrence: ref,
|
||||
onStartCallback: () => {
|
||||
console.log("I started!");
|
||||
},
|
||||
onEndCallback: () => {
|
||||
console.log("I ended :(");
|
||||
}
|
||||
};
|
||||
stdio.println("Welcome to the soundtester. Simply input some of these commands to begin:");
|
||||
stdio.println("playnew 0\nplay 0\nstop 0\npause 0\nplayloop 0", "red");
|
||||
stdio.println("\n<command> <channel> <volume (0-1)>\n", "red");
|
||||
this.takeCommand();
|
||||
});
|
||||
}
|
||||
enable() {
|
||||
}
|
||||
disable() {
|
||||
}
|
||||
onClose() {
|
||||
return true;
|
||||
}
|
||||
takeCommand() {
|
||||
this.stdio.readline({
|
||||
callback: (res) => {
|
||||
let parts = res.split(" ");
|
||||
let command = parts[0];
|
||||
let channel = parseInt(parts[1]);
|
||||
let volume = parseFloat(parts[2]) || 1;
|
||||
console.log(volume);
|
||||
if (!isNaN(channel)) {
|
||||
this.sound.loops = false;
|
||||
this.sound.volume = volume;
|
||||
if (command == "play") {
|
||||
this.stdio.playSound(channel);
|
||||
}
|
||||
else if (command == "pause") {
|
||||
this.stdio.pauseSound(channel);
|
||||
}
|
||||
else if (command == "stop") {
|
||||
this.stdio.stopSound(channel);
|
||||
}
|
||||
else if (command == "playnew") {
|
||||
this.stdio.playSound(channel, this.sound);
|
||||
}
|
||||
else if (command == "playloop") {
|
||||
this.sound.loops = true;
|
||||
this.stdio.playSound(channel, this.sound);
|
||||
}
|
||||
}
|
||||
this.takeCommand();
|
||||
},
|
||||
prefix: "\n> "
|
||||
});
|
||||
}
|
||||
}
|
233
terminal/js/std.js
Normal file
233
terminal/js/std.js
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var Std;
|
||||
(function (Std) {
|
||||
class IO {
|
||||
constructor(terminal) {
|
||||
this.mousePos = { x: -1, y: -1 };
|
||||
this.listeners = {};
|
||||
this.mouseListeners = {};
|
||||
$(document).keypress(evt => {
|
||||
Object.getOwnPropertyNames(this.listeners).forEach(id => {
|
||||
this.listeners[parseInt(id)].handleKeypress(evt);
|
||||
});
|
||||
});
|
||||
$(document).keydown(evt => {
|
||||
Object.getOwnPropertyNames(this.listeners).forEach(id => {
|
||||
this.listeners[parseInt(id)].handleKeydown(evt);
|
||||
});
|
||||
});
|
||||
$(document).keyup(evt => {
|
||||
Object.getOwnPropertyNames(this.listeners).forEach(id => {
|
||||
this.listeners[parseInt(id)].handleKeyup(evt);
|
||||
});
|
||||
});
|
||||
let termElem = $(terminal.selector);
|
||||
$(terminal.selector).mousemove(evt => {
|
||||
let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
|
||||
let x = term[0];
|
||||
let y = term[1];
|
||||
let changed = x != this.mousePos.x || y != this.mousePos.y;
|
||||
if (changed) {
|
||||
let mousePos = { x: x, y: y };
|
||||
Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
|
||||
this.mouseListeners[parseInt(id)].mousemove(this.mousePos, mousePos);
|
||||
});
|
||||
this.mousePos = mousePos;
|
||||
}
|
||||
});
|
||||
$(terminal.selector).mouseleave(evt => {
|
||||
if (this.mousePos.x != -1) {
|
||||
Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
|
||||
this.mouseListeners[parseInt(id)].mousemove(this.mousePos, { x: -1, y: -1 });
|
||||
});
|
||||
this.mousePos = { x: -1, y: -1 };
|
||||
}
|
||||
});
|
||||
$(terminal.selector).mousedown(evt => {
|
||||
let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
|
||||
Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
|
||||
this.mouseListeners[parseInt(id)].mousedown(evt.button, term[0], term[1]);
|
||||
});
|
||||
});
|
||||
$(terminal.selector).mouseup(evt => {
|
||||
let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
|
||||
Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
|
||||
this.mouseListeners[parseInt(id)].mouseup(evt.button, term[0], term[1]);
|
||||
});
|
||||
});
|
||||
$(terminal.selector).click(evt => {
|
||||
let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
|
||||
Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
|
||||
this.mouseListeners[parseInt(id)].click(evt.button, term[0], term[1]);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Add a listener which then receives all key-events.
|
||||
* Returns the ID of the listener.
|
||||
*/
|
||||
addListener(listener) {
|
||||
let id = this.listenerCounter++;
|
||||
this.listeners[id] = listener;
|
||||
listener.onListen(id);
|
||||
return id;
|
||||
}
|
||||
/**
|
||||
* Remove listener with the specified ID.
|
||||
*/
|
||||
removeListener(id) {
|
||||
delete this.listeners[id];
|
||||
}
|
||||
/**
|
||||
* Add a mouse listener which then receives all mouse-events.
|
||||
* Returns the ID of the mouse listener.
|
||||
*/
|
||||
addMouseListener(listener) {
|
||||
let id = this.mouseListenerCounter++;
|
||||
this.mouseListeners[id] = listener;
|
||||
listener.onMouseListen(id);
|
||||
return id;
|
||||
}
|
||||
/**
|
||||
* Remove mouse listener with the specified ID.
|
||||
*/
|
||||
removeMouseListener(id) {
|
||||
delete this.mouseListeners[id];
|
||||
}
|
||||
/**
|
||||
* Set standard output channel.
|
||||
* Used when calling print, println, clear and refresh.
|
||||
*/
|
||||
setOutput(output) {
|
||||
this.output = output;
|
||||
}
|
||||
/**
|
||||
* Set standard input channel
|
||||
* Used when calling readline.
|
||||
*/
|
||||
setInput(input) {
|
||||
this.input = input;
|
||||
}
|
||||
/**
|
||||
* Set standard audio output channel
|
||||
* Used when calling playSound, stopSound, and loadSound
|
||||
*/
|
||||
setAudioOutput(audioOutput) {
|
||||
this.audioOutput = audioOutput;
|
||||
}
|
||||
/**
|
||||
* Calls print for the assigned IO interface.
|
||||
* Standard IO prints out a line to the cursor's last location
|
||||
*/
|
||||
print(text, style) {
|
||||
if (!this.output) {
|
||||
return;
|
||||
}
|
||||
this.output.print(text, style);
|
||||
}
|
||||
/**
|
||||
* Calls println for the assigned IO interface.
|
||||
* Standard IO prints out a line to a new line relatively to the cursor's last location.
|
||||
*/
|
||||
println(text, style) {
|
||||
if (!this.output) {
|
||||
return;
|
||||
}
|
||||
this.output.println(text, style);
|
||||
}
|
||||
/**
|
||||
* Calls refresh for the assigned IO interface.
|
||||
* Standard IO refreshes the IO buffer so it can be drawn and then draws this.
|
||||
* This last step can be avouded though with the boolean parameter.
|
||||
*/
|
||||
refresh(alsoRefreshTerminal = true) {
|
||||
if (!this.output) {
|
||||
return;
|
||||
}
|
||||
this.output.refresh(alsoRefreshTerminal);
|
||||
}
|
||||
/**
|
||||
* Calls clear for the assigned IO interface.
|
||||
* Standard IO clears the (bash and terminal) screen.
|
||||
*/
|
||||
clear(style) {
|
||||
if (!this.output) {
|
||||
return;
|
||||
}
|
||||
this.output.clear(style);
|
||||
}
|
||||
/**
|
||||
* Calls readline for the assigned IO inteface
|
||||
* Sandard IO starts taking input and calls the callback with a string it readed while printing it out.
|
||||
* Note: Some IO interfaces might not implement this method. Only use it
|
||||
* When using stdio
|
||||
*/
|
||||
readline(readline) {
|
||||
if (!this.input) {
|
||||
return;
|
||||
}
|
||||
this.input.readline(readline);
|
||||
}
|
||||
/**
|
||||
* Calls play for the assigned AudioOutput interface.
|
||||
* Jukebox (Standerd IO) starts playing a sound in the given channel. If no channel is given, channel 0.
|
||||
* Note: Some IO interfaces might not implement this method.
|
||||
*/
|
||||
playSound(channel, sound) {
|
||||
if (!this.audioOutput) {
|
||||
return;
|
||||
}
|
||||
this.audioOutput.play(channel, sound);
|
||||
}
|
||||
/**
|
||||
* Calls pause for the assigned AudioOutput interface.
|
||||
* Jukebox (Standerd IO) pauses the sound at the specified channel. Sound can be resumed with stdio.play(channel).
|
||||
* Note: Some IO interfaces might not implement this method.
|
||||
*/
|
||||
pauseSound(channel) {
|
||||
if (!this.audioOutput) {
|
||||
return;
|
||||
}
|
||||
this.audioOutput.pause(channel);
|
||||
}
|
||||
/**
|
||||
* Calls stop for the assigned AudioOutput interface.
|
||||
* Jukebox (Standerd IO) stops the current sound at given channel. If no channel is given, channel 0.
|
||||
* Note: Some IO interfaces might not implement this method.
|
||||
*/
|
||||
stopSound(channel) {
|
||||
if (!this.audioOutput) {
|
||||
return;
|
||||
}
|
||||
this.audioOutput.stop(channel);
|
||||
}
|
||||
/**
|
||||
* Calls stop for the assigned AudioOutput interface.
|
||||
* Jukebox (Standerd IO) loads the given sound and returns a refrence string, which points to a loaded sound.
|
||||
* Note: Some IO interfaces might not implement this method. If AudioOutput-interface not specified (or Jukebox launched), it will return an empty string.
|
||||
*/
|
||||
loadSound(path, onready) {
|
||||
if (!this.audioOutput) {
|
||||
return "";
|
||||
}
|
||||
return this.audioOutput.loadSound(path, onready);
|
||||
}
|
||||
}
|
||||
Std.IO = IO;
|
||||
function translatePageToTerminal(termElem, terminal, x, y) {
|
||||
let cellW = termElem.outerWidth() / terminal.width; // Portional width of a single cell;
|
||||
let cellH = termElem.outerHeight() / terminal.height; // Portional height of a single cell;
|
||||
let termX = Math.floor((x - termElem.offset().left) / cellW);
|
||||
let termY = Math.floor((y - termElem.offset().top) / cellH);
|
||||
return [termX, termY];
|
||||
}
|
||||
})(Std || (Std = {}));
|
48
terminal/js/testprogram.js
Normal file
48
terminal/js/testprogram.js
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* This file is an exception. It's licensed CC-0.
|
||||
*/
|
||||
class TestProgram {
|
||||
create(terminal, stdio) {
|
||||
let ref = stdio.loadSound("audio/battle_music.ogg", (ref) => {
|
||||
let sound = {
|
||||
soundRefrence: ref,
|
||||
onStartCallback: () => {
|
||||
console.log("I started!");
|
||||
},
|
||||
onEndCallback: () => {
|
||||
console.log("I ended :(");
|
||||
}
|
||||
};
|
||||
stdio.playSound(0, sound);
|
||||
});
|
||||
stdio.println("Welcome to a tour! This is a tour for JSh, a platform for the upcoming JSX!");
|
||||
stdio.println("\nFirst things first:\nWho are you?");
|
||||
stdio.refresh();
|
||||
stdio.readline({
|
||||
prefix: "\n> ",
|
||||
callback: (resp) => {
|
||||
console.log("got: " + resp);
|
||||
stdio.println("Hello, '");
|
||||
stdio.print(resp, "red");
|
||||
stdio.print("', I am JSh, the JavaScript Bash.");
|
||||
stdio.println("I am but a mere tool that can print and read stuff from the terminal");
|
||||
stdio.stopSound(0);
|
||||
terminal.closeProgram(this);
|
||||
},
|
||||
printAfterDone: true,
|
||||
style: "red",
|
||||
hidden: false,
|
||||
onChangeCallback: (curr) => {
|
||||
console.log(curr);
|
||||
},
|
||||
default: "heh"
|
||||
});
|
||||
}
|
||||
enable() {
|
||||
}
|
||||
disable() {
|
||||
}
|
||||
onClose() {
|
||||
return true;
|
||||
}
|
||||
}
|
13
terminal/tsconfig.json
Normal file
13
terminal/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "ts/",
|
||||
"outDir": "js/",
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user