Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
diff --git a/browser/index.html b/browser/index.html new file mode 100644 index 0000000..37e10c4 --- /dev/null +++ b/browser/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<title>live term test</title> +</head> +<body> +<iframe src="liveterm.html" width="600" height="400" style="border: none;"></iframe> +</body> +</html> diff --git a/browser/liveterm.html b/browser/liveterm.html new file mode 100644 index 0000000..150ee77 --- /dev/null +++ b/browser/liveterm.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<title>liveterm</title> +<link href="./style.css" rel="stylesheet" type="text/css"> +<script src="./socket.io/socket.io.js"></script> +<script src="./term.js"></script> +<script src="./main.js"></script> +</head> +<body> + +</body> +</html> diff --git a/browser/main.js b/browser/main.js new file mode 100644 index 0000000..cc36f55 --- /dev/null +++ b/browser/main.js @@ -0,0 +1,20 @@ +var noop = function () { /* do nothing */ }; + +var socket = io.connect(); + +var term = new Terminal(80, 30, noop); + +socket.on('connect', function () { + term.open(); + socket.emit('create'); +}); + +socket.on('input', function (data) { + term.write(data); +}); + +socket.on('snapshot', function (data) { + for (var prop in data) { + term[prop] = data[prop]; + } +}); \ No newline at end of file diff --git a/browser/style.css b/browser/style.css new file mode 100644 index 0000000..a25d596 --- /dev/null +++ b/browser/style.css @@ -0,0 +1,31 @@ +/** + * style.css (https://github.com/chjj/tty.js) + * Copyright (c) 2012, Christopher Jeffrey (MIT License) + */ + +html, body { + margin: 0; + padding: 0; +} + +div { + border: 0; + padding: 0; + margin: 0; +} + +html { + background: #fff; +} + +.term { + font-family: "DejaVu Sans Mono", monospace; + font-size: 11px; + color: #f0f0f0; + background: #000; +} + +.termReverse { + color: #000; + background: #f0f0f0; +} \ No newline at end of file diff --git a/browser/term.js b/browser/term.js new file mode 100644 index 0000000..7423080 --- /dev/null +++ b/browser/term.js @@ -0,0 +1,2950 @@ +/** + * tty.js - an xterm emulator + * Christopher Jeffrey (https://github.com/chjj/tty.js) + * + * Originally forked from (with the author's permission): + * + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * (Redistribution or commercial use is prohibited + * without the author's permission.) + * + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. +*/ + +;(function() { + +/** + * Terminal Emulation References: + * http://vt100.net/ + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * http://invisible-island.net/vttest/ + * http://www.inwap.com/pdp10/ansicode.txt + * http://linux.die.net/man/4/console_codes + * http://linux.die.net/man/7/urxvt + */ + +'use strict'; + +/** + * States + */ + +var normal = 0 + , escaped = 1 + , csi = 2 + , osc = 3 + , charset = 4; + +/** + * Terminal + */ + +var Terminal = function(cols, rows, handler) { + this.cols = cols; + this.rows = rows; + this.handler = handler; + this.scrollback = 1000; + this.ybase = 0; + this.ydisp = 0; + this.x = 0; + this.y = 0; + this.cursorState = 0; + this.cursorHidden = false; + this.convertEol = false; + this.state = 0; + this.outputQueue = ''; + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + + this.applicationKeypad = false; + this.originMode = false; + this.insertMode = false; + this.wraparoundMode = false; + this.mouseEvents; + this.tabs = []; + this.charset = null; + this.normal = null; + + this.bgColors = [ + '#2e3436', + '#cc0000', + '#4e9a06', + '#c4a000', + '#3465a4', + '#75507b', + '#06989a', + '#d3d7cf' + ]; + + this.fgColors = [ + '#555753', + '#ef2929', + '#8ae234', + '#fce94f', + '#729fcf', + '#ad7fa8', + '#34e2e2', + '#eeeeec' + ]; + + this.defAttr = (7 << 3) | 0; + this.curAttr = this.defAttr; + this.isMac = ~navigator.userAgent.indexOf('Mac'); + this.keyState = 0; + this.keyStr = ''; + + this.params = []; + this.currentParam = 0; + + var i = this.rows - 1; + this.lines = [ this.blankLine() ]; + while (i--) { + this.lines.push(this.lines[0].slice()); + } +}; + +/** + * Open Terminal + */ + +Terminal.prototype.open = function() { + var self = this + , i = 0 + , div; + + this.element = document.createElement('div'); + this.element.className = 'terminal'; + this.children = []; + + for (; i < this.rows; i++) { + div = document.createElement('div'); + div.className = 'term'; + this.element.appendChild(div); + this.children.push(div); + } + + document.body.appendChild(this.element); + + this.refresh(0, this.rows - 1); + + setInterval(function() { + self.cursorBlink(); + }, 500); +}; + +Terminal.prototype.refresh = function(start, end) { + var element + , x + , y + , i + , line + , out + , ch + , width + , data + , defAttr + , fgColor + , bgColor + , row; + + for (y = start; y <= end; y++) { + row = y + this.ydisp; + + line = this.lines[row]; + out = ''; + width = this.cols; + + if (y === this.y + && this.cursorState + && this.ydisp === this.ybase) { + x = this.x; + } else { + x = -1; + } + + defAttr = this.defAttr; + + for (i = 0; i < width; i++) { + ch = line[i]; + data = ch >> 16; + ch &= 0xffff; + if (i === x) { + data = -1; + } + + if (data !== defAttr) { + if (defAttr !== this.defAttr) + out += '</span>'; + if (data !== this.defAttr) { + if (data === -1) { + out += '<span class="termReverse">'; + } else { + out += '<span style="'; + fgColor = (data >> 3) & 7; + bgColor = data & 7; + if (fgColor !== 7) { + out += 'color:' + + this.fgColors[fgColor] + + ';'; + } + if (bgColor !== 0) { + out += 'background-color:' + + this.bgColors[bgColor] + + ';'; + } + if ((data >> 8) & 1) { + out += 'font-weight:bold;'; + } + if ((data >> 8) & 4) { + out += 'text-decoration:underline;'; + } + out += '">'; + } + } + } + + switch (ch) { + case 32: + out += ' '; + break; + case 38: + out += '&'; + break; + case 60: + out += '<'; + break; + case 62: + out += '>'; + break; + default: + if (ch < 32) { + out += ' '; + } else { + out += String.fromCharCode(ch); + } + break; + } + + defAttr = data; + } + + if (defAttr !== this.defAttr) { + out += '</span>'; + } + + element = this.children[y]; + element.innerHTML = out; + } +}; + +Terminal.prototype.cursorBlink = function() { + this.cursorState ^= 1; + this.refresh(this.y, this.y); +}; + +Terminal.prototype.showCursor = function() { + if (!this.cursorState) { + this.cursorState = 1; + this.refresh(this.y, this.y); + } +}; + +Terminal.prototype.scroll = function() { + var row; + + // maybe check this.lines.length ? + if (++this.ybase === this.scrollback) { + this.ybase = 0; + this.ydisp = 0; // always reset disp to zero + this.lines = this.lines.slice(-this.rows + 1); + } + + // if (this.scrollTtyOutput) + this.ydisp = this.ybase; + + // last line + row = this.ybase + this.rows - 1; + + // subtract the bottom scroll region + row -= this.rows - 1 - this.scrollBottom; + + // add our new line + this.lines.splice(row, 0, this.blankLine()); + + if (this.scrollTop !== 0) { + if (this.ybase !== 0) { + this.ybase--; + this.ydisp = this.ybase; + } + this.lines.splice(this.ybase + this.scrollTop, 1); + } +}; + +Terminal.prototype.scrollDisp = function(disp) { + this.ydisp += disp; + + if (this.ydisp > this.ybase) { + this.ydisp = this.ybase; + } else if (this.ydisp < 0) { + this.ydisp = 0; + } + + this.refresh(0, this.rows - 1); +}; + +Terminal.prototype.write = function(str) { + // console.log(JSON.stringify(str.replace(/\x1b/g, '^['))); + + var l = str.length + , i = 0 + , ch + , param + , row; + + this.refreshStart = this.rows; + this.refreshEnd = -1; + this.getRows(this.y); + + if (this.ybase !== this.ydisp) { + this.ydisp = this.ybase; + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + } + + for (; i < l; i++) { + ch = str.charCodeAt(i); + switch (this.state) { + case normal: + switch (ch) { + // '\0' + case 0: + break; + + // '\a' + case 7: + this.bell(); + break; + + // '\n', '\v', '\f' + case 10: + case 11: + case 12: + if (this.convertEol) { + this.x = 0; + } + this.y++; + if (this.y >= this.scrollBottom + 1) { + this.y--; + this.scroll(); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + } + break; + + // '\r' + case 13: + this.x = 0; + break; + + // '\b' + case 8: + if (this.x > 0) { + this.x--; + } + break; + + // '\t' + case 9: + // should check tabstops + param = (this.x + 8) & ~7; + if (param <= this.cols) { + this.x = param; + } + break; + + // '\e' + case 27: + this.state = escaped; + break; + + default: + // ' ' + if (ch >= 32) { + if (this.charset && this.charset[ch]) { + ch = this.charset[ch]; + } + if (this.x >= this.cols) { + this.x = 0; + this.y++; + if (this.y >= this.scrollBottom + 1) { + this.y--; + this.scroll(); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + } + } + row = this.y + this.ybase; + this.lines[row][this.x] = (ch & 0xffff) | (this.curAttr << 16); + this.x++; + this.getRows(this.y); + } + break; + } + break; + case escaped: + switch (str[i]) { + // ESC [ Control Sequence Introducer ( CSI is 0x9b). + case '[': + this.params = []; + this.currentParam = 0; + this.state = csi; + break; + + // ESC ] Operating System Command ( OSC is 0x9d). + case ']': + this.params = []; + this.currentParam = 0; + this.state = osc; + break; + + // ESC P Device Control String ( DCS is 0x90). + case 'P': + this.state = osc; + break; + + // ESC _ Application Program Command ( APC is 0x9f). + case '_': + this.state = osc; + break; + + // ESC ^ Privacy Message ( PM is 0x9e). + case '^': + this.state = osc; + break; + + // ESC c Full Reset (RIS). + case 'c': + this.reset(); + break; + + // ESC E Next Line ( NEL is 0x85). + // ESC D Index ( IND is 0x84). + case 'E': + this.x = 0; + ; + case 'D': + this.index(); + break; + + // ESC M Reverse Index ( RI is 0x8d). + case 'M': + this.reverseIndex(); + break; + + // ESC % Select default/utf-8 character set. + // @ = default, G = utf-8 + case '%': + this.charset = null; + this.state = normal; + i++; + break; + + // ESC (,),*,+,-,. Designate G0-G2 Character Set. + case '(': // <-- this seems to get all the attention + case ')': + case '*': + case '+': + case '-': + case '.': + this.state = charset; + break; + + // Designate G3 Character Set (VT300). + // A = ISO Latin-1 Supplemental. + // Not implemented. + case '/': + this.charset = null; + this.state = normal; + i++; + break; + + // ESC 7 Save Cursor (DECSC). + case '7': + this.saveCursor(); + this.state = normal; + break; + + // ESC 8 Restore Cursor (DECRC). + case '8': + this.restoreCursor(); + this.state = normal; + break; + + // ESC # 3 DEC line height/width + case '#': + this.state = normal; + i++; + break; + + // ESC H Tab Set ( HTS is 0x88). + case 'H': + // this.tabSet(this.x); + this.state = normal; + break; + + // ESC = Application Keypad (DECPAM). + case '=': + console.log('Serial port requested application keypad.'); + this.applicationKeypad = true; + this.state = normal; + break; + + // ESC > Normal Keypad (DECPNM). + case '>': + console.log('Switching back to normal keypad.'); + this.applicationKeypad = false; + this.state = normal; + break; + + default: + this.state = normal; + console.log('Unknown ESC control: ' + str[i] + '.'); + break; + } + break; + + case charset: + switch (str[i]) { + // DEC Special Character and Line Drawing Set. + case '0': + this.charset = SCLD; + break; + // United States (USASCII). + case 'B': + default: + this.charset = null; + break; + } + this.state = normal; + break; + + case osc: + if (ch !== 27 && ch !== 7) break; + console.log('Unknown OSC code.'); + this.state = normal; + // increment for the trailing slash in ST + if (ch === 27) i++; + break; + + case csi: + // '?', '>', '!' + if (ch === 63 || ch === 62 || ch === 33) { + this.prefix = str[i]; + break; + } + + // 0 - 9 + if (ch >= 48 && ch <= 57) { + this.currentParam = this.currentParam * 10 + ch - 48; + break; + } + + // '$', '"', ' ', '\'' + if (ch === 36 || ch === 34 || ch === 32 || ch === 39) { + this.postfix = str[i]; + break; + } + + this.params[this.params.length] = this.currentParam; + this.currentParam = 0; + + // ';' + if (ch === 59) break; + + this.state = normal; + + switch (ch) { + // CSI Ps A + // Cursor Up Ps Times (default = 1) (CUU). + case 65: + this.cursorUp(this.params); + break; + + // CSI Ps B + // Cursor Down Ps Times (default = 1) (CUD). + case 66: + this.cursorDown(this.params); + break; + + // CSI Ps C + // Cursor Forward Ps Times (default = 1) (CUF). + case 67: + this.cursorForward(this.params); + break; + + // CSI Ps D + // Cursor Backward Ps Times (default = 1) (CUB). + case 68: + this.cursorBackward(this.params); + break; + + // CSI Ps ; Ps H + // Cursor Position [row;column] (default = [1,1]) (CUP). + case 72: + this.cursorPos(this.params); + break; + + // CSI Ps J Erase in Display (ED). + case 74: + this.eraseInDisplay(this.params); + break; + + // CSI Ps K Erase in Line (EL). + case 75: + this.eraseInLine(this.params); + break; + + // CSI Pm m Character Attributes (SGR). + case 109: + this.charAttributes(this.params); + break; + + // CSI Ps n Device Status Report (DSR). + case 110: + this.deviceStatus(this.params); + break; + + /** + * Additions + */ + + // CSI Ps @ + // Insert Ps (Blank) Character(s) (default = 1) (ICH). + case 64: + this.insertChars(this.params); + break; + + // CSI Ps E + // Cursor Next Line Ps Times (default = 1) (CNL). + case 69: + this.cursorNextLine(this.params); + break; + + // CSI Ps F + // Cursor Preceding Line Ps Times (default = 1) (CNL). + case 70: + this.cursorPrecedingLine(this.params); + break; + + // CSI Ps G + // Cursor Character Absolute [column] (default = [row,1]) (CHA). + case 71: + this.cursorCharAbsolute(this.params); + break; + + // CSI Ps L + // Insert Ps Line(s) (default = 1) (IL). + case 76: + this.insertLines(this.params); + break; + + // CSI Ps M + // Delete Ps Line(s) (default = 1) (DL). + case 77: + this.deleteLines(this.params); + break; + + // CSI Ps P + // Delete Ps Character(s) (default = 1) (DCH). + case 80: + this.deleteChars(this.params); + break; + + // CSI Ps X + // Erase Ps Character(s) (default = 1) (ECH). + case 88: + this.eraseChars(this.params); + break; + + // CSI Pm ` Character Position Absolute + // [column] (default = [row,1]) (HPA). + case 96: + this.charPosAbsolute(this.params); + break; + + // 141 61 a * HPR - + // Horizontal Position Relative + case 97: + this.HPositionRelative(this.params); + break; + + // CSI P s c + // Send Device Attributes (Primary DA). + // CSI > P s c + // Send Device Attributes (Secondary DA) + case 99: + this.sendDeviceAttributes(this.params); + break; + + // CSI Pm d + // Line Position Absolute [row] (default = [1,column]) (VPA). + case 100: + this.linePosAbsolute(this.params); + break; + + // 145 65 e * VPR - Vertical Position Relative + case 101: + this.VPositionRelative(this.params); + break; + + // CSI Ps ; Ps f + // Horizontal and Vertical Position [row;column] (default = + // [1,1]) (HVP). + case 102: + this.HVPosition(this.params); + break; + + // CSI Pm h Set Mode (SM). + // CSI ? Pm h - mouse escape codes, cursor escape codes + case 104: + this.setMode(this.params); + break; + + // CSI Pm l Reset Mode (RM). + // CSI ? Pm l + case 108: + this.resetMode(this.params); + break; + + // CSI Ps ; Ps r + // Set Scrolling Region [top;bottom] (default = full size of win- + // dow) (DECSTBM). + // CSI ? Pm r + case 114: + this.setScrollRegion(this.params); + break; + + // CSI s Save cursor (ANSI.SYS). + case 115: + this.saveCursor(this.params); + break; + + // CSI u Restore cursor (ANSI.SYS). + case 117: + this.restoreCursor(this.params); + break; + + /** + * Lesser Used + */ + + // CSI Ps I + // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). + case 73: + this.cursorForwardTab(this.params); + break; + + // CSI Ps S Scroll up Ps lines (default = 1) (SU). + case 83: + this.scrollUp(this.params); + break; + + // CSI Ps T Scroll down Ps lines (default = 1) (SD). + // CSI Ps ; Ps ; Ps ; Ps ; Ps T + // CSI > Ps; Ps T + case 84: + // if (this.prefix === '>') { + // this.resetTitleModes(this.params); + // break; + // } + // if (this.params.length > 1) { + // this.initMouseTracking(this.params); + // break; + // } + this.scrollDown(this.params); + break; + + // CSI Ps Z + // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + case 90: + this.cursorBackwardTab(this.params); + break; + + // CSI Ps b Repeat the preceding graphic character Ps times (REP). + case 98: + this.repeatPrecedingCharacter(this.params); + break; + + // CSI Ps g Tab Clear (TBC). + // case 103: + // this.tabClear(this.params); + // break; + + // CSI Pm i Media Copy (MC). + // CSI ? Pm i + // case 105: + // this.mediaCopy(this.params); + // break; + + // CSI Pm m Character Attributes (SGR). + // CSI > Ps; Ps m + // case 109: // duplicate + // if (this.prefix === '>') { + // this.setResources(this.params); + // } else { + // this.charAttributes(this.params); + // } + // break; + + // CSI Ps n Device Status Report (DSR). + // CSI > Ps n + // case 110: // duplicate + // if (this.prefix === '>') { + // this.disableModifiers(this.params); + // } else { + // this.deviceStatus(this.params); + // } + // break; + + // CSI > Ps p Set pointer mode. + // CSI ! p Soft terminal reset (DECSTR). + // CSI Ps$ p + // Request ANSI mode (DECRQM). + // CSI ? Ps$ p + // Request DEC private mode (DECRQM). + // CSI Ps ; Ps " p + case 112: + switch (this.prefix) { + // case '>': + // this.setPointerMode(this.params); + // break; + case '!': + this.softReset(this.params); + break; + // case '?': + // if (this.postfix === '$') { + // this.requestPrivateMode(this.params); + // } + // break; + // default: + // if (this.postfix === '"') { + // this.setConformanceLevel(this.params); + // } else if (this.postfix === '$') { + // this.requestAnsiMode(this.params); + // } + // break; + } + break; + + // CSI Ps q Load LEDs (DECLL). + // CSI Ps SP q + // CSI Ps " q + // case 113: + // if (this.postfix === ' ') { + // this.setCursorStyle(this.params); + // break; + // } + // if (this.postfix === '"') { + // this.setCharProtectionAttr(this.params); + // break; + // } + // this.loadLEDs(this.params); + // break; + + // CSI Ps ; Ps r + // Set Scrolling Region [top;bottom] (default = full size of win- + // dow) (DECSTBM). + // CSI ? Pm r + // CSI Pt; Pl; Pb; Pr; Ps$ r + // case 114: // duplicate + // if (this.prefix === '?') { + // this.restorePrivateValues(this.params); + // } else if (this.postfix === '$') { + // this.setAttrInRectangle(this.params); + // } else { + // this.setScrollRegion(this.params); + // } + // break; + + // CSI s Save cursor (ANSI.SYS). + // CSI ? Pm s + // case 115: // duplicate + // if (this.prefix === '?') { + // this.savePrivateValues(this.params); + // } else { + // this.saveCursor(this.params); + // } + // break; + + // CSI Ps ; Ps ; Ps t + // CSI Pt; Pl; Pb; Pr; Ps$ t + // CSI > Ps; Ps t + // CSI Ps SP t + // case 116: + // if (this.postfix === '$') { + // this.reverseAttrInRectangle(this.params); + // } else if (this.postfix === ' ') { + // this.setWarningBellVolume(this.params); + // } else { + // if (this.prefix === '>') { + // this.setTitleModeFeature(this.params); + // } else { + // this.manipulateWindow(this.params); + // } + // } + // break; + + // CSI u Restore cursor (ANSI.SYS). + // CSI Ps SP u + // case 117: // duplicate + // if (this.postfix === ' ') { + // this.setMarginBellVolume(this.params); + // } else { + // this.restoreCursor(this.params); + // } + // break; + + // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v + // case 118: + // if (this.postfix === '$') { + // this.copyRectagle(this.params); + // } + // break; + + // CSI Pt ; Pl ; Pb ; Pr ' w + // case 119: + // if (this.postfix === '\'') { + // this.enableFilterRectangle(this.params); + // } + // break; + + // CSI Ps x Request Terminal Parameters (DECREQTPARM). + // CSI Ps x Select Attribute Change Extent (DECSACE). + // CSI Pc; Pt; Pl; Pb; Pr$ x + // case 120: + // if (this.postfix === '$') { + // this.fillRectangle(this.params); + // } else { + // this.requestParameters(this.params); + // //this.__(this.params); + // } + // break; + + // CSI Ps ; Pu ' z + // CSI Pt; Pl; Pb; Pr$ z + // case 122: + // if (this.postfix === '\'') { + // this.enableLocatorReporting(this.params); + // } else if (this.postfix === '$') { + // this.eraseRectangle(this.params); + // } + // break; + + // CSI Pm ' { + // CSI Pt; Pl; Pb; Pr$ { + // case 123: + // if (this.postfix === '\'') { + // this.setLocatorEvents(this.params); + // } else if (this.postfix === '$') { + // this.selectiveEraseRectangle(this.params); + // } + // break; + + // CSI Ps ' | + // case 124: + // if (this.postfix === '\'') { + // this.requestLocatorPosition(this.params); + // } + // break; + + // CSI P m SP } + // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. + // case 125: + // if (this.postfix === ' ') { + // this.insertColumns(this.params); + // } + // break; + + // CSI P m SP ~ + // Delete P s Column(s) (default = 1) (DECDC), VT420 and up + // case 126: + // if (this.postfix === ' ') { + // this.deleteColumns(this.params); + // } + // break; + + default: + console.log( + 'Unknown CSI code: %s', + str[i], this.params); + break; + } + + this.prefix = ''; + this.postfix = ''; + break; + } + } + + this.getRows(this.y); + + if (this.refreshEnd >= this.refreshStart) { + this.refresh(this.refreshStart, this.refreshEnd); + } +}; + +Terminal.prototype.writeln = function(str) { + this.write(str + '\r\n'); +}; + +Terminal.prototype.keyDownHandler = function(ev) { + var str = ''; + switch (ev.keyCode) { + // backspace + case 8: + str = '\x7f'; // ^? + //str = '\x08'; // ^H + break; + // tab + case 9: + str = '\t'; + break; + // return/enter + case 13: + str = '\r'; + break; + // escape + case 27: + str = '\x1b'; + break; + // left-arrow + case 37: + if (this.applicationKeypad) { + str = '\x1bOD'; // SS3 as ^O for 7-bit + //str = '\x8fD'; // SS3 as 0x8f for 8-bit + break; + } + str = '\x1b[D'; + break; + // right-arrow + case 39: + if (this.applicationKeypad) { + str = '\x1bOC'; + break; + } + str = '\x1b[C'; + break; + // up-arrow + case 38: + if (this.applicationKeypad) { + str = '\x1bOA'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(-1); + } else { + str = '\x1b[A'; + } + break; + // down-arrow + case 40: + if (this.applicationKeypad) { + str = '\x1bOB'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(1); + } else { + str = '\x1b[B'; + } + break; + // delete + case 46: + str = '\x1b[3~'; + break; + // insert + case 45: + str = '\x1b[2~'; + break; + // home + case 36: + if (this.applicationKeypad) { + str = '\x1bOH'; + break; + } + str = '\x1bOH'; + break; + // end + case 35: + if (this.applicationKeypad) { + str = '\x1bOF'; + break; + } + str = '\x1bOF'; + break; + // page up + case 33: + if (ev.ctrlKey) { + this.scrollDisp(-(this.rows - 1)); + } else { + str = '\x1b[5~'; + } + break; + // page down + case 34: + if (ev.ctrlKey) { + this.scrollDisp(this.rows - 1); + } else { + str = '\x1b[6~'; + } + break; + // F1 + case 112: + str = '\x1bOP'; + break; + // F2 + case 113: + str = '\x1bOQ'; + break; + // F3 + case 114: + str = '\x1bOR'; + break; + // F4 + case 115: + str = '\x1bOS'; + break; + // F5 + case 116: + str = '\x1b[15~'; + break; + // F6 + case 117: + str = '\x1b[17~'; + break; + // F7 + case 118: + str = '\x1b[18~'; + break; + // F8 + case 119: + str = '\x1b[19~'; + break; + // F9 + case 120: + str = '\x1b[20~'; + break; + // F10 + case 121: + str = '\x1b[21~'; + break; + // F11 + case 122: + str = '\x1b[23~'; + break; + // F12 + case 123: + str = '\x1b[24~'; + break; + default: + // a-z and space + if (ev.ctrlKey) { + if (ev.keyCode >= 65 && ev.keyCode <= 90) { + str = String.fromCharCode(ev.keyCode - 64); + } else if (ev.keyCode === 32) { + // NUL + str = String.fromCharCode(0); + } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { + // escape, file sep, group sep, record sep, unit sep + str = String.fromCharCode(ev.keyCode - 51 + 27); + } else if (ev.keyCode === 56) { + // delete + str = String.fromCharCode(127); + } + } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) { + if (ev.keyCode >= 65 && ev.keyCode <= 90) { + str = '\x1b' + String.fromCharCode(ev.keyCode + 32); + } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { + str = '\x1b' + (ev.keyCode - 48); + } + } + break; + } + + if (str) { + cancel(ev); + + this.showCursor(); + this.keyState = 1; + this.keyStr = str; + this.handler(str); + + return false; + } else { + this.keyState = 0; + return true; + } +}; + +Terminal.prototype.keyPressHandler = function(ev) { + var str = '' + , key; + + cancel(ev); + + if (!('charCode' in ev)) { + key = ev.keyCode; + if (this.keyState === 1) { + this.keyState = 2; + return false; + } else if (this.keyState === 2) { + this.showCursor(); + this.handler(this.keyStr); + return false; + } + } else { + key = ev.charCode; + } + + if (key !== 0) { + if (!ev.ctrlKey + && ((!this.isMac && !ev.altKey) + || (this.isMac && !ev.metaKey))) { + str = String.fromCharCode(key); + } + } + + if (str) { + this.showCursor(); + this.handler(str); + return false; + } else { + return true; + } +}; + +Terminal.prototype.queueChars = function(str) { + var self = this; + + this.outputQueue += str; + + if (this.outputQueue) { + setTimeout(function() { + self.outputHandler(); + }, 1); + } +}; + +Terminal.prototype.outputHandler = function() { + if (this.outputQueue) { + this.handler(this.outputQueue); + this.outputQueue = ''; + } +}; + +Terminal.prototype.bell = function() { + if (!this.useBell) return; + var self = this; + this.element.style.borderColor = 'white'; + setTimeout(function() { + self.element.style.borderColor = ''; + }, 10); +}; + +Terminal.prototype.resize = function(x, y) { + var line + , el + , i + , j; + + if (x < 1) x = 1; + if (y < 1) y = 1; + + // make sure the cursor stays on screen + if (this.y >= y) this.y = y - 1; + if (this.x >= x) this.x = x - 1; + + if (this.cols < x) { + i = this.lines.length; + while (i--) { + while (this.lines[i].length < x) { + this.lines[i].push((this.defAttr << 16) | 32); + } + } + } else if (this.cols > x) { + i = this.lines.length; + while (i--) { + while (this.lines[i].length > x) { + this.lines[i].pop(); + } + } + } + + j = this.rows; + if (j < y) { + el = this.element; + while (j++ < y) { + if (this.lines.length < y + this.ybase) { + this.lines.push(this.blankLine()); + } + if (this.children.length < y) { + line = document.createElement('div'); + line.className = 'term'; + el.appendChild(line); + this.children.push(line); + } + } + } else if (j > y) { + while (j-- > y) { + if (this.lines.length > y + this.ybase) { + this.lines.shift(); + } + if (this.children.length > y) { + el = this.children.pop(); + if (!el) continue; + el.parentNode.removeChild(el); + } + } + } + + this.cols = x; + this.rows = y; + this.scrollTop = 0; + this.scrollBottom = y - 1; + this.refreshStart = 0; + this.refreshEnd = y - 1; + + this.refresh(0, this.rows - 1); + + // it's a real nightmare trying + // to resize the original + // screen buffer. just set it + // to null for now. + this.normal = null; +}; + +Terminal.prototype.getRows = function(y) { + this.refreshStart = Math.min(this.refreshStart, y); + this.refreshEnd = Math.max(this.refreshEnd, y); +}; + +Terminal.prototype.eraseLine = function(x, y) { + var line, i, ch, row; + + row = this.ybase + y; + + line = this.lines[row]; + // screen: + // ch = 32 | (this.defAttr << 16); + // xterm, linux: + ch = 32 | (this.curAttr << 16); + + for (i = x; i < this.cols; i++) { + line[i] = ch; + } + + this.getRows(y); +}; + +Terminal.prototype.blankLine = function(cur) { + var attr = cur + ? this.curAttr + : this.defAttr; + + var ch = 32 | (attr << 16) + , line = [] + , i = 0; + + for (; i < this.cols; i++) { + line[i] = ch; + } + + return line; +}; + +/** + * ESC + */ + +// ESC D Index (IND is 0x84). +Terminal.prototype.index = function() { + this.y++; + if (this.y >= this.scrollBottom + 1) { + this.y--; + this.scroll(); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + } + this.state = normal; +}; + +// ESC M Reverse Index (RI is 0x8d). +Terminal.prototype.reverseIndex = function() { + var j; + this.y--; + if (this.y < this.scrollTop) { + this.y++; + // echo -ne '\e[1;1H\e[44m\eM\e[0m' + // use this.blankLine(false) for screen behavior + this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); + j = this.rows - 1 - this.scrollBottom; + // add an extra one because we just added a line + // maybe put this above + this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + } + this.state = normal; +}; + +// ESC c Full Reset (RIS). +Terminal.prototype.reset = function() { + Terminal.call(this, this.cols, this.rows, this.handler); +}; + +/** + * CSI + */ + +// CSI Ps A +// Cursor Up Ps Times (default = 1) (CUU). +Terminal.prototype.cursorUp = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y -= param; + if (this.y < 0) this.y = 0; +}; + +// CSI Ps B +// Cursor Down Ps Times (default = 1) (CUD). +Terminal.prototype.cursorDown = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } +}; + +// CSI Ps C +// Cursor Forward Ps Times (default = 1) (CUF). +Terminal.prototype.cursorForward = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.x += param; + if (this.x >= this.cols - 1) { + this.x = this.cols - 1; + } +}; + +// CSI Ps D +// Cursor Backward Ps Times (default = 1) (CUB). +Terminal.prototype.cursorBackward = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.x -= param; + if (this.x < 0) this.x = 0; +}; + +// CSI Ps ; Ps H +// Cursor Position [row;column] (default = [1,1]) (CUP). +Terminal.prototype.cursorPos = function(params) { + var param, row, col; + + row = params[0] - 1; + + if (params.length >= 2) { + col = params[1] - 1; + } else { + col = 0; + } + + if (row < 0) { + row = 0; + } else if (row >= this.rows) { + row = this.rows - 1; + } + + if (col < 0) { + col = 0; + } else if (col >= this.cols) { + col = this.cols - 1; + } + + this.x = col; + this.y = row; +}; + +// CSI Ps J Erase in Display (ED). +// Ps = 0 -> Erase Below (default). +// Ps = 1 -> Erase Above. +// Ps = 2 -> Erase All. +// Ps = 3 -> Erase Saved Lines (xterm). +// CSI ? Ps J +// Erase in Display (DECSED). +// Ps = 0 -> Selective Erase Below (default). +// Ps = 1 -> Selective Erase Above. +// Ps = 2 -> Selective Erase All. +Terminal.prototype.eraseInDisplay = function(params) { + var param, row, j; + switch (params[0] || 0) { + case 0: + this.eraseLine(this.x, this.y); + for (j = this.y + 1; j < this.rows; j++) { + this.eraseLine(0, j); + } + break; + case 1: + this.eraseInLine([1]); + j = this.y; + while (j--) { + this.eraseLine(0, j); + } + break; + case 2: + this.eraseInDisplay([0]); + this.eraseInDisplay([1]); + break; + case 3: + ; // no saved lines + break; + } +}; + +// CSI Ps K Erase in Line (EL). +// Ps = 0 -> Erase to Right (default). +// Ps = 1 -> Erase to Left. +// Ps = 2 -> Erase All. +// CSI ? Ps K +// Erase in Line (DECSEL). +// Ps = 0 -> Selective Erase to Right (default). +// Ps = 1 -> Selective Erase to Left. +// Ps = 2 -> Selective Erase All. +Terminal.prototype.eraseInLine = function(params) { + switch (params[0] || 0) { + case 0: + this.eraseLine(this.x, this.y); + break; + case 1: + var x = this.x + 1; + var line = this.lines[this.ybase + this.y]; + // screen: + //var ch = (this.defAttr << 16) | 32; + // xterm, linux: + var ch = (this.curAttr << 16) | 32; + while (x--) line[x] = ch; + break; + case 2: + var x = this.cols; + var line = this.lines[this.ybase + this.y]; + // screen: + //var ch = (this.defAttr << 16) | 32; + // xterm, linux: + var ch = (this.curAttr << 16) | 32; + while (x--) line[x] = ch; + break; + } +}; + +// CSI Pm m Character Attributes (SGR). +// Ps = 0 -> Normal (default). +// Ps = 1 -> Bold. +// Ps = 4 -> Underlined. +// Ps = 5 -> Blink (appears as Bold). +// Ps = 7 -> Inverse. +// Ps = 8 -> Invisible, i.e., hidden (VT300). +// Ps = 2 2 -> Normal (neither bold nor faint). +// Ps = 2 4 -> Not underlined. +// Ps = 2 5 -> Steady (not blinking). +// Ps = 2 7 -> Positive (not inverse). +// Ps = 2 8 -> Visible, i.e., not hidden (VT300). +// Ps = 3 0 -> Set foreground color to Black. +// Ps = 3 1 -> Set foreground color to Red. +// Ps = 3 2 -> Set foreground color to Green. +// Ps = 3 3 -> Set foreground color to Yellow. +// Ps = 3 4 -> Set foreground color to Blue. +// Ps = 3 5 -> Set foreground color to Magenta. +// Ps = 3 6 -> Set foreground color to Cyan. +// Ps = 3 7 -> Set foreground color to White. +// Ps = 3 9 -> Set foreground color to default (original). +// Ps = 4 0 -> Set background color to Black. +// Ps = 4 1 -> Set background color to Red. +// Ps = 4 2 -> Set background color to Green. +// Ps = 4 3 -> Set background color to Yellow. +// Ps = 4 4 -> Set background color to Blue. +// Ps = 4 5 -> Set background color to Magenta. +// Ps = 4 6 -> Set background color to Cyan. +// Ps = 4 7 -> Set background color to White. +// Ps = 4 9 -> Set background color to default (original). + +// If 16-color support is compiled, the following apply. Assume +// that xterm's resources are set so that the ISO color codes are +// the first 8 of a set of 16. Then the aixterm colors are the +// bright versions of the ISO colors: +// Ps = 9 0 -> Set foreground color to Black. +// Ps = 9 1 -> Set foreground color to Red. +// Ps = 9 2 -> Set foreground color to Green. +// Ps = 9 3 -> Set foreground color to Yellow. +// Ps = 9 4 -> Set foreground color to Blue. +// Ps = 9 5 -> Set foreground color to Magenta. +// Ps = 9 6 -> Set foreground color to Cyan. +// Ps = 9 7 -> Set foreground color to White. +// Ps = 1 0 0 -> Set background color to Black. +// Ps = 1 0 1 -> Set background color to Red. +// Ps = 1 0 2 -> Set background color to Green. +// Ps = 1 0 3 -> Set background color to Yellow. +// Ps = 1 0 4 -> Set background color to Blue. +// Ps = 1 0 5 -> Set background color to Magenta. +// Ps = 1 0 6 -> Set background color to Cyan. +// Ps = 1 0 7 -> Set background color to White. + +// If xterm is compiled with the 16-color support disabled, it +// supports the following, from rxvt: +// Ps = 1 0 0 -> Set foreground and background color to +// default. + +// If 88- or 256-color support is compiled, the following apply. +// Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second +// Ps. +// Ps = 4 8 ; 5 ; Ps -> Set background color to the second +// Ps. +Terminal.prototype.charAttributes = function(params) { + var i, p; + if (params.length === 0) { + this.curAttr = this.defAttr; + } else { + for (i = 0; i < params.length; i++) { + p = params[i]; + if (p >= 30 && p <= 37) { + this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 30) << 3); + } else if (p >= 40 && p <= 47) { + this.curAttr = (this.curAttr & ~7) | (p - 40); + } else if (p >= 90 && p <= 97) { + this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 90) << 3); + } else if (p >= 100 && p <= 107) { + this.curAttr = (this.curAttr & ~7) | (p - 100); + } else if (p === 0) { + this.curAttr = this.defAttr; + } else if (p === 1) { + // bold text + this.curAttr = this.curAttr | (1 << 8); + } else if (p === 4) { + // underlined text + this.curAttr = this.curAttr | (4 << 8); + } else if (p === 22) { + // not bold + this.curAttr = this.curAttr & ~(1 << 8); + } else if (p === 24) { + // not underlined + this.curAttr = this.curAttr & ~(4 << 8); + } else if (p === 39) { + // reset fg + p = this.curAttr & 7; + this.curAttr = (this.defAttr & ~7) | p; + } else if (p === 49) { + // reset bg + p = (this.curAttr >> 3) & 7; + this.curAttr = (this.defAttr & ~(7 << 3)) | (p << 3); + } + } + } +}; + +// CSI Ps n Device Status Report (DSR). +// Ps = 5 -> Status Report. Result (``OK'') is +// CSI 0 n +// Ps = 6 -> Report Cursor Position (CPR) [row;column]. +// Result is +// CSI r ; c R +// CSI ? Ps n +// Device Status Report (DSR, DEC-specific). +// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI +// ? r ; c R (assumes page is zero). +// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). +// or CSI ? 1 1 n (not ready). +// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) +// or CSI ? 2 1 n (locked). +// Ps = 2 6 -> Report Keyboard status as +// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). +// The last two parameters apply to VT400 & up, and denote key- +// board ready and LK01 respectively. +// Ps = 5 3 -> Report Locator status as +// CSI ? 5 3 n Locator available, if compiled-in, or +// CSI ? 5 0 n No Locator, if not. +Terminal.prototype.deviceStatus = function(params) { + if (this.prefix === '?') { + // modern xterm doesnt seem to + // respond to any of these except ?6, 6, and 5 + switch (params[0]) { + case 6: + this.queueChars('\x1b[' + + (this.y + 1) + + ';' + + (this.x + 1) + + 'R'); + break; + case 15: + // no printer + // this.queueChars('\x1b[?11n'); + break; + case 25: + // dont support user defined keys + // this.queueChars('\x1b[?21n'); + break; + case 26: + // this.queueChars('\x1b[?27;1;0;0n'); + break; + case 53: + // no dec locator/mouse + // this.queueChars('\x1b[?50n'); + break; + } + return; + } + switch (params[0]) { + case 5: + this.queueChars('\x1b[0n'); + break; + case 6: + this.queueChars('\x1b[' + + (this.y + 1) + + ';' + + (this.x + 1) + + 'R'); + break; + } +}; + +/** + * Additions + */ + +// CSI Ps @ +// Insert Ps (Blank) Character(s) (default = 1) (ICH). +Terminal.prototype.insertChars = function(params) { + var param, row, j; + param = params[0]; + if (param < 1) param = 1; + row = this.y + this.ybase; + j = this.x; + while (param-- && j < this.cols) { + // screen: + //this.lines[row].splice(j++, 0, (this.defAttr << 16) | 32); + // xterm, linux: + this.lines[row].splice(j++, 0, (this.curAttr << 16) | 32); + this.lines[row].pop(); + } +}; + +// CSI Ps E +// Cursor Next Line Ps Times (default = 1) (CNL). +Terminal.prototype.cursorNextLine = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + // above is the same as CSI Ps B + this.x = 0; +}; + +// CSI Ps F +// Cursor Preceding Line Ps Times (default = 1) (CNL). +Terminal.prototype.cursorPrecedingLine = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y -= param; + if (this.y < 0) this.y = 0; + // above is the same as CSI Ps A + this.x = 0; +}; + +// CSI Ps G +// Cursor Character Absolute [column] (default = [row,1]) (CHA). +Terminal.prototype.cursorCharAbsolute = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.x = param - 1; +}; + +// CSI Ps L +// Insert Ps Line(s) (default = 1) (IL). +Terminal.prototype.insertLines = function(params) { + var param, row, j; + param = params[0]; + if (param < 1) param = 1; + row = this.y + this.ybase; + + j = this.rows - 1 - this.scrollBottom; + // add an extra one because we added one + // above + j = this.rows - 1 + this.ybase - j + 1; + + while (param--) { + // this.blankLine(false) for screen behavior + // test: echo -e '\e[44m\e[1L\e[0m' + this.lines.splice(row, 0, this.blankLine(true)); + this.lines.splice(j, 1); + } + + //this.refresh(0, this.rows - 1); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; +}; + +// CSI Ps M +// Delete Ps Line(s) (default = 1) (DL). +Terminal.prototype.deleteLines = function(params) { + var param, row, j; + param = params[0]; + if (param < 1) param = 1; + row = this.y + this.ybase; + + j = this.rows - 1 - this.scrollBottom; + j = this.rows - 1 + this.ybase - j; + + while (param--) { + // this.blankLine(false) for screen behavior + // test: echo -e '\e[44m\e[1M\e[0m' + this.lines.splice(j + 1, 0, this.blankLine(true)); + this.lines.splice(row, 1); + } + + //this.refresh(0, this.rows - 1); + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; +}; + +// CSI Ps P +// Delete Ps Character(s) (default = 1) (DCH). +Terminal.prototype.deleteChars = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + row = this.y + this.ybase; + while (param--) { + this.lines[row].splice(this.x, 1); + // screen: + //this.lines.push((this.defAttr << 16) | 32); + // xterm, linux: + this.lines.push((this.curAttr << 16) | 32); + } +}; + +// CSI Ps X +// Erase Ps Character(s) (default = 1) (ECH). +Terminal.prototype.eraseChars = function(params) { + var param, row, j; + param = params[0]; + if (param < 1) param = 1; + row = this.y + this.ybase; + j = this.x; + while (param-- && j < this.cols) { + // screen: + // this.lines[row][j++] = (this.defAttr << 16) | 32; + // xterm, linux: + this.lines[row][j++] = (this.curAttr << 16) | 32; + } +}; + +// CSI Pm ` Character Position Absolute +// [column] (default = [row,1]) (HPA). +Terminal.prototype.charPosAbsolute = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.x = param - 1; + if (this.x >= this.cols) { + this.x = this.cols - 1; + } +}; + +// 141 61 a * HPR - +// Horizontal Position Relative +Terminal.prototype.HPositionRelative = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.x += param; + if (this.x >= this.cols - 1) { + this.x = this.cols - 1; + } + // above is the same as CSI Ps C +}; + +// CSI Ps c Send Device Attributes (Primary DA). +// Ps = 0 or omitted -> request attributes from terminal. The +// response depends on the decTerminalID resource setting. +// -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') +// -> CSI ? 1 ; 0 c (``VT101 with No Options'') +// -> CSI ? 6 c (``VT102'') +// -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') +// The VT100-style response parameters do not mean anything by +// themselves. VT220 parameters do, telling the host what fea- +// tures the terminal supports: +// Ps = 1 -> 132-columns. +// Ps = 2 -> Printer. +// Ps = 6 -> Selective erase. +// Ps = 8 -> User-defined keys. +// Ps = 9 -> National replacement character sets. +// Ps = 1 5 -> Technical characters. +// Ps = 2 2 -> ANSI color, e.g., VT525. +// Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). +// CSI > Ps c +// Send Device Attributes (Secondary DA). +// Ps = 0 or omitted -> request the terminal's identification +// code. The response depends on the decTerminalID resource set- +// ting. It should apply only to VT220 and up, but xterm extends +// this to VT100. +// -> CSI > Pp ; Pv ; Pc c +// where Pp denotes the terminal type +// Pp = 0 -> ``VT100''. +// Pp = 1 -> ``VT220''. +// and Pv is the firmware version (for xterm, this was originally +// the XFree86 patch number, starting with 95). In a DEC termi- +// nal, Pc indicates the ROM cartridge registration number and is +// always zero. +Terminal.prototype.sendDeviceAttributes = function(params) { + // This severely breaks things if + // TERM is set to `linux`. xterm + // is fine. + return; + + if (this.prefix !== '>') { + this.queueChars('\x1b[?1;2c'); + } else { + // say we're a vt100 with + // firmware version 95 + // this.queueChars('\x1b[>0;95;0c'); + // modern xterm responds with: + this.queueChars('\x1b[>0;276;0c'); + } +}; + +// CSI Pm d +// Line Position Absolute [row] (default = [1,column]) (VPA). +Terminal.prototype.linePosAbsolute = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y = param - 1; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } +}; + +// 145 65 e * VPR - Vertical Position Relative +Terminal.prototype.VPositionRelative = function(params) { + var param, row; + param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + // above is same as CSI Ps B +}; + +// CSI Ps ; Ps f +// Horizontal and Vertical Position [row;column] (default = +// [1,1]) (HVP). +Terminal.prototype.HVPosition = function(params) { + if (params[0] < 1) params[0] = 1; + if (params[1] < 1) params[1] = 1; + + this.y = params[0] - 1; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + + this.x = params[1] - 1; + if (this.x >= this.cols) { + this.x = this.cols - 1; + } +}; + +// CSI Pm h Set Mode (SM). +// Ps = 2 -> Keyboard Action Mode (AM). +// Ps = 4 -> Insert Mode (IRM). +// Ps = 1 2 -> Send/receive (SRM). +// Ps = 2 0 -> Automatic Newline (LNM). +// CSI ? Pm h +// DEC Private Mode Set (DECSET). +// Ps = 1 -> Application Cursor Keys (DECCKM). +// Ps = 2 -> Designate USASCII for character sets G0-G3 +// (DECANM), and set VT100 mode. +// Ps = 3 -> 132 Column Mode (DECCOLM). +// Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). +// Ps = 5 -> Reverse Video (DECSCNM). +// Ps = 6 -> Origin Mode (DECOM). +// Ps = 7 -> Wraparound Mode (DECAWM). +// Ps = 8 -> Auto-repeat Keys (DECARM). +// Ps = 9 -> Send Mouse X & Y on button press. See the sec- +// tion Mouse Tracking. +// Ps = 1 0 -> Show toolbar (rxvt). +// Ps = 1 2 -> Start Blinking Cursor (att610). +// Ps = 1 8 -> Print form feed (DECPFF). +// Ps = 1 9 -> Set print extent to full screen (DECPEX). +// Ps = 2 5 -> Show Cursor (DECTCEM). +// Ps = 3 0 -> Show scrollbar (rxvt). +// Ps = 3 5 -> Enable font-shifting functions (rxvt). +// Ps = 3 8 -> Enter Tektronix Mode (DECTEK). +// Ps = 4 0 -> Allow 80 -> 132 Mode. +// Ps = 4 1 -> more(1) fix (see curses resource). +// Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- +// RCM). +// Ps = 4 4 -> Turn On Margin Bell. +// Ps = 4 5 -> Reverse-wraparound Mode. +// Ps = 4 6 -> Start Logging. This is normally disabled by a +// compile-time option. +// Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- +// abled by the titeInhibit resource). +// Ps = 6 6 -> Application keypad (DECNKM). +// Ps = 6 7 -> Backarrow key sends backspace (DECBKM). +// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and +// release. See the section Mouse Tracking. +// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. +// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. +// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. +// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. +// Ps = 1 0 0 5 -> Enable Extended Mouse Mode. +// Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). +// Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). +// Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. +// (enables the eightBitInput resource). +// Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- +// Lock keys. (This enables the numLock resource). +// Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This +// enables the metaSendsEscape resource). +// Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete +// key. +// Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This +// enables the altSendsEscape resource). +// Ps = 1 0 4 0 -> Keep selection even if not highlighted. +// (This enables the keepSelection resource). +// Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables +// the selectToClipboard resource). +// Ps = 1 0 4 2 -> Enable Urgency window manager hint when +// Control-G is received. (This enables the bellIsUrgent +// resource). +// Ps = 1 0 4 3 -> Enable raising of the window when Control-G +// is received. (enables the popOnBell resource). +// Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be +// disabled by the titeInhibit resource). +// Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- +// abled by the titeInhibit resource). +// Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate +// Screen Buffer, clearing it first. (This may be disabled by +// the titeInhibit resource). This combines the effects of the 1 +// 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based +// applications rather than the 4 7 mode. +// Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. +// Ps = 1 0 5 1 -> Set Sun function-key mode. +// Ps = 1 0 5 2 -> Set HP function-key mode. +// Ps = 1 0 5 3 -> Set SCO function-key mode. +// Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). +// Ps = 1 0 6 1 -> Set VT220 keyboard emulation. +// Ps = 2 0 0 4 -> Set bracketed paste mode. +// Modes: +// http://vt100.net/docs/vt220-rm/chapter4.html +Terminal.prototype.setMode = function(params) { + if (typeof params === 'object') { + while (params.length) this.setMode(params.shift()); + return; + } + + if (this.prefix !== '?') { + switch (params) { + case 4: + this.insertMode = true; + break; + case 20: + //this.convertEol = true; + break; + } + } else { + switch (params) { + case 1: + this.applicationKeypad = true; + break; + case 6: + this.originMode = true; + break; + case 7: + this.wraparoundMode = true; + break; + case 9: // X10 Mouse + // button press only. + break; + case 1000: // vt200 mouse + // no wheel events, no motion. + // no modifiers except control. + // button press, release. + break; + case 1001: // vt200 highlight mouse + // no wheel events, no motion. + // first event is to send tracking instead + // of button press, *then* button release. + break; + case 1002: // button event mouse + case 1003: // any event mouse + // button press, release, wheel, and motion. + // no modifiers except control. + console.log('binding to mouse events - warning: experimental!'); + this.mouseEvents = true; + this.element.style.cursor = 'default'; + break; + case 1004: // send focusin/focusout events + // focusin: ^[[>I + // focusout: ^[[>O + break; + case 1005: // utf8 ext mode mouse + // for wide terminals + // simply encodes large values as utf8 characters + break; + case 1006: // sgr ext mode mouse + // for wide terminals + // does not add 32 to fields + // press: ^[[<b;x;yM + // release: ^[[<b;x;ym + break; + case 1015: // urxvt ext mode mouse + // for wide terminals + // numbers for fields + // press: ^[[b;x;yM + // motion: ^[[b;x;yT + break; + case 25: // show cursor + this.cursorHidden = false; + break; + case 1049: // alt screen buffer cursor + //this.saveCursor(); + ; // FALL-THROUGH + case 47: // alt screen buffer + case 1047: // alt screen buffer + if (!this.normal) { + var normal = { + lines: this.lines, + ybase: this.ybase, + ydisp: this.ydisp, + x: this.x, + y: this.y, + scrollTop: this.scrollTop, + scrollBottom: this.scrollBottom + }; + this.reset(); + this.normal = normal; + } + break; + } + } +}; + +// CSI Pm l Reset Mode (RM). +// Ps = 2 -> Keyboard Action Mode (AM). +// Ps = 4 -> Replace Mode (IRM). +// Ps = 1 2 -> Send/receive (SRM). +// Ps = 2 0 -> Normal Linefeed (LNM). +// CSI ? Pm l +// DEC Private Mode Reset (DECRST). +// Ps = 1 -> Normal Cursor Keys (DECCKM). +// Ps = 2 -> Designate VT52 mode (DECANM). +// Ps = 3 -> 80 Column Mode (DECCOLM). +// Ps = 4 -> Jump (Fast) Scroll (DECSCLM). +// Ps = 5 -> Normal Video (DECSCNM). +// Ps = 6 -> Normal Cursor Mode (DECOM). +// Ps = 7 -> No Wraparound Mode (DECAWM). +// Ps = 8 -> No Auto-repeat Keys (DECARM). +// Ps = 9 -> Don't send Mouse X & Y on button press. +// Ps = 1 0 -> Hide toolbar (rxvt). +// Ps = 1 2 -> Stop Blinking Cursor (att610). +// Ps = 1 8 -> Don't print form feed (DECPFF). +// Ps = 1 9 -> Limit print to scrolling region (DECPEX). +// Ps = 2 5 -> Hide Cursor (DECTCEM). +// Ps = 3 0 -> Don't show scrollbar (rxvt). +// Ps = 3 5 -> Disable font-shifting functions (rxvt). +// Ps = 4 0 -> Disallow 80 -> 132 Mode. +// Ps = 4 1 -> No more(1) fix (see curses resource). +// Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- +// NRCM). +// Ps = 4 4 -> Turn Off Margin Bell. +// Ps = 4 5 -> No Reverse-wraparound Mode. +// Ps = 4 6 -> Stop Logging. (This is normally disabled by a +// compile-time option). +// Ps = 4 7 -> Use Normal Screen Buffer. +// Ps = 6 6 -> Numeric keypad (DECNKM). +// Ps = 6 7 -> Backarrow key sends delete (DECBKM). +// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and +// release. See the section Mouse Tracking. +// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. +// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. +// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. +// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. +// Ps = 1 0 0 5 -> Disable Extended Mouse Mode. +// Ps = 1 0 1 0 -> Don't scroll to bottom on tty output +// (rxvt). +// Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). +// Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables +// the eightBitInput resource). +// Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- +// Lock keys. (This disables the numLock resource). +// Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. +// (This disables the metaSendsEscape resource). +// Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad +// Delete key. +// Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. +// (This disables the altSendsEscape resource). +// Ps = 1 0 4 0 -> Do not keep selection when not highlighted. +// (This disables the keepSelection resource). +// Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables +// the selectToClipboard resource). +// Ps = 1 0 4 2 -> Disable Urgency window manager hint when +// Control-G is received. (This disables the bellIsUrgent +// resource). +// Ps = 1 0 4 3 -> Disable raising of the window when Control- +// G is received. (This disables the popOnBell resource). +// Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen +// first if in the Alternate Screen. (This may be disabled by +// the titeInhibit resource). +// Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be +// disabled by the titeInhibit resource). +// Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor +// as in DECRC. (This may be disabled by the titeInhibit +// resource). This combines the effects of the 1 0 4 7 and 1 0 +// 4 8 modes. Use this with terminfo-based applications rather +// than the 4 7 mode. +// Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. +// Ps = 1 0 5 1 -> Reset Sun function-key mode. +// Ps = 1 0 5 2 -> Reset HP function-key mode. +// Ps = 1 0 5 3 -> Reset SCO function-key mode. +// Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). +// Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. +// Ps = 2 0 0 4 -> Reset bracketed paste mode. +Terminal.prototype.resetMode = function(params) { + if (typeof para…
- Loading branch information