diff --git a/data/scripts/Terminal.js b/data/scripts/Terminal.js new file mode 100644 index 0000000..87a35dc --- /dev/null +++ b/data/scripts/Terminal.js @@ -0,0 +1,560 @@ +const TERM_X_MIN = 60; +const TERM_Y_MIN = 10; +// the following 2 values are empirical determined in chromium +const TERM_CELL_HEIGHT = 24; +const TERM_CELL_WIDTH = 10; + +const ANSI_NO = 0; +const ANSI_ESC = 1; +const ANSI_CSI1 = 2; +const ANSI_CSI2 = 3; +const ANSI_CSI3 = 4; +const ANSI_CUU = 5; +const ANSI_CUD = 6; +const ANSI_CUF = 7; +const ANSI_CUB = 8; +const ANSI_CNL = 9; +const ANSI_CPL = 10; +const ANSI_CHA = 11; +const ANSI_CUP = 12; +const ANSI_ED = 13; +const ANSI_EL = 14; +const ANSI_SCP = 15; +const ANSI_RCP = 16; +const ANSI_DECTCEM = 17; + +var Terminal = function() { +} +Terminal.prototype.xSize; +Terminal.prototype.ySize; +Terminal.prototype.xPosition = 0; +Terminal.prototype.yPosition = 0; +Terminal.prototype.xPositionSaved = 0; +Terminal.prototype.yPositionSaved = 0; +Terminal.prototype.tickId; + +Terminal.prototype.staticNoShift = 4; +Terminal.prototype.color = "white"; +Terminal.prototype.backgroundColor = "black"; +Terminal.prototype.bold = false; +Terminal.prototype.displayCursor = true; + +Terminal.prototype.calculateSize = function(width, height) { + this.xSize = parseInt(width / (TERM_CELL_WIDTH)); + if (this.xSize < TERM_X_MIN) + this.xSize = TERM_X_MIN; + this.ySize = parseInt(height / (TERM_CELL_HEIGHT)); + if (this.ySize < TERM_Y_MIN) + this.ySize = TERM_Y_MIN; + + console.log("Terminal resolution is: " + this.xSize + "x" + this.ySize); +} +Terminal.prototype.getBasicHTML = function() { + var code = ""; + code += ""; + for (var i = 0; i < this.ySize; i++) { + code += ""; + for (var j = 0; j < this.xSize; j++) + code += "" + code += ""; + } + code += "
"; + code += this.getInputHTML(); + return code; +} +Terminal.prototype.getInputHTML = function() { + var code = "\ +
\ + \ +
"; + return code; +} +Terminal.prototype.init = function() { + window.onkeypress = this.handleKeyPress; + window.onkeydown = this.handleKeyDown; + this.tickId = window.setInterval(this.tick, 10); + document.getElementById("input").focus(); + this.cursorOn(); +} +Terminal.prototype.tick = function() { + +} +Terminal.prototype.globalLineFeed = function() { + for (var i = this.staticNoShift; i < this.ySize; i++) { + for (var j = 0; j < this.xSize; j++) { + var to = document.getElementsByClassName((i - 1) + "c" + j)[0]; + var from = document.getElementsByClassName(i + "c" + j)[0]; + to.innerHTML = form.innerHTML; + to.style.color = from.style.color; + to.style.fontWeight = from.style.fontWeight; + to.style.backgroundColor = from.style.backgroundColor; + } + } + for (var j = 0; j < this.xSize; j++) { + var to = document.getElementsByClassName((this.ySize - 1) + "c" + j)[0]; + to.innerHTML = " "; + to.style.color = this.color; + to.style.backgroundColor = this.backgroundColor; + if (this.bold) { + to.style.fontWeight = "bold"; + } else { + to.style.fontWeight = "normal"; + } + } + if (--this.yPosition < 0) + this.yPosition = 0; +} +Terminal.prototype.cursorOff = function() { + var cell = document.getElementById("cursor"); + if (cell != undefined) { + cell.style.borderColor = this.backgroundColor; + cell.id = undefined; + } +} +Terminal.prototype.cursorOn = function() { + var cell = document.getElementsByClassName(this.yPosition + "c" + this.xPosition)[0]; + if (this.displayCursor) { + cell.style.borderColor = this.color; + cell.id = "cursor"; + } +} + +Terminal.prototype.normalOutputChar = function(char) { + this.cursorOff(); + if (char == "\n") { + if (++this.yPosition >= this.ySize) + this.globalLineFeed(); + this.xPosition = 0; + } else { + var cell = document.getElementsByClassName(this.yPosition + "c" + this.xPosition++)[0]; + cell.innerHTML = char; + if (this.bold) + cell.style.fontWeight = "bold"; + else + cell.style.fontWeight = "normal"; + cell.style.color = this.color; + cell.style.backgroundColor = this.backgroundColor; + cell.style.boderColor = this.backgroundColor; + + if (this.xPosition >= this.xSize) { + this.xPosition = 0; + this.yPosition++; + } + if (this.yPosition >= this.ySize) { + this.globalLineFeed(); + } + } + this.cursorOn(); +} +Terminal.prototype.normalOutput = function(text) { + for (var i = 0; i < text.length; i++) { + this.normalOutputChar(text.charAt(i)); + } +} +Terminal.prototype.output = function(text) { + var tmp = ""; + var param1 = ""; + var param2 = ""; + var state = ANSI_NO; + for (var i = 0; i < text.length; i++) { + switch(state) { + case ANSI_NO: // normal text + if (text[i] == "\033") + state = ANSI_ESC; // maybe a ANSI-sequence + else + this.normalOutputChar(text.charAt(i)); + break; + case ANSI_ESC: + if (text[i] == "[") + state = ANSI_CSI1; // maybe a CSI-sequence + else { + this.normalOutputChar("\033"); + state = ANSI_NO; + } + break; + case ANSI_CSI1: + if (isNumber(text[i])) { + param1 += text[i]; + break; + } + if (param1.length > 0 && text[i] == ";") { + tmp = ";" + state = ANSI_CSI2; + break; + } + if (param1.length > 0) { + state = ANSI_CSI3; + break; + } + if (text[i] == "?") { + state = ANSI_DECTCEM; + break; + } + if (text[i] == "s") { + state = ANSI_SCP; + i--; + break; + } + if (text[i] == "u") { + state = ANSI_RCP; + i--; + break; + } + if (text[i] == "J") { + state = ANSI_ED; + i--; + if (param1.length == 0) + param1 = "0"; + break; + } + this.normalOutput("\033[" + param1 + text[i]) + param1 = ""; + state = ANSI_NO; + break; + case ANSI_CSI2: + if (isNumber(text[i])) { + param2 += text[i]; + break; + } + if (param2.length > 0) { + state = ANSI_CSI3; + break; + } + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + tmp = ""; + param2 = ""; + state = ANSI_NO; + break; + case ANSI_CSI3: + switch(text[i--]) { + case "A": + state = ANSI_CUU; + break; + case "B": + state = ANSI_CUD; + break; + case "C": + state = ANSI_CUF; + break; + case "D": + state = ANSI_CUB; + break; + case "E": + state = ANSI_CNL; + break; + case "F": + state = ANSI_CPL; + break; + case "G": + state = ANSI_CHA; + break; + case "H": + state = ANSI_CUP; + break; + case "J": + state = ANSI_ED; + break; + case "K": + state = ANSI_EL; + break; + default: + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[++i]); + param1 = ""; + param2 = ""; + tmp = ""; + } + break; + case ANSI_CUU: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.yPosition -= parseInt(param1); + if (this.yPosition < 0) + this.yPosition = 0; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CUD: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.yPosition += parseInt(param1); + if (this.yPosition >= this.ySize) + this.yPosition = this.ySize - 1; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CUF: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition += parseInt(param1); + if (this.xPosition >= this.xSize) + this.xPosition = this.xSize - 1; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CUB: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition -= parseInt(param1); + if (this.xPosition < 0) + this.xPosition = 0; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CNL: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition = 0; + this.yPosition += parseInt(param1); + if (this.yPosition >= this.ySize) + this.yPosition = this.ySize - 1; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CPL: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition = 0; + this.yPosition -= parseInt(param1); + if (this.yPosition < 0) + this.yPosition = 0; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CHA: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition = parseInt(param1) - 1; + if (this.xPosition >= this.xSize) + this.xPosition = this.xSize - 1; + if (this.xPosition < 0) + this.xPosition = 0; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_CUP: + if (tmp == "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + this.cursorOff(); + this.xPosition = parseInt(param2) - 1; + if (this.xPosition >= this.xSize) + this.xPosition = this.xSize - 1; + if (this.xPosition < 0) + this.xPosition = 0; + this.yPosition = parseInt(param1) - 1; + if (this.yPosition >= this.ySize) + this.yPosition = this.ySize - 1; + if (this.yPosition < 0) + this.yPosition = 0; + this.cursorOn(); + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_ED: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + switch (parseInt(param1)) { + case 0: + var x = this.xPosition; + var y = this.yPosition; + var string = ""; + for (var i = 0; i < this.xSize * (this.ySize - y - 1) + this.xSize - x; i++) + string += " "; + this.normalOutput(string); + this.xPosition = x; + this.yPosition = y; + break; + case 1: + var x = this.xPosition; + var y = this.yPosition; + var string = ""; + for (var i = 0; i < this.xSize * (y - 1) + x; i++) + string += " "; + this.xPosition = 0; + this.yPosition = 0; + this.normalOutput(string); + this.xPosition = x; + this.yPosition = y; + break; + case 2: + var x = this.xPosition; + var y = this.yPosition; + var string = ""; + for (var i = 0; i < this.xSize * this.ySize; i++) + string += " "; + this.xPosition = 0; + this.yPosition = 0; + this.normalOutput(string); + this.xPosition = x; + this.yPosition = y; + break; + default: + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + } + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_EL: + if (tmp != "") { + state = ANSI_NO; + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + param1 = ""; + param2 = ""; + tmp = ""; + break; + } + switch (parseInt(param1)) { + case 0: + var x = this.xPosition; + var y = this.yPosition; + var string = ""; + for (var i = 0; i < this.xSize - x; i++) + string += " "; + this.normalOutput(string); + this.xPosition = x; + this.yPosition = y; + break; + case 1: + var x = this.xPosition; + var string = ""; + for (var i = 0; i < x; i++) + string += " "; + this.xPosition = 0; + this.normalOutput(string); + this.xPosition = x; + break; + case 2: + var x = this.xPosition; + var y = this.yPosition; + var string = ""; + for (var i = 0; i < this.xSize; i++) + string += " "; + this.xPosition = 0; + this.normalOutput(string); + this.xPosition = x; + this.yPosition = y; + break; + default: + this.normalOutput("\033[" + param1 + tmp + param2 + text[i]); + } + state = ANSI_NO; + param1 = ""; + param2 = ""; + tmp = ""; + break; + case ANSI_SCP: + state = ANSI_NO; + this.xPositionSaved = this.xPosition; + this.yPositionSaved = this.yPosition; + break; + case ANSI_RCP: + state = ANSI_NO; + this.xPosition = this.xPositionSaved; + this.yPosition = this.yPositionSaved; + break; + case ANSI_DECTCEM: + if (tmp.length < 3) + break; + if (tmp == "25l") { + this.displayCursor = false; + tmp = ""; + state = ANSI_NO; + } + if (tmp == "25h") { + this.displayCursor = true; + tmp = ""; + state = ANSI_NO; + } + break; + } + } + if (tmp.length || param1.length || param2.length) + this.normalOutput("\033[" + param1 + tmp + param2); +}