From 3f455f90d944e4ea0d4ad8e7659fe3ffa3b982ac Mon Sep 17 00:00:00 2001 From: paris Date: Wed, 10 Sep 2014 11:09:23 +0000 Subject: [PATCH] Fix #4 --- addons/attach/attach.js | 47 +- addons/fit/fit.js | 72 +- addons/fullscreen/fullscreen.js | 40 +- src/xterm.js | 9112 ++++++++++++++++--------------- 4 files changed, 4659 insertions(+), 4612 deletions(-) diff --git a/addons/attach/attach.js b/addons/attach/attach.js index 12eb1359..e7cac84d 100644 --- a/addons/attach/attach.js +++ b/addons/attach/attach.js @@ -5,19 +5,34 @@ * The bidirectional argument indicates, whether the terminal should * send data to the socket as well and is true, by default. */ -Terminal.prototype.attach = function (socket, bidirectional) { - var term = this; - - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - this.socket = socket; - - socket.addEventListener('message', function (ev) { - term.write(ev.data); - }); - - if (bidirectional) { - this.on('data', function (data) { - socket.send(data); - }); - } -} \ No newline at end of file + +(function (attach) { + if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], attach); + } else { + /* + * Plain browser environment + */ + attach(this.Xterm); + } +})(function (Xterm) { + Xterm.prototype.attach = function (socket, bidirectional) { + var term = this; + + bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; + this.socket = socket; + + socket.addEventListener('message', function (ev) { + term.write(ev.data); + }); + + if (bidirectional) { + this.on('data', function (data) { + socket.send(data); + }); + } + }; +}); \ No newline at end of file diff --git a/addons/fit/fit.js b/addons/fit/fit.js index 3dc339af..69909c41 100644 --- a/addons/fit/fit.js +++ b/addons/fit/fit.js @@ -11,35 +11,49 @@ * terminal row and truncate its width with the current number * of columns) */ -Terminal.prototype.fit = function () { - var container = this.rowContainer, - subjectRow = this.rowContainer.firstElementChild, - rows, - contentBuffer, - characterWidth, - cols; +(function (fit) { + if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fit); + } else { + /* + * Plain browser environment + */ + fit(this.Xterm); + } +})(function (Xterm) { + Xterm.prototype.fit = function () { + var container = this.rowContainer, + subjectRow = this.rowContainer.firstElementChild, + rows, + contentBuffer, + characterWidth, + cols; - subjectRow.style.display = 'inline'; - - contentBuffer = subjectRow.textContent; + subjectRow.style.display = 'inline'; - subjectRow.innerHTML = ' '; /* Arbitrary character to calculate its dimensions */ - characterWidth = parseInt(subjectRow.offsetWidth); - characterHeight = parseInt(subjectRow.offsetHeight); + contentBuffer = subjectRow.textContent; - subjectRow.style.display = ''; - - cols = container.offsetWidth / characterWidth; - cols = parseInt(cols); - - var parentElementStyle = window.getComputedStyle(this.element.parentElement), - parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), - elementStyle = window.getComputedStyle(this.element), - elementPadding = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), - availableHeight = parentElementHeight - elementPadding, - rowHeight = this.rowContainer.firstElementChild.offsetHeight; - - rows = parseInt(availableHeight / rowHeight); - - this.resize(cols, rows); -}; \ No newline at end of file + subjectRow.innerHTML = ' '; /* Arbitrary character to calculate its dimensions */ + characterWidth = parseInt(subjectRow.offsetWidth); + characterHeight = parseInt(subjectRow.offsetHeight); + + subjectRow.style.display = ''; + + cols = container.offsetWidth / characterWidth; + cols = parseInt(cols); + + var parentElementStyle = window.getComputedStyle(this.element.parentElement), + parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), + elementStyle = window.getComputedStyle(this.element), + elementPadding = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), + availableHeight = parentElementHeight - elementPadding, + rowHeight = this.rowContainer.firstElementChild.offsetHeight; + + rows = parseInt(availableHeight / rowHeight); + + this.resize(cols, rows); + }; +}); \ No newline at end of file diff --git a/addons/fullscreen/fullscreen.js b/addons/fullscreen/fullscreen.js index a3012a7e..4816e951 100644 --- a/addons/fullscreen/fullscreen.js +++ b/addons/fullscreen/fullscreen.js @@ -10,16 +10,30 @@ * If the `fullscreen` argument has not been supplied, the * fullscreen mode is being toggled. */ -Terminal.prototype.toggleFullscreen = function (fullscreen) { - var fn; - - if (typeof fullscreen == 'undefined') { - fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add'; - } else if (!fullscreen) { - fn = 'remove'; - } else { - fn = 'add'; - } - - this.element.classList[fn]('fullscreen'); -} \ No newline at end of file +(function (fullscreen) { + if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fullscreen); + } else { + /* + * Plain browser environment + */ + fullscreen(this.Xterm); + } +})(function (Xterm) { + Xterm.prototype.toggleFullscreen = function (fullscreen) { + var fn; + + if (typeof fullscreen == 'undefined') { + fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add'; + } else if (!fullscreen) { + fn = 'remove'; + } else { + fn = 'add'; + } + + this.element.classList[fn]('fullscreen'); + }; +}); \ No newline at end of file diff --git a/src/xterm.js b/src/xterm.js index 44d04853..15ac2bb9 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -31,4744 +31,4748 @@ * 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'; - -/** - * Shared - */ - -var window = this - , document = this.document; - -/** - * EventEmitter - */ - -function EventEmitter() { - this._events = this._events || {}; -} - -EventEmitter.prototype.addListener = function(type, listener) { - this._events[type] = this._events[type] || []; - this._events[type].push(listener); -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.removeListener = function(type, listener) { - if (!this._events[type]) return; - - var obj = this._events[type] - , i = obj.length; - - while (i--) { - if (obj[i] === listener || obj[i].listener === listener) { - obj.splice(i, 1); - return; - } - } -}; - -EventEmitter.prototype.off = EventEmitter.prototype.removeListener; - -EventEmitter.prototype.removeAllListeners = function(type) { - if (this._events[type]) delete this._events[type]; -}; - -EventEmitter.prototype.once = function(type, listener) { - function on() { - var args = Array.prototype.slice.call(arguments); - this.removeListener(type, on); - return listener.apply(this, args); - } - on.listener = listener; - return this.on(type, on); -}; - -EventEmitter.prototype.emit = function(type) { - if (!this._events[type]) return; - - var args = Array.prototype.slice.call(arguments, 1) - , obj = this._events[type] - , l = obj.length - , i = 0; - - for (; i < l; i++) { - obj[i].apply(this, args); - } -}; - -EventEmitter.prototype.listeners = function(type) { - return this._events[type] = this._events[type] || []; -}; - - -/** - * States - */ -var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6; - -/** - * Terminal - */ - -function Terminal(options) { - var self = this; - - if (!(this instanceof Terminal)) { - return new Terminal(arguments[0], arguments[1], arguments[2]); - } - - self.cancel = Terminal.cancel; - - EventEmitter.call(this); - - if (typeof options === 'number') { - options = { - cols: arguments[0], - rows: arguments[1], - handler: arguments[2] - }; - } - - options = options || {}; - - - Object.keys(Terminal.defaults).forEach(function(key) { - if (options[key] == null) { - options[key] = Terminal.options[key]; - - if (Terminal[key] !== Terminal.defaults[key]) { - options[key] = Terminal[key]; - } - } - self[key] = options[key]; - }); - - if (options.colors.length === 8) { - options.colors = options.colors.concat(Terminal._colors.slice(8)); - } else if (options.colors.length === 16) { - options.colors = options.colors.concat(Terminal._colors.slice(16)); - } else if (options.colors.length === 10) { - options.colors = options.colors.slice(0, -2).concat( - Terminal._colors.slice(8, -2), options.colors.slice(-2)); - } else if (options.colors.length === 18) { - options.colors = options.colors.concat( - Terminal._colors.slice(16, -2), options.colors.slice(-2)); - } - this.colors = options.colors; - - this.options = options; - - // this.context = options.context || window; - // this.document = options.document || document; - this.parent = options.body || options.parent - || (document ? document.getElementsByTagName('body')[0] : null); - - this.cols = options.cols || options.geometry[0]; - this.rows = options.rows || options.geometry[1]; - - if (options.handler) { - this.on('data', options.handler); - } - - this.ybase = 0; - this.ydisp = 0; - this.x = 0; - this.y = 0; - this.cursorState = 0; - this.cursorHidden = false; - this.convertEol; - this.state = 0; - this.queue = ''; - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - - // modes - this.applicationKeypad = false; - this.applicationCursor = false; - this.originMode = false; - this.insertMode = false; - this.wraparoundMode = false; - this.normal = null; - - // charset - this.charset = null; - this.gcharset = null; - this.glevel = 0; - this.charsets = [null]; - - // mouse properties - this.decLocator; - this.x10Mouse; - this.vt200Mouse; - this.vt300Mouse; - this.normalMouse; - this.mouseEvents; - this.sendFocus; - this.utfMouse; - this.sgrMouse; - this.urxvtMouse; - - // misc - this.element; - this.children; - this.refreshStart; - this.refreshEnd; - this.savedX; - this.savedY; - this.savedCols; - - // stream - this.readable = true; - this.writable = true; - - this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.curAttr = this.defAttr; - - this.params = []; - this.currentParam = 0; - this.prefix = ''; - this.postfix = ''; - - this.lines = []; - var i = this.rows; - while (i--) { - this.lines.push(this.blankLine()); - } - - this.tabs; - this.setupStops(); -} - -inherits(Terminal, EventEmitter); - -// back_color_erase feature for xterm. -Terminal.prototype.eraseAttr = function() { - // if (this.is('screen')) return this.defAttr; - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); -}; - -/** - * Colors - */ - -// Colors 0-15 -Terminal.tangoColors = [ - // dark: - '#2e3436', - '#cc0000', - '#4e9a06', - '#c4a000', - '#3465a4', - '#75507b', - '#06989a', - '#d3d7cf', - // bright: - '#555753', - '#ef2929', - '#8ae234', - '#fce94f', - '#729fcf', - '#ad7fa8', - '#34e2e2', - '#eeeeec' -]; - -// Colors 0-15 + 16-255 -// Much thanks to TooTallNate for writing this. -Terminal.colors = (function() { - var colors = Terminal.tangoColors.slice() - , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] - , i; - - // 16-231 - i = 0; - for (; i < 216; i++) { - out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]); - } - - // 232-255 (grey) - i = 0; - for (; i < 24; i++) { - r = 8 + i * 10; - out(r, r, r); - } - - function out(r, g, b) { - colors.push('#' + hex(r) + hex(g) + hex(b)); - } - - function hex(c) { - c = c.toString(16); - return c.length < 2 ? '0' + c : c; - } - - return colors; -})(); - -Terminal._colors = Terminal.colors.slice(); - -Terminal.vcolors = (function() { - var out = [] - , colors = Terminal.colors - , i = 0 - , color; - - for (; i < 256; i++) { - color = parseInt(colors[i].substring(1), 16); - out.push([ - (color >> 16) & 0xff, - (color >> 8) & 0xff, - color & 0xff - ]); - } - - return out; -})(); - -/** - * Options - */ - -Terminal.defaults = { - colors: Terminal.colors, - theme: 'default', - convertEol: false, - termName: 'xterm', - geometry: [80, 24], - cursorBlink: false, - visualBell: false, - popOnBell: false, - scrollback: 1000, - screenKeys: false, - debug: false, - cancelEvents: false - // programFeatures: false, - // focusKeys: false, -}; - -Terminal.options = {}; - -each(keys(Terminal.defaults), function(key) { - Terminal[key] = Terminal.defaults[key]; - Terminal.options[key] = Terminal.defaults[key]; -}); - -/** - * Focused Terminal - */ -Terminal.prototype.focus = function() { - if (document.activeElement === this.textarea) { - return; - } - - if (this.sendFocus) { - this.send('\x1b[I'); - } - - this.showCursor(); - this.textarea.focus(); - - this.emit('focus', {terminal: this}); -}; - -Terminal.prototype.blur = function() { - if (Terminal.focus !== this) { - return; - } - - this.cursorState = 0; - this.refresh(this.y, this.y); - - if (this.sendFocus) { - this.send('\x1b[O'); - } - - this.emit('blur', {terminal: this}); -}; - -/** - * Initialize default behavior - */ - -Terminal.prototype.initGlobal = function() { - Terminal.bindPaste(this); - Terminal.bindKeys(this); - Terminal.bindCopy(this); -}; - -/** - * Bind to paste event - */ -Terminal.bindPaste = function(term) { - on([term.textarea, term.element], 'paste', function(ev) { - ev.stopPropagation(); - if (ev.clipboardData) { - var text = ev.clipboardData.getData('text/plain'); - term.handler(text); - } - term.textarea.value = ''; - return term.cancel(ev); - }); -}; - - -/* - * Apply key handling to the terminal - */ -Terminal.bindKeys = function(term) { - on(term.element, 'keydown', function(ev) { - if (document.activeElement != this) { - return; - } - term.keyDown(ev); - }, true); - - on(term.element, 'keypress', function(ev) { - if (document.activeElement != this) { - return; - } - term.keyPress(ev); - }, true); - - on(term.element, 'keyup', term.focus.bind(term)); - - on(term.textarea, 'keydown', function(ev) { - term.keyDown(ev); - }, true); - - on(term.textarea, 'keypress', function(ev) { - term.keyPress(ev); - - /* - * Truncate the textarea's value, since it is not needed - */ - this.value = ''; - }, true); -}; - - -/* - * Bind copy event - */ -Terminal.bindCopy = function(term) { - on(term.element, 'copy', function(ev) { - return; //temporary - }); -}; - - -/* - * Insert the given row to the terminal or produce a new one - * if no row argument is passed. Return the inserted row. - */ -Terminal.prototype.insertRow = function (row) { - if (typeof row != 'object') { - row = document.createElement('div'); - } - - this.rowContainer.appendChild(row); - this.children.push(row); - - return row; -} - - -/* - * Open Terminal in the DOM - */ -Terminal.prototype.open = function(parent) { - var self=this, i=0, div; - - this.parent = parent || this.parent; - - if (!this.parent) { - throw new Error('Terminal requires a parent element.'); - } - - /* - * Grab global elements - */ - this.context = this.parent.ownerDocument.defaultView; - this.document = this.parent.ownerDocument; - this.body = this.document.getElementsByTagName('body')[0]; - - /* - * Parse User-Agent - */ - if (this.context.navigator && this.context.navigator.userAgent) { - this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac'); - this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad'); - this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone'); - this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); - } - - /* - * Create main element container - */ - this.element = this.document.createElement('div'); - this.element.classList.add('terminal', 'xterm', 'xterm-theme-' + this.theme); - this.element.setAttribute('tabindex', 0); - - /* - * Create the container that will hold the lines of the terminal and then - * produce the lines the lines. - */ - this.rowContainer = document.createElement('div'); - this.rowContainer.classList.add('xterm-rows'); - this.element.appendChild(this.rowContainer); - this.children = []; - - /* - * Create the container that will hold helpers like the textarea for - * capturing DOM Events. Then produce the helpers. - */ - this.helperContainer = document.createElement('div'); - this.helperContainer.classList.add('xterm-helpers'); - this.element.appendChild(this.helperContainer); - this.textarea = document.createElement('textarea'); - this.textarea.classList.add('xterm-helper-textarea'); - this.textarea.setAttribute('autocorrect', 'off'); - this.textarea.setAttribute('autocapitalize', 'off'); - this.textarea.setAttribute('spellcheck', 'false'); - this.textarea.tabIndex = 0; - this.helperContainer.appendChild(this.textarea); - - for (; i < this.rows; i++) { - this.insertRow(); - } - this.parent.appendChild(this.element); - - // Draw the screen. - this.refresh(0, this.rows - 1); - - // Initialize global actions that - // need to be taken on the document. - this.initGlobal(); - - // Ensure there is a Terminal.focus. - this.focus(); - - // Start blinking the cursor. - this.startBlink(); - - on(this.element, 'mouseup', function() { - var selection = document.getSelection(); - if (selection.type != 'Range') { - self.focus(); - } - }); - - // Listen for mouse events and translate - // them into terminal mouse protocols. - this.bindMouse(); - - // Figure out whether boldness affects - // the character width of monospace fonts. - if (Terminal.brokenBold == null) { - Terminal.brokenBold = isBoldBroken(this.document); - } - - this.emit('open'); -}; - -// XTerm mouse events -// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking -// To better understand these -// the xterm code is very helpful: -// Relevant files: -// button.c, charproc.c, misc.c -// Relevant functions in xterm/button.c: -// BtnCode, EmitButtonCode, EditorButton, SendMousePosition -Terminal.prototype.bindMouse = function() { - var el = this.element - , self = this - , pressed = 32; - - var wheelEvent = 'onmousewheel' in this.context - ? 'mousewheel' - : 'DOMMouseScroll'; - - // mouseup, mousedown, mousewheel - // left click: ^[[M 3<^[[M#3< - // mousewheel up: ^[[M`3> - function sendButton(ev) { - var button - , pos; - - // get the xterm-style button - button = getButton(ev); - - // get mouse coordinates - pos = getCoords(ev); - if (!pos) return; - - sendEvent(button, pos); - - switch (ev.type) { - case 'mousedown': - pressed = button; - break; - case 'mouseup': - // keep it at the left - // button, just in case. - pressed = 32; - break; - case wheelEvent: - // nothing. don't - // interfere with - // `pressed`. - break; - } - } - - // motion example of a left click: - // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< - function sendMove(ev) { - var button = pressed - , pos; - - pos = getCoords(ev); - if (!pos) return; - - // buttons marked as motions - // are incremented by 32 - button += 32; - - sendEvent(button, pos); - } - - // encode button and - // position to characters - function encode(data, ch) { - if (!self.utfMouse) { - if (ch === 255) return data.push(0); - if (ch > 127) ch = 127; - data.push(ch); +(function (xterm) { + if (typeof define == 'function') { + /* + * Require.js is available + */ + define([], xterm); } else { - if (ch === 2047) return data.push(0); - if (ch < 127) { - data.push(ch); - } else { - if (ch > 2047) ch = 2047; - data.push(0xC0 | (ch >> 6)); - data.push(0x80 | (ch & 0x3F)); - } + /* + * Plain browser environment + */ + this.Xterm = xterm.call(this); + this.Terminal = this.Xterm; /* Backwards compatibility with term.js */ } - } +})(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 + */ - // send a mouse event: - // regular/utf8: ^[[M Cb Cx Cy - // urxvt: ^[[ Cb ; Cx ; Cy M - // sgr: ^[[ Cb ; Cx ; Cy M/m - // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r - // locator: CSI P e ; P b ; P r ; P c ; P p & w - function sendEvent(button, pos) { - // self.emit('mouse', { - // x: pos.x - 32, - // y: pos.x - 32, - // button: button - // }); + 'use strict'; - if (self.vt300Mouse) { - // NOTE: Unstable. - // http://www.vt100.net/docs/vt3xx-gp/chapter15.html - button &= 3; - pos.x -= 32; - pos.y -= 32; - var data = '\x1b[24'; - if (button === 0) data += '1'; - else if (button === 1) data += '3'; - else if (button === 2) data += '5'; - else if (button === 3) return; - else data += '0'; - data += '~[' + pos.x + ',' + pos.y + ']\r'; - self.send(data); - return; + /** + * Shared + */ + + var window = this, document = this.document; + + /** + * EventEmitter + */ + + function EventEmitter() { + this._events = this._events || {}; } - if (self.decLocator) { - // NOTE: Unstable. - button &= 3; - pos.x -= 32; - pos.y -= 32; - if (button === 0) button = 2; - else if (button === 1) button = 4; - else if (button === 2) button = 6; - else if (button === 3) button = 3; - self.send('\x1b[' - + button - + ';' - + (button === 3 ? 4 : 0) - + ';' - + pos.y - + ';' - + pos.x - + ';' - + (pos.page || 0) - + '&w'); - return; - } - - if (self.urxvtMouse) { - pos.x -= 32; - pos.y -= 32; - pos.x++; - pos.y++; - self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M'); - return; - } - - if (self.sgrMouse) { - pos.x -= 32; - pos.y -= 32; - self.send('\x1b[<' - + ((button & 3) === 3 ? button & ~3 : button) - + ';' - + pos.x - + ';' - + pos.y - + ((button & 3) === 3 ? 'm' : 'M')); - return; - } - - var data = []; - - encode(data, button); - encode(data, pos.x); - encode(data, pos.y); - - self.send('\x1b[M' + String.fromCharCode.apply(String, data)); - } - - function getButton(ev) { - var button - , shift - , meta - , ctrl - , mod; - - // two low bits: - // 0 = left - // 1 = middle - // 2 = right - // 3 = release - // wheel up/down: - // 1, and 2 - with 64 added - switch (ev.type) { - case 'mousedown': - button = ev.button != null - ? +ev.button - : ev.which != null - ? ev.which - 1 - : null; - - if (self.isMSIE) { - button = button === 1 ? 0 : button === 4 ? 1 : button; - } - break; - case 'mouseup': - button = 3; - break; - case 'DOMMouseScroll': - button = ev.detail < 0 - ? 64 - : 65; - break; - case 'mousewheel': - button = ev.wheelDeltaY > 0 - ? 64 - : 65; - break; - } - - // next three bits are the modifiers: - // 4 = shift, 8 = meta, 16 = control - shift = ev.shiftKey ? 4 : 0; - meta = ev.metaKey ? 8 : 0; - ctrl = ev.ctrlKey ? 16 : 0; - mod = shift | meta | ctrl; - - // no mods - if (self.vt200Mouse) { - // ctrl only - mod &= ctrl; - } else if (!self.normalMouse) { - mod = 0; - } - - // increment to SP - button = (32 + (mod << 2)) + button; - - return button; - } - - // mouse coordinates measured in cols/rows - function getCoords(ev) { - var x, y, w, h, el; - - // ignore browsers without pageX for now - if (ev.pageX == null) return; - - x = ev.pageX; - y = ev.pageY; - el = self.element; - - // should probably check offsetParent - // but this is more portable - while (el && el !== self.document.documentElement) { - x -= el.offsetLeft; - y -= el.offsetTop; - el = 'offsetParent' in el - ? el.offsetParent - : el.parentNode; - } - - // convert to cols/rows - w = self.element.clientWidth; - h = self.element.clientHeight; - x = Math.round((x / w) * self.cols); - y = Math.round((y / h) * self.rows); - - // be sure to avoid sending - // bad positions to the program - if (x < 0) x = 0; - if (x > self.cols) x = self.cols; - if (y < 0) y = 0; - if (y > self.rows) y = self.rows; - - // xterm sends raw bytes and - // starts at 32 (SP) for each. - x += 32; - y += 32; - - return { - x: x, - y: y, - type: ev.type === wheelEvent - ? 'mousewheel' - : ev.type + EventEmitter.prototype.addListener = function(type, listener) { + this._events[type] = this._events[type] || []; + this._events[type].push(listener); }; - } - on(el, 'mousedown', function(ev) { - if (!self.mouseEvents) return; + EventEmitter.prototype.on = EventEmitter.prototype.addListener; - // send the button - sendButton(ev); + EventEmitter.prototype.removeListener = function(type, listener) { + if (!this._events[type]) return; - // ensure focus - self.focus(); + var obj = this._events[type] + , i = obj.length; - // fix for odd bug - //if (self.vt200Mouse && !self.normalMouse) { - if (self.vt200Mouse) { - sendButton({ __proto__: ev, type: 'mouseup' }); - return self.cancel(ev); + while (i--) { + if (obj[i] === listener || obj[i].listener === listener) { + obj.splice(i, 1); + return; + } + } + }; + + EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + + EventEmitter.prototype.removeAllListeners = function(type) { + if (this._events[type]) delete this._events[type]; + }; + + EventEmitter.prototype.once = function(type, listener) { + function on() { + var args = Array.prototype.slice.call(arguments); + this.removeListener(type, on); + return listener.apply(this, args); + } + on.listener = listener; + return this.on(type, on); + }; + + EventEmitter.prototype.emit = function(type) { + if (!this._events[type]) return; + + var args = Array.prototype.slice.call(arguments, 1) + , obj = this._events[type] + , l = obj.length + , i = 0; + + for (; i < l; i++) { + obj[i].apply(this, args); + } + }; + + EventEmitter.prototype.listeners = function(type) { + return this._events[type] = this._events[type] || []; + }; + + + /** + * States + */ + var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6; + + /** + * Terminal + */ + + function Terminal(options) { + var self = this; + + if (!(this instanceof Terminal)) { + return new Terminal(arguments[0], arguments[1], arguments[2]); + } + + self.cancel = Terminal.cancel; + + EventEmitter.call(this); + + if (typeof options === 'number') { + options = { + cols: arguments[0], + rows: arguments[1], + handler: arguments[2] + }; + } + + options = options || {}; + + + Object.keys(Terminal.defaults).forEach(function(key) { + if (options[key] == null) { + options[key] = Terminal.options[key]; + + if (Terminal[key] !== Terminal.defaults[key]) { + options[key] = Terminal[key]; + } + } + self[key] = options[key]; + }); + + if (options.colors.length === 8) { + options.colors = options.colors.concat(Terminal._colors.slice(8)); + } else if (options.colors.length === 16) { + options.colors = options.colors.concat(Terminal._colors.slice(16)); + } else if (options.colors.length === 10) { + options.colors = options.colors.slice(0, -2).concat( + Terminal._colors.slice(8, -2), options.colors.slice(-2)); + } else if (options.colors.length === 18) { + options.colors = options.colors.concat( + Terminal._colors.slice(16, -2), options.colors.slice(-2)); + } + this.colors = options.colors; + + this.options = options; + + // this.context = options.context || window; + // this.document = options.document || document; + this.parent = options.body || options.parent + || (document ? document.getElementsByTagName('body')[0] : null); + + this.cols = options.cols || options.geometry[0]; + this.rows = options.rows || options.geometry[1]; + + if (options.handler) { + this.on('data', options.handler); + } + + this.ybase = 0; + this.ydisp = 0; + this.x = 0; + this.y = 0; + this.cursorState = 0; + this.cursorHidden = false; + this.convertEol; + this.state = 0; + this.queue = ''; + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + + // modes + this.applicationKeypad = false; + this.applicationCursor = false; + this.originMode = false; + this.insertMode = false; + this.wraparoundMode = false; + this.normal = null; + + // charset + this.charset = null; + this.gcharset = null; + this.glevel = 0; + this.charsets = [null]; + + // mouse properties + this.decLocator; + this.x10Mouse; + this.vt200Mouse; + this.vt300Mouse; + this.normalMouse; + this.mouseEvents; + this.sendFocus; + this.utfMouse; + this.sgrMouse; + this.urxvtMouse; + + // misc + this.element; + this.children; + this.refreshStart; + this.refreshEnd; + this.savedX; + this.savedY; + this.savedCols; + + // stream + this.readable = true; + this.writable = true; + + this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); + this.curAttr = this.defAttr; + + this.params = []; + this.currentParam = 0; + this.prefix = ''; + this.postfix = ''; + + this.lines = []; + var i = this.rows; + while (i--) { + this.lines.push(this.blankLine()); + } + + this.tabs; + this.setupStops(); } - // bind events - if (self.normalMouse) on(self.document, 'mousemove', sendMove); + inherits(Terminal, EventEmitter); - // x10 compatibility mode can't send button releases - if (!self.x10Mouse) { - on(self.document, 'mouseup', function up(ev) { + // back_color_erase feature for xterm. + Terminal.prototype.eraseAttr = function() { + // if (this.is('screen')) return this.defAttr; + return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); + }; + + /** + * Colors + */ + + // Colors 0-15 + Terminal.tangoColors = [ + // dark: + '#2e3436', + '#cc0000', + '#4e9a06', + '#c4a000', + '#3465a4', + '#75507b', + '#06989a', + '#d3d7cf', + // bright: + '#555753', + '#ef2929', + '#8ae234', + '#fce94f', + '#729fcf', + '#ad7fa8', + '#34e2e2', + '#eeeeec' + ]; + + // Colors 0-15 + 16-255 + // Much thanks to TooTallNate for writing this. + Terminal.colors = (function() { + var colors = Terminal.tangoColors.slice() + , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] + , i; + + // 16-231 + i = 0; + for (; i < 216; i++) { + out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]); + } + + // 232-255 (grey) + i = 0; + for (; i < 24; i++) { + r = 8 + i * 10; + out(r, r, r); + } + + function out(r, g, b) { + colors.push('#' + hex(r) + hex(g) + hex(b)); + } + + function hex(c) { + c = c.toString(16); + return c.length < 2 ? '0' + c : c; + } + + return colors; + })(); + + Terminal._colors = Terminal.colors.slice(); + + Terminal.vcolors = (function() { + var out = [] + , colors = Terminal.colors + , i = 0 + , color; + + for (; i < 256; i++) { + color = parseInt(colors[i].substring(1), 16); + out.push([ + (color >> 16) & 0xff, + (color >> 8) & 0xff, + color & 0xff + ]); + } + + return out; + })(); + + /** + * Options + */ + + Terminal.defaults = { + colors: Terminal.colors, + theme: 'default', + convertEol: false, + termName: 'xterm', + geometry: [80, 24], + cursorBlink: false, + visualBell: false, + popOnBell: false, + scrollback: 1000, + screenKeys: false, + debug: false, + cancelEvents: false + // programFeatures: false, + // focusKeys: false, + }; + + Terminal.options = {}; + + each(keys(Terminal.defaults), function(key) { + Terminal[key] = Terminal.defaults[key]; + Terminal.options[key] = Terminal.defaults[key]; + }); + + /** + * Focused Terminal + */ + Terminal.prototype.focus = function() { + if (document.activeElement === this.textarea) { + return; + } + + if (this.sendFocus) { + this.send('\x1b[I'); + } + + this.showCursor(); + this.textarea.focus(); + + this.emit('focus', {terminal: this}); + }; + + Terminal.prototype.blur = function() { + if (Terminal.focus !== this) { + return; + } + + this.cursorState = 0; + this.refresh(this.y, this.y); + + if (this.sendFocus) { + this.send('\x1b[O'); + } + + this.emit('blur', {terminal: this}); + }; + + /** + * Initialize default behavior + */ + + Terminal.prototype.initGlobal = function() { + Terminal.bindPaste(this); + Terminal.bindKeys(this); + Terminal.bindCopy(this); + }; + + /** + * Bind to paste event + */ + Terminal.bindPaste = function(term) { + on([term.textarea, term.element], 'paste', function(ev) { + ev.stopPropagation(); + if (ev.clipboardData) { + var text = ev.clipboardData.getData('text/plain'); + term.handler(text); + } + term.textarea.value = ''; + return term.cancel(ev); + }); + }; + + + /* + * Apply key handling to the terminal + */ + Terminal.bindKeys = function(term) { + on(term.element, 'keydown', function(ev) { + if (document.activeElement != this) { + return; + } + term.keyDown(ev); + }, true); + + on(term.element, 'keypress', function(ev) { + if (document.activeElement != this) { + return; + } + term.keyPress(ev); + }, true); + + on(term.element, 'keyup', term.focus.bind(term)); + + on(term.textarea, 'keydown', function(ev) { + term.keyDown(ev); + }, true); + + on(term.textarea, 'keypress', function(ev) { + term.keyPress(ev); + + /* + * Truncate the textarea's value, since it is not needed + */ + this.value = ''; + }, true); + }; + + + /* + * Bind copy event + */ + Terminal.bindCopy = function(term) { + on(term.element, 'copy', function(ev) { + return; //temporary + }); + }; + + + /* + * Insert the given row to the terminal or produce a new one + * if no row argument is passed. Return the inserted row. + */ + Terminal.prototype.insertRow = function (row) { + if (typeof row != 'object') { + row = document.createElement('div'); + } + + this.rowContainer.appendChild(row); + this.children.push(row); + + return row; + } + + + /* + * Open Terminal in the DOM + */ + Terminal.prototype.open = function(parent) { + var self=this, i=0, div; + + this.parent = parent || this.parent; + + if (!this.parent) { + throw new Error('Terminal requires a parent element.'); + } + + /* + * Grab global elements + */ + this.context = this.parent.ownerDocument.defaultView; + this.document = this.parent.ownerDocument; + this.body = this.document.getElementsByTagName('body')[0]; + + /* + * Parse User-Agent + */ + if (this.context.navigator && this.context.navigator.userAgent) { + this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac'); + this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad'); + this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone'); + this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); + } + + /* + * Create main element container + */ + this.element = this.document.createElement('div'); + this.element.classList.add('terminal', 'xterm', 'xterm-theme-' + this.theme); + this.element.setAttribute('tabindex', 0); + + /* + * Create the container that will hold the lines of the terminal and then + * produce the lines the lines. + */ + this.rowContainer = document.createElement('div'); + this.rowContainer.classList.add('xterm-rows'); + this.element.appendChild(this.rowContainer); + this.children = []; + + /* + * Create the container that will hold helpers like the textarea for + * capturing DOM Events. Then produce the helpers. + */ + this.helperContainer = document.createElement('div'); + this.helperContainer.classList.add('xterm-helpers'); + this.element.appendChild(this.helperContainer); + this.textarea = document.createElement('textarea'); + this.textarea.classList.add('xterm-helper-textarea'); + this.textarea.setAttribute('autocorrect', 'off'); + this.textarea.setAttribute('autocapitalize', 'off'); + this.textarea.setAttribute('spellcheck', 'false'); + this.textarea.tabIndex = 0; + this.helperContainer.appendChild(this.textarea); + + for (; i < this.rows; i++) { + this.insertRow(); + } + this.parent.appendChild(this.element); + + // Draw the screen. + this.refresh(0, this.rows - 1); + + // Initialize global actions that + // need to be taken on the document. + this.initGlobal(); + + // Ensure there is a Terminal.focus. + this.focus(); + + // Start blinking the cursor. + this.startBlink(); + + on(this.element, 'mouseup', function() { + var selection = document.getSelection(); + if (selection.type != 'Range') { + self.focus(); + } + }); + + // Listen for mouse events and translate + // them into terminal mouse protocols. + this.bindMouse(); + + // Figure out whether boldness affects + // the character width of monospace fonts. + if (Terminal.brokenBold == null) { + Terminal.brokenBold = isBoldBroken(this.document); + } + + this.emit('open'); + }; + + // XTerm mouse events + // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking + // To better understand these + // the xterm code is very helpful: + // Relevant files: + // button.c, charproc.c, misc.c + // Relevant functions in xterm/button.c: + // BtnCode, EmitButtonCode, EditorButton, SendMousePosition + Terminal.prototype.bindMouse = function() { + var el = this.element + , self = this + , pressed = 32; + + var wheelEvent = 'onmousewheel' in this.context + ? 'mousewheel' + : 'DOMMouseScroll'; + + // mouseup, mousedown, mousewheel + // left click: ^[[M 3<^[[M#3< + // mousewheel up: ^[[M`3> + function sendButton(ev) { + var button + , pos; + + // get the xterm-style button + button = getButton(ev); + + // get mouse coordinates + pos = getCoords(ev); + if (!pos) return; + + sendEvent(button, pos); + + switch (ev.type) { + case 'mousedown': + pressed = button; + break; + case 'mouseup': + // keep it at the left + // button, just in case. + pressed = 32; + break; + case wheelEvent: + // nothing. don't + // interfere with + // `pressed`. + break; + } + } + + // motion example of a left click: + // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< + function sendMove(ev) { + var button = pressed + , pos; + + pos = getCoords(ev); + if (!pos) return; + + // buttons marked as motions + // are incremented by 32 + button += 32; + + sendEvent(button, pos); + } + + // encode button and + // position to characters + function encode(data, ch) { + if (!self.utfMouse) { + if (ch === 255) return data.push(0); + if (ch > 127) ch = 127; + data.push(ch); + } else { + if (ch === 2047) return data.push(0); + if (ch < 127) { + data.push(ch); + } else { + if (ch > 2047) ch = 2047; + data.push(0xC0 | (ch >> 6)); + data.push(0x80 | (ch & 0x3F)); + } + } + } + + // send a mouse event: + // regular/utf8: ^[[M Cb Cx Cy + // urxvt: ^[[ Cb ; Cx ; Cy M + // sgr: ^[[ Cb ; Cx ; Cy M/m + // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r + // locator: CSI P e ; P b ; P r ; P c ; P p & w + function sendEvent(button, pos) { + // self.emit('mouse', { + // x: pos.x - 32, + // y: pos.x - 32, + // button: button + // }); + + if (self.vt300Mouse) { + // NOTE: Unstable. + // http://www.vt100.net/docs/vt3xx-gp/chapter15.html + button &= 3; + pos.x -= 32; + pos.y -= 32; + var data = '\x1b[24'; + if (button === 0) data += '1'; + else if (button === 1) data += '3'; + else if (button === 2) data += '5'; + else if (button === 3) return; + else data += '0'; + data += '~[' + pos.x + ',' + pos.y + ']\r'; + self.send(data); + return; + } + + if (self.decLocator) { + // NOTE: Unstable. + button &= 3; + pos.x -= 32; + pos.y -= 32; + if (button === 0) button = 2; + else if (button === 1) button = 4; + else if (button === 2) button = 6; + else if (button === 3) button = 3; + self.send('\x1b[' + + button + + ';' + + (button === 3 ? 4 : 0) + + ';' + + pos.y + + ';' + + pos.x + + ';' + + (pos.page || 0) + + '&w'); + return; + } + + if (self.urxvtMouse) { + pos.x -= 32; + pos.y -= 32; + pos.x++; + pos.y++; + self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M'); + return; + } + + if (self.sgrMouse) { + pos.x -= 32; + pos.y -= 32; + self.send('\x1b[<' + + ((button & 3) === 3 ? button & ~3 : button) + + ';' + + pos.x + + ';' + + pos.y + + ((button & 3) === 3 ? 'm' : 'M')); + return; + } + + var data = []; + + encode(data, button); + encode(data, pos.x); + encode(data, pos.y); + + self.send('\x1b[M' + String.fromCharCode.apply(String, data)); + } + + function getButton(ev) { + var button + , shift + , meta + , ctrl + , mod; + + // two low bits: + // 0 = left + // 1 = middle + // 2 = right + // 3 = release + // wheel up/down: + // 1, and 2 - with 64 added + switch (ev.type) { + case 'mousedown': + button = ev.button != null + ? +ev.button + : ev.which != null + ? ev.which - 1 + : null; + + if (self.isMSIE) { + button = button === 1 ? 0 : button === 4 ? 1 : button; + } + break; + case 'mouseup': + button = 3; + break; + case 'DOMMouseScroll': + button = ev.detail < 0 + ? 64 + : 65; + break; + case 'mousewheel': + button = ev.wheelDeltaY > 0 + ? 64 + : 65; + break; + } + + // next three bits are the modifiers: + // 4 = shift, 8 = meta, 16 = control + shift = ev.shiftKey ? 4 : 0; + meta = ev.metaKey ? 8 : 0; + ctrl = ev.ctrlKey ? 16 : 0; + mod = shift | meta | ctrl; + + // no mods + if (self.vt200Mouse) { + // ctrl only + mod &= ctrl; + } else if (!self.normalMouse) { + mod = 0; + } + + // increment to SP + button = (32 + (mod << 2)) + button; + + return button; + } + + // mouse coordinates measured in cols/rows + function getCoords(ev) { + var x, y, w, h, el; + + // ignore browsers without pageX for now + if (ev.pageX == null) return; + + x = ev.pageX; + y = ev.pageY; + el = self.element; + + // should probably check offsetParent + // but this is more portable + while (el && el !== self.document.documentElement) { + x -= el.offsetLeft; + y -= el.offsetTop; + el = 'offsetParent' in el + ? el.offsetParent + : el.parentNode; + } + + // convert to cols/rows + w = self.element.clientWidth; + h = self.element.clientHeight; + x = Math.round((x / w) * self.cols); + y = Math.round((y / h) * self.rows); + + // be sure to avoid sending + // bad positions to the program + if (x < 0) x = 0; + if (x > self.cols) x = self.cols; + if (y < 0) y = 0; + if (y > self.rows) y = self.rows; + + // xterm sends raw bytes and + // starts at 32 (SP) for each. + x += 32; + y += 32; + + return { + x: x, + y: y, + type: ev.type === wheelEvent + ? 'mousewheel' + : ev.type + }; + } + + on(el, 'mousedown', function(ev) { + if (!self.mouseEvents) return; + + // send the button sendButton(ev); - if (self.normalMouse) off(self.document, 'mousemove', sendMove); - off(self.document, 'mouseup', up); + + // ensure focus + self.focus(); + + // fix for odd bug + //if (self.vt200Mouse && !self.normalMouse) { + if (self.vt200Mouse) { + sendButton({ __proto__: ev, type: 'mouseup' }); + return self.cancel(ev); + } + + // bind events + if (self.normalMouse) on(self.document, 'mousemove', sendMove); + + // x10 compatibility mode can't send button releases + if (!self.x10Mouse) { + on(self.document, 'mouseup', function up(ev) { + sendButton(ev); + if (self.normalMouse) off(self.document, 'mousemove', sendMove); + off(self.document, 'mouseup', up); + return self.cancel(ev); + }); + } + return self.cancel(ev); }); - } - return self.cancel(ev); - }); + //if (self.normalMouse) { + // on(self.document, 'mousemove', sendMove); + //} - //if (self.normalMouse) { - // on(self.document, 'mousemove', sendMove); - //} + on(el, wheelEvent, function(ev) { + if (!self.mouseEvents) return; + if (self.x10Mouse + || self.vt300Mouse + || self.decLocator) return; + sendButton(ev); + return self.cancel(ev); + }); - on(el, wheelEvent, function(ev) { - if (!self.mouseEvents) return; - if (self.x10Mouse - || self.vt300Mouse - || self.decLocator) return; - sendButton(ev); - return self.cancel(ev); - }); + // allow mousewheel scrolling in + // the shell for example + on(el, wheelEvent, function(ev) { + if (self.mouseEvents) return; + if (self.applicationKeypad) return; + if (ev.type === 'DOMMouseScroll') { + self.scrollDisp(ev.detail < 0 ? -1 : 1); + } else { + self.scrollDisp(ev.wheelDeltaY > 0 ? -1 : 1); + } + return self.cancel(ev); + }); + }; - // allow mousewheel scrolling in - // the shell for example - on(el, wheelEvent, function(ev) { - if (self.mouseEvents) return; - if (self.applicationKeypad) return; - if (ev.type === 'DOMMouseScroll') { - self.scrollDisp(ev.detail < 0 ? -1 : 1); - } else { - self.scrollDisp(ev.wheelDeltaY > 0 ? -1 : 1); - } - return self.cancel(ev); - }); -}; + /** + * Destroy Terminal + */ -/** - * Destroy Terminal - */ + Terminal.prototype.destroy = function() { + this.readable = false; + this.writable = false; + this._events = {}; + this.handler = function() {}; + this.write = function() {}; + if (this.element.parentNode) { + this.element.parentNode.removeChild(this.element); + } + //this.emit('close'); + }; -Terminal.prototype.destroy = function() { - this.readable = false; - this.writable = false; - this._events = {}; - this.handler = function() {}; - this.write = function() {}; - if (this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - } - //this.emit('close'); -}; - -/* - * Rendering Engine - * - * In the screen buffer, each character - * is stored as a an array with a character - * and a 32-bit integer. - * First value: a utf-16 character. - * Second value: - * Next 9 bits: background color (0-511). - * Next 9 bits: foreground color (0-511). - * Next 14 bits: a mask for misc. flags: - * 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible -*/ -Terminal.prototype.refresh = function(start, end) { - var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement; + /* + * Rendering Engine + * + * In the screen buffer, each character + * is stored as a an array with a character + * and a 32-bit integer. + * First value: a utf-16 character. + * Second value: + * Next 9 bits: background color (0-511). + * Next 9 bits: foreground color (0-511). + * Next 14 bits: a mask for misc. flags: + * 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible + */ + Terminal.prototype.refresh = function(start, end) { + var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement; - if (end - start >= this.rows / 2) { - parent = this.element.parentNode; - if (parent) parent.removeChild(this.element); - } + if (end - start >= this.rows / 2) { + parent = this.element.parentNode; + if (parent) parent.removeChild(this.element); + } - width = this.cols; - y = start; + width = this.cols; + y = start; - if (end >= this.lines.length) { - this.log('`end` is too large. Most likely a bad CSR.'); - end = this.lines.length - 1; - } + if (end >= this.lines.length) { + this.log('`end` is too large. Most likely a bad CSR.'); + end = this.lines.length - 1; + } - for (; y <= end; y++) { - row = y + this.ydisp; + for (; y <= end; y++) { + row = y + this.ydisp; - line = this.lines[row]; - out = ''; + line = this.lines[row]; + out = ''; - if (y === this.y - && this.cursorState - && (this.ydisp === this.ybase) - && !this.cursorHidden) { - x = this.x; - } else { - x = -1; - } + if (y === this.y + && this.cursorState + && (this.ydisp === this.ybase) + && !this.cursorHidden) { + x = this.x; + } else { + x = -1; + } - attr = this.defAttr; - i = 0; + attr = this.defAttr; + i = 0; - for (; i < width; i++) { - data = line[i][0]; - ch = line[i][1]; + for (; i < width; i++) { + data = line[i][0]; + ch = line[i][1]; - if (i === x) data = -1; + if (i === x) data = -1; + + if (data !== attr) { + if (attr !== this.defAttr) { + out += ''; + } + if (data !== this.defAttr) { + if (data === -1) { + out += ''; + } else { + out += ''; } - if (data !== this.defAttr) { - if (data === -1) { - out += ''; - } else { - out += ''; - } - - this.children[y].innerHTML = out; - } + Terminal.prototype._cursorBlink = function() { + if (Terminal.focus !== this) return; + this.cursorState ^= 1; + this.refresh(this.y, this.y); + }; - if (parent) { - parent.appendChild(this.element); - } - - /* - * Return focus to previously focused element - */ - focused.focus(); - this.emit('refresh', {element: this.element, start: start, end: end}); -}; + Terminal.prototype.showCursor = function() { + if (!this.cursorState) { + this.cursorState = 1; + this.refresh(this.y, this.y); + } else { + // Temporarily disabled: + // this.refreshBlink(); + } + }; -Terminal.prototype._cursorBlink = function() { - if (Terminal.focus !== this) return; - this.cursorState ^= 1; - this.refresh(this.y, this.y); -}; + Terminal.prototype.startBlink = function() { + if (!this.cursorBlink) return; + var self = this; + this._blinker = function() { + self._cursorBlink(); + }; + this._blink = setInterval(this._blinker, 500); + }; -Terminal.prototype.showCursor = function() { - if (!this.cursorState) { - this.cursorState = 1; - this.refresh(this.y, this.y); - } else { - // Temporarily disabled: - // this.refreshBlink(); - } -}; + Terminal.prototype.refreshBlink = function() { + if (!this.cursorBlink) return; + clearInterval(this._blink); + this._blink = setInterval(this._blinker, 500); + }; -Terminal.prototype.startBlink = function() { - if (!this.cursorBlink) return; - var self = this; - this._blinker = function() { - self._cursorBlink(); - }; - this._blink = setInterval(this._blinker, 500); -}; + Terminal.prototype.scroll = function() { + var row; -Terminal.prototype.refreshBlink = function() { - if (!this.cursorBlink) return; - clearInterval(this._blink); - this._blink = setInterval(this._blinker, 500); -}; + if (++this.ybase === this.scrollback) { + this.ybase = this.ybase / 2 | 0; + this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); + } -Terminal.prototype.scroll = function() { - var row; - - if (++this.ybase === this.scrollback) { - this.ybase = this.ybase / 2 | 0; - this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); - } - - this.ydisp = this.ybase; - - // last line - row = this.ybase + this.rows - 1; - - // subtract the bottom scroll region - row -= this.rows - 1 - this.scrollBottom; - - if (row === this.lines.length) { - // potential optimization: - // pushing is faster than splicing - // when they amount to the same - // behavior. - this.lines.push(this.blankLine()); - } else { - // 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); - } - // this.maxRange(); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); -}; + // last line + row = this.ybase + this.rows - 1; -Terminal.prototype.scrollDisp = function(disp) { - this.ydisp += disp; + // subtract the bottom scroll region + row -= this.rows - 1 - this.scrollBottom; - if (this.ydisp > this.ybase) { - this.ydisp = this.ybase; - } else if (this.ydisp < 0) { - this.ydisp = 0; - } + if (row === this.lines.length) { + // potential optimization: + // pushing is faster than splicing + // when they amount to the same + // behavior. + this.lines.push(this.blankLine()); + } else { + // add our new line + this.lines.splice(row, 0, this.blankLine()); + } - this.refresh(0, this.rows - 1); -}; + if (this.scrollTop !== 0) { + if (this.ybase !== 0) { + this.ybase--; + this.ydisp = this.ybase; + } + this.lines.splice(this.ybase + this.scrollTop, 1); + } -Terminal.prototype.write = function(data) { - var l = data.length, i = 0, j, cs, ch; + // this.maxRange(); + this.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; - this.refreshStart = this.y; - this.refreshEnd = this.y; + Terminal.prototype.scrollDisp = function(disp) { + this.ydisp += disp; - if (this.ybase !== this.ydisp) { - this.ydisp = this.ybase; - this.maxRange(); - } + if (this.ydisp > this.ybase) { + this.ydisp = this.ybase; + } else if (this.ydisp < 0) { + this.ydisp = 0; + } - for (; i < l; i++) { - ch = data[i]; - switch (this.state) { - case normal: - switch (ch) { - case '\x07': - this.bell(); - break; + this.refresh(0, this.rows - 1); + }; - // '\n', '\v', '\f' - case '\n': - case '\x0b': - case '\x0c': - if (this.convertEol) { - this.x = 0; - } - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - break; + Terminal.prototype.write = function(data) { + var l = data.length, i = 0, j, cs, ch; - // '\r' - case '\r': - this.x = 0; - break; + this.refreshStart = this.y; + this.refreshEnd = this.y; - // '\b' - case '\x08': - if (this.x > 0) { - this.x--; - } - break; + if (this.ybase !== this.ydisp) { + this.ydisp = this.ybase; + this.maxRange(); + } - // '\t' - case '\t': - this.x = this.nextStop(); - break; + for (; i < l; i++) { + ch = data[i]; + switch (this.state) { + case normal: + switch (ch) { + case '\x07': + this.bell(); + break; - // shift out - case '\x0e': - this.setgLevel(1); - break; - - // shift in - case '\x0f': - this.setgLevel(0); - break; - - // '\e' - case '\x1b': - this.state = escaped; - break; - - default: - // ' ' - if (ch >= ' ') { - if (this.charset && this.charset[ch]) { - ch = this.charset[ch]; - } - - if (this.x >= this.cols) { - this.x = 0; + // '\n', '\v', '\f' + case '\n': + case '\x0b': + case '\x0c': + if (this.convertEol) { + this.x = 0; + } this.y++; if (this.y > this.scrollBottom) { this.y--; this.scroll(); } - } + break; - this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; - this.x++; - this.updateRange(this.y); + // '\r' + case '\r': + this.x = 0; + break; - if (isWide(ch)) { - j = this.y + this.ybase; - if (this.cols < 2 || this.x >= this.cols) { - this.lines[j][this.x - 1] = [this.curAttr, ' ']; - break; + // '\b' + case '\x08': + if (this.x > 0) { + this.x--; } - this.lines[j][this.x] = [this.curAttr, ' ']; - this.x++; - } + break; + + // '\t' + case '\t': + this.x = this.nextStop(); + break; + + // shift out + case '\x0e': + this.setgLevel(1); + break; + + // shift in + case '\x0f': + this.setgLevel(0); + break; + + // '\e' + case '\x1b': + this.state = escaped; + break; + + default: + // ' ' + if (ch >= ' ') { + 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) { + this.y--; + this.scroll(); + } + } + + this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; + this.x++; + this.updateRange(this.y); + + if (isWide(ch)) { + j = this.y + this.ybase; + if (this.cols < 2 || this.x >= this.cols) { + this.lines[j][this.x - 1] = [this.curAttr, ' ']; + break; + } + this.lines[j][this.x] = [this.curAttr, ' ']; + this.x++; + } + } + break; } break; - } - break; - case escaped: - switch (ch) { - // 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.params = []; - this.currentParam = 0; - this.state = dcs; - break; - - // ESC _ Application Program Command ( APC is 0x9f). - case '_': - this.state = ignore; - break; - - // ESC ^ Privacy Message ( PM is 0x9e). - case '^': - this.state = ignore; - 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.setgLevel(0); - this.setgCharset(0, Terminal.charsets.US); - this.state = normal; - i++; - break; - - // ESC (,),*,+,-,. Designate G0-G2 Character Set. - case '(': // <-- this seems to get all the attention - case ')': - case '*': - case '+': - case '-': - case '.': + case escaped: switch (ch) { - case '(': - this.gcharset = 0; + // 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.params = []; + this.currentParam = 0; + this.state = dcs; + break; + + // ESC _ Application Program Command ( APC is 0x9f). + case '_': + this.state = ignore; + break; + + // ESC ^ Privacy Message ( PM is 0x9e). + case '^': + this.state = ignore; + 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.setgLevel(0); + this.setgCharset(0, Terminal.charsets.US); + this.state = normal; + i++; + break; + + // ESC (,),*,+,-,. Designate G0-G2 Character Set. + case '(': // <-- this seems to get all the attention case ')': - this.gcharset = 1; - break; case '*': - this.gcharset = 2; - break; case '+': - this.gcharset = 3; - break; case '-': - this.gcharset = 1; - break; case '.': - this.gcharset = 2; + switch (ch) { + case '(': + this.gcharset = 0; + break; + case ')': + this.gcharset = 1; + break; + case '*': + this.gcharset = 2; + break; + case '+': + this.gcharset = 3; + break; + case '-': + this.gcharset = 1; + break; + case '.': + this.gcharset = 2; + break; + } + this.state = charset; + break; + + // Designate G3 Character Set (VT300). + // A = ISO Latin-1 Supplemental. + // Not implemented. + case '/': + this.gcharset = 3; + this.state = charset; + i--; + break; + + // ESC N + // Single Shift Select of G2 Character Set + // ( SS2 is 0x8e). This affects next character only. + case 'N': + break; + // ESC O + // Single Shift Select of G3 Character Set + // ( SS3 is 0x8f). This affects next character only. + case 'O': + break; + // ESC n + // Invoke the G2 Character Set as GL (LS2). + case 'n': + this.setgLevel(2); + break; + // ESC o + // Invoke the G3 Character Set as GL (LS3). + case 'o': + this.setgLevel(3); + break; + // ESC | + // Invoke the G3 Character Set as GR (LS3R). + case '|': + this.setgLevel(3); + break; + // ESC } + // Invoke the G2 Character Set as GR (LS2R). + case '}': + this.setgLevel(2); + break; + // ESC ~ + // Invoke the G1 Character Set as GR (LS1R). + case '~': + this.setgLevel(1); + 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(); + break; + + // ESC = Application Keypad (DECPAM). + case '=': + this.log('Serial port requested application keypad.'); + this.applicationKeypad = true; + this.state = normal; + break; + + // ESC > Normal Keypad (DECPNM). + case '>': + this.log('Switching back to normal keypad.'); + this.applicationKeypad = false; + this.state = normal; + break; + + default: + this.state = normal; + this.error('Unknown ESC control: %s.', ch); break; } - this.state = charset; break; - // Designate G3 Character Set (VT300). - // A = ISO Latin-1 Supplemental. - // Not implemented. - case '/': - this.gcharset = 3; - this.state = charset; - i--; - break; - - // ESC N - // Single Shift Select of G2 Character Set - // ( SS2 is 0x8e). This affects next character only. - case 'N': - break; - // ESC O - // Single Shift Select of G3 Character Set - // ( SS3 is 0x8f). This affects next character only. - case 'O': - break; - // ESC n - // Invoke the G2 Character Set as GL (LS2). - case 'n': - this.setgLevel(2); - break; - // ESC o - // Invoke the G3 Character Set as GL (LS3). - case 'o': - this.setgLevel(3); - break; - // ESC | - // Invoke the G3 Character Set as GR (LS3R). - case '|': - this.setgLevel(3); - break; - // ESC } - // Invoke the G2 Character Set as GR (LS2R). - case '}': - this.setgLevel(2); - break; - // ESC ~ - // Invoke the G1 Character Set as GR (LS1R). - case '~': - this.setgLevel(1); - break; - - // ESC 7 Save Cursor (DECSC). - case '7': - this.saveCursor(); + case charset: + switch (ch) { + case '0': // DEC Special Character and Line Drawing Set. + cs = Terminal.charsets.SCLD; + break; + case 'A': // UK + cs = Terminal.charsets.UK; + break; + case 'B': // United States (USASCII). + cs = Terminal.charsets.US; + break; + case '4': // Dutch + cs = Terminal.charsets.Dutch; + break; + case 'C': // Finnish + case '5': + cs = Terminal.charsets.Finnish; + break; + case 'R': // French + cs = Terminal.charsets.French; + break; + case 'Q': // FrenchCanadian + cs = Terminal.charsets.FrenchCanadian; + break; + case 'K': // German + cs = Terminal.charsets.German; + break; + case 'Y': // Italian + cs = Terminal.charsets.Italian; + break; + case 'E': // NorwegianDanish + case '6': + cs = Terminal.charsets.NorwegianDanish; + break; + case 'Z': // Spanish + cs = Terminal.charsets.Spanish; + break; + case 'H': // Swedish + case '7': + cs = Terminal.charsets.Swedish; + break; + case '=': // Swiss + cs = Terminal.charsets.Swiss; + break; + case '/': // ISOLatin (actually /A) + cs = Terminal.charsets.ISOLatin; + i++; + break; + default: // Default + cs = Terminal.charsets.US; + break; + } + this.setgCharset(this.gcharset, cs); + this.gcharset = null; this.state = normal; break; - // ESC 8 Restore Cursor (DECRC). - case '8': - this.restoreCursor(); - this.state = normal; - break; + case osc: + // OSC Ps ; Pt ST + // OSC Ps ; Pt BEL + // Set Text Parameters. + if (ch === '\x1b' || ch === '\x07') { + if (ch === '\x1b') i++; - // ESC # 3 DEC line height/width - case '#': - this.state = normal; - i++; - break; - - // ESC H Tab Set (HTS is 0x88). - case 'H': - this.tabSet(); - break; - - // ESC = Application Keypad (DECPAM). - case '=': - this.log('Serial port requested application keypad.'); - this.applicationKeypad = true; - this.state = normal; - break; - - // ESC > Normal Keypad (DECPNM). - case '>': - this.log('Switching back to normal keypad.'); - this.applicationKeypad = false; - this.state = normal; - break; - - default: - this.state = normal; - this.error('Unknown ESC control: %s.', ch); - break; - } - break; - - case charset: - switch (ch) { - case '0': // DEC Special Character and Line Drawing Set. - cs = Terminal.charsets.SCLD; - break; - case 'A': // UK - cs = Terminal.charsets.UK; - break; - case 'B': // United States (USASCII). - cs = Terminal.charsets.US; - break; - case '4': // Dutch - cs = Terminal.charsets.Dutch; - break; - case 'C': // Finnish - case '5': - cs = Terminal.charsets.Finnish; - break; - case 'R': // French - cs = Terminal.charsets.French; - break; - case 'Q': // FrenchCanadian - cs = Terminal.charsets.FrenchCanadian; - break; - case 'K': // German - cs = Terminal.charsets.German; - break; - case 'Y': // Italian - cs = Terminal.charsets.Italian; - break; - case 'E': // NorwegianDanish - case '6': - cs = Terminal.charsets.NorwegianDanish; - break; - case 'Z': // Spanish - cs = Terminal.charsets.Spanish; - break; - case 'H': // Swedish - case '7': - cs = Terminal.charsets.Swedish; - break; - case '=': // Swiss - cs = Terminal.charsets.Swiss; - break; - case '/': // ISOLatin (actually /A) - cs = Terminal.charsets.ISOLatin; - i++; - break; - default: // Default - cs = Terminal.charsets.US; - break; - } - this.setgCharset(this.gcharset, cs); - this.gcharset = null; - this.state = normal; - break; - - case osc: - // OSC Ps ; Pt ST - // OSC Ps ; Pt BEL - // Set Text Parameters. - if (ch === '\x1b' || ch === '\x07') { - if (ch === '\x1b') i++; - - this.params.push(this.currentParam); - - switch (this.params[0]) { - case 0: - case 1: - case 2: - if (this.params[1]) { - this.title = this.params[1]; - this.handleTitle(this.title); - } - break; - case 3: - // set X property - break; - case 4: - case 5: - // change dynamic colors - break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - // change dynamic ui colors - break; - case 46: - // change log file - break; - case 50: - // dynamic font - break; - case 51: - // emacs shell - break; - case 52: - // manipulate selection data - break; - case 104: - case 105: - case 110: - case 111: - case 112: - case 113: - case 114: - case 115: - case 116: - case 117: - case 118: - // reset colors - break; - } - - this.params = []; - this.currentParam = 0; - this.state = normal; - } else { - if (!this.params.length) { - if (ch >= '0' && ch <= '9') { - this.currentParam = - this.currentParam * 10 + ch.charCodeAt(0) - 48; - } else if (ch === ';') { this.params.push(this.currentParam); - this.currentParam = ''; - } - } else { - this.currentParam += ch; - } - } - break; - case csi: - // '?', '>', '!' - if (ch === '?' || ch === '>' || ch === '!') { - this.prefix = ch; - break; - } + switch (this.params[0]) { + case 0: + case 1: + case 2: + if (this.params[1]) { + this.title = this.params[1]; + this.handleTitle(this.title); + } + break; + case 3: + // set X property + break; + case 4: + case 5: + // change dynamic colors + break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + // change dynamic ui colors + break; + case 46: + // change log file + break; + case 50: + // dynamic font + break; + case 51: + // emacs shell + break; + case 52: + // manipulate selection data + break; + case 104: + case 105: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + // reset colors + break; + } - // 0 - 9 - if (ch >= '0' && ch <= '9') { - this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; - break; - } - - // '$', '"', ' ', '\'' - if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') { - this.postfix = ch; - break; - } - - this.params.push(this.currentParam); - this.currentParam = 0; - - // ';' - if (ch === ';') break; - - this.state = normal; - - switch (ch) { - // CSI Ps A - // Cursor Up Ps Times (default = 1) (CUU). - case 'A': - this.cursorUp(this.params); - break; - - // CSI Ps B - // Cursor Down Ps Times (default = 1) (CUD). - case 'B': - this.cursorDown(this.params); - break; - - // CSI Ps C - // Cursor Forward Ps Times (default = 1) (CUF). - case 'C': - this.cursorForward(this.params); - break; - - // CSI Ps D - // Cursor Backward Ps Times (default = 1) (CUB). - case 'D': - this.cursorBackward(this.params); - break; - - // CSI Ps ; Ps H - // Cursor Position [row;column] (default = [1,1]) (CUP). - case 'H': - this.cursorPos(this.params); - break; - - // CSI Ps J Erase in Display (ED). - case 'J': - this.eraseInDisplay(this.params); - break; - - // CSI Ps K Erase in Line (EL). - case 'K': - this.eraseInLine(this.params); - break; - - // CSI Pm m Character Attributes (SGR). - case 'm': - if (!this.prefix) { - this.charAttributes(this.params); + this.params = []; + this.currentParam = 0; + this.state = normal; + } else { + if (!this.params.length) { + if (ch >= '0' && ch <= '9') { + this.currentParam = + this.currentParam * 10 + ch.charCodeAt(0) - 48; + } else if (ch === ';') { + this.params.push(this.currentParam); + this.currentParam = ''; + } + } else { + this.currentParam += ch; + } } break; - // CSI Ps n Device Status Report (DSR). - case 'n': - if (!this.prefix) { - this.deviceStatus(this.params); - } - break; - - /** - * Additions - */ - - // CSI Ps @ - // Insert Ps (Blank) Character(s) (default = 1) (ICH). - case '@': - this.insertChars(this.params); - break; - - // CSI Ps E - // Cursor Next Line Ps Times (default = 1) (CNL). - case 'E': - this.cursorNextLine(this.params); - break; - - // CSI Ps F - // Cursor Preceding Line Ps Times (default = 1) (CNL). - case 'F': - this.cursorPrecedingLine(this.params); - break; - - // CSI Ps G - // Cursor Character Absolute [column] (default = [row,1]) (CHA). - case 'G': - this.cursorCharAbsolute(this.params); - break; - - // CSI Ps L - // Insert Ps Line(s) (default = 1) (IL). - case 'L': - this.insertLines(this.params); - break; - - // CSI Ps M - // Delete Ps Line(s) (default = 1) (DL). - case 'M': - this.deleteLines(this.params); - break; - - // CSI Ps P - // Delete Ps Character(s) (default = 1) (DCH). - case 'P': - this.deleteChars(this.params); - break; - - // CSI Ps X - // Erase Ps Character(s) (default = 1) (ECH). - case 'X': - this.eraseChars(this.params); - break; - - // CSI Pm ` Character Position Absolute - // [column] (default = [row,1]) (HPA). - case '`': - this.charPosAbsolute(this.params); - break; - - // 141 61 a * HPR - - // Horizontal Position Relative - case 'a': - this.HPositionRelative(this.params); - break; - - // CSI P s c - // Send Device Attributes (Primary DA). - // CSI > P s c - // Send Device Attributes (Secondary DA) - case 'c': - this.sendDeviceAttributes(this.params); - break; - - // CSI Pm d - // Line Position Absolute [row] (default = [1,column]) (VPA). - case 'd': - this.linePosAbsolute(this.params); - break; - - // 145 65 e * VPR - Vertical Position Relative - case 'e': - this.VPositionRelative(this.params); - break; - - // CSI Ps ; Ps f - // Horizontal and Vertical Position [row;column] (default = - // [1,1]) (HVP). - case 'f': - this.HVPosition(this.params); - break; - - // CSI Pm h Set Mode (SM). - // CSI ? Pm h - mouse escape codes, cursor escape codes - case 'h': - this.setMode(this.params); - break; - - // CSI Pm l Reset Mode (RM). - // CSI ? Pm l - case 'l': - 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 'r': - this.setScrollRegion(this.params); - break; - - // CSI s - // Save cursor (ANSI.SYS). - case 's': - this.saveCursor(this.params); - break; - - // CSI u - // Restore cursor (ANSI.SYS). - case 'u': - this.restoreCursor(this.params); - break; - - /** - * Lesser Used - */ - - // CSI Ps I - // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). - case 'I': - this.cursorForwardTab(this.params); - break; - - // CSI Ps S Scroll up Ps lines (default = 1) (SU). - case 'S': - 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 'T': - // if (this.prefix === '>') { - // this.resetTitleModes(this.params); - // break; - // } - // if (this.params.length > 2) { - // this.initMouseTracking(this.params); - // break; - // } - if (this.params.length < 2 && !this.prefix) { - this.scrollDown(this.params); - } - break; - - // CSI Ps Z - // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). - case 'Z': - this.cursorBackwardTab(this.params); - break; - - // CSI Ps b Repeat the preceding graphic character Ps times (REP). - case 'b': - this.repeatPrecedingCharacter(this.params); - break; - - // CSI Ps g Tab Clear (TBC). - case 'g': - this.tabClear(this.params); - break; - - // CSI Pm i Media Copy (MC). - // CSI ? Pm i - // case 'i': - // this.mediaCopy(this.params); - // break; - - // CSI Pm m Character Attributes (SGR). - // CSI > Ps; Ps m - // case 'm': // 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 'n': // 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 'p': - 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 'q': - // 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 'r': // 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 's': // 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 't': - // 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 'u': // 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 'v': - // if (this.postfix === '$') { - // this.copyRectagle(this.params); - // } - // break; - - // CSI Pt ; Pl ; Pb ; Pr ' w - // case 'w': - // 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 'x': - // 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 'z': - // if (this.postfix === '\'') { - // this.enableLocatorReporting(this.params); - // } else if (this.postfix === '$') { - // this.eraseRectangle(this.params); - // } - // break; - - // CSI Pm ' { - // CSI Pt; Pl; Pb; Pr$ { - // case '{': - // if (this.postfix === '\'') { - // this.setLocatorEvents(this.params); - // } else if (this.postfix === '$') { - // this.selectiveEraseRectangle(this.params); - // } - // break; - - // CSI Ps ' | - // case '|': - // if (this.postfix === '\'') { - // this.requestLocatorPosition(this.params); - // } - // break; - - // CSI P m SP } - // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. - // case '}': - // if (this.postfix === ' ') { - // this.insertColumns(this.params); - // } - // break; - - // CSI P m SP ~ - // Delete P s Column(s) (default = 1) (DECDC), VT420 and up - // case '~': - // if (this.postfix === ' ') { - // this.deleteColumns(this.params); - // } - // break; - - default: - this.error('Unknown CSI code: %s.', ch); - break; - } - - this.prefix = ''; - this.postfix = ''; - break; - - case dcs: - if (ch === '\x1b' || ch === '\x07') { - if (ch === '\x1b') i++; - - switch (this.prefix) { - // User-Defined Keys (DECUDK). - case '': + case csi: + // '?', '>', '!' + if (ch === '?' || ch === '>' || ch === '!') { + this.prefix = ch; break; + } - // Request Status String (DECRQSS). - // test: echo -e '\eP$q"p\e\\' - case '$q': - var pt = this.currentParam - , valid = false; + // 0 - 9 + if (ch >= '0' && ch <= '9') { + this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; + break; + } - switch (pt) { - // DECSCA - case '"q': - pt = '0"q'; + // '$', '"', ' ', '\'' + if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') { + this.postfix = ch; + break; + } + + this.params.push(this.currentParam); + this.currentParam = 0; + + // ';' + if (ch === ';') break; + + this.state = normal; + + switch (ch) { + // CSI Ps A + // Cursor Up Ps Times (default = 1) (CUU). + case 'A': + this.cursorUp(this.params); + break; + + // CSI Ps B + // Cursor Down Ps Times (default = 1) (CUD). + case 'B': + this.cursorDown(this.params); + break; + + // CSI Ps C + // Cursor Forward Ps Times (default = 1) (CUF). + case 'C': + this.cursorForward(this.params); + break; + + // CSI Ps D + // Cursor Backward Ps Times (default = 1) (CUB). + case 'D': + this.cursorBackward(this.params); + break; + + // CSI Ps ; Ps H + // Cursor Position [row;column] (default = [1,1]) (CUP). + case 'H': + this.cursorPos(this.params); + break; + + // CSI Ps J Erase in Display (ED). + case 'J': + this.eraseInDisplay(this.params); + break; + + // CSI Ps K Erase in Line (EL). + case 'K': + this.eraseInLine(this.params); + break; + + // CSI Pm m Character Attributes (SGR). + case 'm': + if (!this.prefix) { + this.charAttributes(this.params); + } + break; + + // CSI Ps n Device Status Report (DSR). + case 'n': + if (!this.prefix) { + this.deviceStatus(this.params); + } + break; + + /** + * Additions + */ + + // CSI Ps @ + // Insert Ps (Blank) Character(s) (default = 1) (ICH). + case '@': + this.insertChars(this.params); + break; + + // CSI Ps E + // Cursor Next Line Ps Times (default = 1) (CNL). + case 'E': + this.cursorNextLine(this.params); + break; + + // CSI Ps F + // Cursor Preceding Line Ps Times (default = 1) (CNL). + case 'F': + this.cursorPrecedingLine(this.params); + break; + + // CSI Ps G + // Cursor Character Absolute [column] (default = [row,1]) (CHA). + case 'G': + this.cursorCharAbsolute(this.params); + break; + + // CSI Ps L + // Insert Ps Line(s) (default = 1) (IL). + case 'L': + this.insertLines(this.params); + break; + + // CSI Ps M + // Delete Ps Line(s) (default = 1) (DL). + case 'M': + this.deleteLines(this.params); + break; + + // CSI Ps P + // Delete Ps Character(s) (default = 1) (DCH). + case 'P': + this.deleteChars(this.params); + break; + + // CSI Ps X + // Erase Ps Character(s) (default = 1) (ECH). + case 'X': + this.eraseChars(this.params); + break; + + // CSI Pm ` Character Position Absolute + // [column] (default = [row,1]) (HPA). + case '`': + this.charPosAbsolute(this.params); + break; + + // 141 61 a * HPR - + // Horizontal Position Relative + case 'a': + this.HPositionRelative(this.params); + break; + + // CSI P s c + // Send Device Attributes (Primary DA). + // CSI > P s c + // Send Device Attributes (Secondary DA) + case 'c': + this.sendDeviceAttributes(this.params); + break; + + // CSI Pm d + // Line Position Absolute [row] (default = [1,column]) (VPA). + case 'd': + this.linePosAbsolute(this.params); + break; + + // 145 65 e * VPR - Vertical Position Relative + case 'e': + this.VPositionRelative(this.params); + break; + + // CSI Ps ; Ps f + // Horizontal and Vertical Position [row;column] (default = + // [1,1]) (HVP). + case 'f': + this.HVPosition(this.params); + break; + + // CSI Pm h Set Mode (SM). + // CSI ? Pm h - mouse escape codes, cursor escape codes + case 'h': + this.setMode(this.params); + break; + + // CSI Pm l Reset Mode (RM). + // CSI ? Pm l + case 'l': + 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 'r': + this.setScrollRegion(this.params); + break; + + // CSI s + // Save cursor (ANSI.SYS). + case 's': + this.saveCursor(this.params); + break; + + // CSI u + // Restore cursor (ANSI.SYS). + case 'u': + this.restoreCursor(this.params); + break; + + /** + * Lesser Used + */ + + // CSI Ps I + // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). + case 'I': + this.cursorForwardTab(this.params); + break; + + // CSI Ps S Scroll up Ps lines (default = 1) (SU). + case 'S': + 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 'T': + // if (this.prefix === '>') { + // this.resetTitleModes(this.params); + // break; + // } + // if (this.params.length > 2) { + // this.initMouseTracking(this.params); + // break; + // } + if (this.params.length < 2 && !this.prefix) { + this.scrollDown(this.params); + } + break; + + // CSI Ps Z + // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + case 'Z': + this.cursorBackwardTab(this.params); + break; + + // CSI Ps b Repeat the preceding graphic character Ps times (REP). + case 'b': + this.repeatPrecedingCharacter(this.params); + break; + + // CSI Ps g Tab Clear (TBC). + case 'g': + this.tabClear(this.params); + break; + + // CSI Pm i Media Copy (MC). + // CSI ? Pm i + // case 'i': + // this.mediaCopy(this.params); + // break; + + // CSI Pm m Character Attributes (SGR). + // CSI > Ps; Ps m + // case 'm': // 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 'n': // 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 'p': + 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 'q': + // 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 'r': // 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 's': // 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 't': + // 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 'u': // 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 'v': + // if (this.postfix === '$') { + // this.copyRectagle(this.params); + // } + // break; + + // CSI Pt ; Pl ; Pb ; Pr ' w + // case 'w': + // 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 'x': + // 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 'z': + // if (this.postfix === '\'') { + // this.enableLocatorReporting(this.params); + // } else if (this.postfix === '$') { + // this.eraseRectangle(this.params); + // } + // break; + + // CSI Pm ' { + // CSI Pt; Pl; Pb; Pr$ { + // case '{': + // if (this.postfix === '\'') { + // this.setLocatorEvents(this.params); + // } else if (this.postfix === '$') { + // this.selectiveEraseRectangle(this.params); + // } + // break; + + // CSI Ps ' | + // case '|': + // if (this.postfix === '\'') { + // this.requestLocatorPosition(this.params); + // } + // break; + + // CSI P m SP } + // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. + // case '}': + // if (this.postfix === ' ') { + // this.insertColumns(this.params); + // } + // break; + + // CSI P m SP ~ + // Delete P s Column(s) (default = 1) (DECDC), VT420 and up + // case '~': + // if (this.postfix === ' ') { + // this.deleteColumns(this.params); + // } + // break; + + default: + this.error('Unknown CSI code: %s.', ch); + break; + } + + this.prefix = ''; + this.postfix = ''; + break; + + case dcs: + if (ch === '\x1b' || ch === '\x07') { + if (ch === '\x1b') i++; + + switch (this.prefix) { + // User-Defined Keys (DECUDK). + case '': break; - // DECSCL - case '"p': - pt = '61"p'; + // Request Status String (DECRQSS). + // test: echo -e '\eP$q"p\e\\' + case '$q': + var pt = this.currentParam + , valid = false; + + switch (pt) { + // DECSCA + case '"q': + pt = '0"q'; + break; + + // DECSCL + case '"p': + pt = '61"p'; + break; + + // DECSTBM + case 'r': + pt = '' + + (this.scrollTop + 1) + + ';' + + (this.scrollBottom + 1) + + 'r'; + break; + + // SGR + case 'm': + pt = '0m'; + break; + + default: + this.error('Unknown DCS Pt: %s.', pt); + pt = ''; + break; + } + + this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\'); break; - // DECSTBM - case 'r': - pt = '' - + (this.scrollTop + 1) - + ';' - + (this.scrollBottom + 1) - + 'r'; + // Set Termcap/Terminfo Data (xterm, experimental). + case '+p': break; - // SGR - case 'm': - pt = '0m'; + // Request Termcap/Terminfo String (xterm, experimental) + // Regular xterm does not even respond to this sequence. + // This can cause a small glitch in vim. + // test: echo -ne '\eP+q6b64\e\\' + case '+q': + var pt = this.currentParam + , valid = false; + + this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); break; default: - this.error('Unknown DCS Pt: %s.', pt); - pt = ''; + this.error('Unknown DCS prefix: %s.', this.prefix); break; } - this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\'); - break; + this.currentParam = 0; + this.prefix = ''; + this.state = normal; + } else if (!this.currentParam) { + if (!this.prefix && ch !== '$' && ch !== '+') { + this.currentParam = ch; + } else if (this.prefix.length === 2) { + this.currentParam = ch; + } else { + this.prefix += ch; + } + } else { + this.currentParam += ch; + } + break; - // Set Termcap/Terminfo Data (xterm, experimental). - case '+p': - break; + case ignore: + // For PM and APC. + if (ch === '\x1b' || ch === '\x07') { + if (ch === '\x1b') i++; + this.state = normal; + } + break; + } + } - // Request Termcap/Terminfo String (xterm, experimental) - // Regular xterm does not even respond to this sequence. - // This can cause a small glitch in vim. - // test: echo -ne '\eP+q6b64\e\\' - case '+q': - var pt = this.currentParam - , valid = false; + this.updateRange(this.y); + this.refresh(this.refreshStart, this.refreshEnd); + }; - this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); - break; + Terminal.prototype.writeln = function(data) { + this.write(data + '\r\n'); + }; - default: - this.error('Unknown DCS prefix: %s.', this.prefix); - break; + // Key Resources: + // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent + Terminal.prototype.keyDown = function(ev) { + var self = this, key; + + switch (ev.keyCode) { + // backspace + case 8: + if (ev.shiftKey) { + key = '\x08'; // ^H + break; } - - this.currentParam = 0; - this.prefix = ''; - this.state = normal; - } else if (!this.currentParam) { - if (!this.prefix && ch !== '$' && ch !== '+') { - this.currentParam = ch; - } else if (this.prefix.length === 2) { - this.currentParam = ch; + key = '\x7f'; // ^? + break; + // tab + case 9: + if (ev.shiftKey) { + key = '\x1b[Z'; + break; + } + key = '\t'; + this.cancel(ev, true); + break; + // return/enter + case 13: + key = '\r'; + this.cancel(ev, true); + break; + // escape + case 27: + key = '\x1b'; + this.cancel(ev, true); + break; + // left-arrow + case 37: + if (this.applicationCursor) { + key = '\x1bOD'; // SS3 as ^[O for 7-bit + //key = '\x8fD'; // SS3 as 0x8f for 8-bit + break; + } + key = '\x1b[D'; + break; + // right-arrow + case 39: + if (this.applicationCursor) { + key = '\x1bOC'; + break; + } + key = '\x1b[C'; + break; + // up-arrow + case 38: + if (this.applicationCursor) { + key = '\x1bOA'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(-1); + return this.cancel(ev); } else { - this.prefix += ch; + key = '\x1b[A'; + } + break; + // down-arrow + case 40: + if (this.applicationCursor) { + key = '\x1bOB'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(1); + return this.cancel(ev); + } else { + key = '\x1b[B'; + } + break; + // delete + case 46: + key = '\x1b[3~'; + break; + // insert + case 45: + key = '\x1b[2~'; + break; + // home + case 36: + if (this.applicationKeypad) { + key = '\x1bOH'; + break; + } + key = '\x1bOH'; + break; + // end + case 35: + if (this.applicationKeypad) { + key = '\x1bOF'; + break; + } + key = '\x1bOF'; + break; + // page up + case 33: + if (ev.shiftKey) { + this.scrollDisp(-(this.rows - 1)); + return this.cancel(ev); + } else { + key = '\x1b[5~'; + } + break; + // page down + case 34: + if (ev.shiftKey) { + this.scrollDisp(this.rows - 1); + return this.cancel(ev); + } else { + key = '\x1b[6~'; + } + break; + // F1 + case 112: + key = '\x1bOP'; + break; + // F2 + case 113: + key = '\x1bOQ'; + break; + // F3 + case 114: + key = '\x1bOR'; + break; + // F4 + case 115: + key = '\x1bOS'; + break; + // F5 + case 116: + key = '\x1b[15~'; + break; + // F6 + case 117: + key = '\x1b[17~'; + break; + // F7 + case 118: + key = '\x1b[18~'; + break; + // F8 + case 119: + key = '\x1b[19~'; + break; + // F9 + case 120: + key = '\x1b[20~'; + break; + // F10 + case 121: + key = '\x1b[21~'; + break; + // F11 + case 122: + key = '\x1b[23~'; + break; + // F12 + case 123: + key = '\x1b[24~'; + break; + default: + // a-z and space + if (ev.ctrlKey) { + if (ev.keyCode >= 65 && ev.keyCode <= 90) { + key = String.fromCharCode(ev.keyCode - 64); + } else if (ev.keyCode === 32) { + // NUL + key = String.fromCharCode(0); + } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { + // escape, file sep, group sep, record sep, unit sep + key = String.fromCharCode(ev.keyCode - 51 + 27); + } else if (ev.keyCode === 56) { + // delete + key = String.fromCharCode(127); + } else if (ev.keyCode === 219) { + // ^[ - escape + key = String.fromCharCode(27); + } else if (ev.keyCode === 221) { + // ^] - group sep + key = String.fromCharCode(29); + } + } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) { + if (ev.keyCode >= 65 && ev.keyCode <= 90) { + key = '\x1b' + String.fromCharCode(ev.keyCode + 32); + } else if (ev.keyCode === 192) { + key = '\x1b`'; + } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { + key = '\x1b' + (ev.keyCode - 48); + } + } + break; + } + + if (!key || (this.isMac && ev.metaKey)) { + return true; + } + + this.emit('keydown', ev); + this.emit('key', key, ev); + this.showCursor(); + this.handler(key); + + return this.cancel(ev); + }; + + Terminal.prototype.setgLevel = function(g) { + this.glevel = g; + this.charset = this.charsets[g]; + }; + + Terminal.prototype.setgCharset = function(g, charset) { + this.charsets[g] = charset; + if (this.glevel === g) { + this.charset = charset; + } + }; + + Terminal.prototype.keyPress = function(ev) { + var key; + + this.cancel(ev); + + if (ev.charCode) { + key = ev.charCode; + } else if (ev.which == null) { + key = ev.keyCode; + } else if (ev.which !== 0 && ev.charCode !== 0) { + key = ev.which; + } else { + return false; + } + + if (!key || ev.ctrlKey || ev.altKey || ev.metaKey) { + return false; + } + + key = String.fromCharCode(key); + + this.emit('keypress', key, ev); + this.emit('key', key, ev); + this.showCursor(); + this.handler(key); + + return false; + }; + + Terminal.prototype.send = function(data) { + var self = this; + + if (!this.queue) { + setTimeout(function() { + self.handler(self.queue); + self.queue = ''; + }, 1); + } + + this.queue += data; + }; + + Terminal.prototype.bell = function() { + if (!this.visualBell) return; + var self = this; + this.element.style.borderColor = 'white'; + setTimeout(function() { + self.element.style.borderColor = ''; + }, 10); + if (this.popOnBell) this.focus(); + }; + + Terminal.prototype.log = function() { + if (!this.debug) return; + if (!this.context.console || !this.context.console.log) return; + var args = Array.prototype.slice.call(arguments); + this.context.console.log.apply(this.context.console, args); + }; + + Terminal.prototype.error = function() { + if (!this.debug) return; + if (!this.context.console || !this.context.console.error) return; + var args = Array.prototype.slice.call(arguments); + this.context.console.error.apply(this.context.console, args); + }; + + Terminal.prototype.resize = function(x, y) { + var line + , el + , i + , j + , ch; + + if (x < 1) x = 1; + if (y < 1) y = 1; + + // resize cols + j = this.cols; + if (j < x) { + ch = [this.defAttr, ' ']; // does xterm use the default attr? + i = this.lines.length; + while (i--) { + while (this.lines[i].length < x) { + this.lines[i].push(ch); } - } else { - this.currentParam += ch; } - break; - - case ignore: - // For PM and APC. - if (ch === '\x1b' || ch === '\x07') { - if (ch === '\x1b') i++; - this.state = normal; - } - break; - } - } - - this.updateRange(this.y); - this.refresh(this.refreshStart, this.refreshEnd); -}; - -Terminal.prototype.writeln = function(data) { - this.write(data + '\r\n'); -}; - -// Key Resources: -// https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent -Terminal.prototype.keyDown = function(ev) { - var self = this, key; - - switch (ev.keyCode) { - // backspace - case 8: - if (ev.shiftKey) { - key = '\x08'; // ^H - break; - } - key = '\x7f'; // ^? - break; - // tab - case 9: - if (ev.shiftKey) { - key = '\x1b[Z'; - break; - } - key = '\t'; - this.cancel(ev, true); - break; - // return/enter - case 13: - key = '\r'; - this.cancel(ev, true); - break; - // escape - case 27: - key = '\x1b'; - this.cancel(ev, true); - break; - // left-arrow - case 37: - if (this.applicationCursor) { - key = '\x1bOD'; // SS3 as ^[O for 7-bit - //key = '\x8fD'; // SS3 as 0x8f for 8-bit - break; - } - key = '\x1b[D'; - break; - // right-arrow - case 39: - if (this.applicationCursor) { - key = '\x1bOC'; - break; - } - key = '\x1b[C'; - break; - // up-arrow - case 38: - if (this.applicationCursor) { - key = '\x1bOA'; - break; - } - if (ev.ctrlKey) { - this.scrollDisp(-1); - return this.cancel(ev); - } else { - key = '\x1b[A'; - } - break; - // down-arrow - case 40: - if (this.applicationCursor) { - key = '\x1bOB'; - break; - } - if (ev.ctrlKey) { - this.scrollDisp(1); - return this.cancel(ev); - } else { - key = '\x1b[B'; - } - break; - // delete - case 46: - key = '\x1b[3~'; - break; - // insert - case 45: - key = '\x1b[2~'; - break; - // home - case 36: - if (this.applicationKeypad) { - key = '\x1bOH'; - break; - } - key = '\x1bOH'; - break; - // end - case 35: - if (this.applicationKeypad) { - key = '\x1bOF'; - break; - } - key = '\x1bOF'; - break; - // page up - case 33: - if (ev.shiftKey) { - this.scrollDisp(-(this.rows - 1)); - return this.cancel(ev); - } else { - key = '\x1b[5~'; - } - break; - // page down - case 34: - if (ev.shiftKey) { - this.scrollDisp(this.rows - 1); - return this.cancel(ev); - } else { - key = '\x1b[6~'; - } - break; - // F1 - case 112: - key = '\x1bOP'; - break; - // F2 - case 113: - key = '\x1bOQ'; - break; - // F3 - case 114: - key = '\x1bOR'; - break; - // F4 - case 115: - key = '\x1bOS'; - break; - // F5 - case 116: - key = '\x1b[15~'; - break; - // F6 - case 117: - key = '\x1b[17~'; - break; - // F7 - case 118: - key = '\x1b[18~'; - break; - // F8 - case 119: - key = '\x1b[19~'; - break; - // F9 - case 120: - key = '\x1b[20~'; - break; - // F10 - case 121: - key = '\x1b[21~'; - break; - // F11 - case 122: - key = '\x1b[23~'; - break; - // F12 - case 123: - key = '\x1b[24~'; - break; - default: - // a-z and space - if (ev.ctrlKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - key = String.fromCharCode(ev.keyCode - 64); - } else if (ev.keyCode === 32) { - // NUL - key = String.fromCharCode(0); - } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { - // escape, file sep, group sep, record sep, unit sep - key = String.fromCharCode(ev.keyCode - 51 + 27); - } else if (ev.keyCode === 56) { - // delete - key = String.fromCharCode(127); - } else if (ev.keyCode === 219) { - // ^[ - escape - key = String.fromCharCode(27); - } else if (ev.keyCode === 221) { - // ^] - group sep - key = String.fromCharCode(29); - } - } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - key = '\x1b' + String.fromCharCode(ev.keyCode + 32); - } else if (ev.keyCode === 192) { - key = '\x1b`'; - } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { - key = '\x1b' + (ev.keyCode - 48); + } else if (j > x) { + i = this.lines.length; + while (i--) { + while (this.lines[i].length > x) { + this.lines[i].pop(); + } } } - break; - } + this.setupStops(j); + this.cols = x; - if (!key || (this.isMac && ev.metaKey)) { - return true; - } - - this.emit('keydown', ev); - this.emit('key', key, ev); - this.showCursor(); - this.handler(key); - - return this.cancel(ev); -}; - -Terminal.prototype.setgLevel = function(g) { - this.glevel = g; - this.charset = this.charsets[g]; -}; - -Terminal.prototype.setgCharset = function(g, charset) { - this.charsets[g] = charset; - if (this.glevel === g) { - this.charset = charset; - } -}; - -Terminal.prototype.keyPress = function(ev) { - var key; - - this.cancel(ev); - - if (ev.charCode) { - key = ev.charCode; - } else if (ev.which == null) { - key = ev.keyCode; - } else if (ev.which !== 0 && ev.charCode !== 0) { - key = ev.which; - } else { - return false; - } - - if (!key || ev.ctrlKey || ev.altKey || ev.metaKey) { - return false; - } - - key = String.fromCharCode(key); - - this.emit('keypress', key, ev); - this.emit('key', key, ev); - this.showCursor(); - this.handler(key); - - return false; -}; - -Terminal.prototype.send = function(data) { - var self = this; - - if (!this.queue) { - setTimeout(function() { - self.handler(self.queue); - self.queue = ''; - }, 1); - } - - this.queue += data; -}; - -Terminal.prototype.bell = function() { - if (!this.visualBell) return; - var self = this; - this.element.style.borderColor = 'white'; - setTimeout(function() { - self.element.style.borderColor = ''; - }, 10); - if (this.popOnBell) this.focus(); -}; - -Terminal.prototype.log = function() { - if (!this.debug) return; - if (!this.context.console || !this.context.console.log) return; - var args = Array.prototype.slice.call(arguments); - this.context.console.log.apply(this.context.console, args); -}; - -Terminal.prototype.error = function() { - if (!this.debug) return; - if (!this.context.console || !this.context.console.error) return; - var args = Array.prototype.slice.call(arguments); - this.context.console.error.apply(this.context.console, args); -}; - -Terminal.prototype.resize = function(x, y) { - var line - , el - , i - , j - , ch; - - if (x < 1) x = 1; - if (y < 1) y = 1; - - // resize cols - j = this.cols; - if (j < x) { - ch = [this.defAttr, ' ']; // does xterm use the default attr? - i = this.lines.length; - while (i--) { - while (this.lines[i].length < x) { - this.lines[i].push(ch); - } - } - } else if (j > x) { - i = this.lines.length; - while (i--) { - while (this.lines[i].length > x) { - this.lines[i].pop(); - } - } - } - this.setupStops(j); - this.cols = x; - - // resize rows - 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) { - this.insertRow(); - } - } - } else if (j > y) { - while (j-- > y) { - if (this.lines.length > y + this.ybase) { - this.lines.pop(); - } - if (this.children.length > y) { - el = this.children.pop(); - if (!el) continue; - el.parentNode.removeChild(el); - } - } - } - this.rows = y; - - /* - * Make sure that the cursor stays on screen - */ - if (this.y >= y) { - this.y = y - 1; - } - - if (this.x >= x) { - this.x = x - 1; - } - - this.scrollTop = 0; - this.scrollBottom = y - 1; - - this.refresh(0, this.rows - 1); - - this.normal = null; - - this.emit('resize', {terminal: this, cols: x, rows: y}); -}; - -Terminal.prototype.updateRange = function(y) { - if (y < this.refreshStart) this.refreshStart = y; - if (y > this.refreshEnd) this.refreshEnd = y; - // if (y > this.refreshEnd) { - // this.refreshEnd = y; - // if (y > this.rows - 1) { - // this.refreshEnd = this.rows - 1; - // } - // } -}; - -Terminal.prototype.maxRange = function() { - this.refreshStart = 0; - this.refreshEnd = this.rows - 1; -}; - -Terminal.prototype.setupStops = function(i) { - if (i != null) { - if (!this.tabs[i]) { - i = this.prevStop(i); - } - } else { - this.tabs = {}; - i = 0; - } - - for (; i < this.cols; i += 8) { - this.tabs[i] = true; - } -}; - -Terminal.prototype.prevStop = function(x) { - if (x == null) x = this.x; - while (!this.tabs[--x] && x > 0); - return x >= this.cols - ? this.cols - 1 - : x < 0 ? 0 : x; -}; - -Terminal.prototype.nextStop = function(x) { - if (x == null) x = this.x; - while (!this.tabs[++x] && x < this.cols); - return x >= this.cols - ? this.cols - 1 - : x < 0 ? 0 : x; -}; - -Terminal.prototype.eraseRight = function(x, y) { - var line = this.lines[this.ybase + y] - , ch = [this.eraseAttr(), ' ']; // xterm - - - for (; x < this.cols; x++) { - line[x] = ch; - } - - this.updateRange(y); -}; - -Terminal.prototype.eraseLeft = function(x, y) { - var line = this.lines[this.ybase + y] - , ch = [this.eraseAttr(), ' ']; // xterm - - x++; - while (x--) line[x] = ch; - - this.updateRange(y); -}; - -Terminal.prototype.eraseLine = function(y) { - this.eraseRight(0, y); -}; - -Terminal.prototype.blankLine = function(cur) { - var attr = cur - ? this.eraseAttr() - : this.defAttr; - - var ch = [attr, ' '] - , line = [] - , i = 0; - - for (; i < this.cols; i++) { - line[i] = ch; - } - - return line; -}; - -Terminal.prototype.ch = function(cur) { - return cur - ? [this.eraseAttr(), ' '] - : [this.defAttr, ' ']; -}; - -Terminal.prototype.is = function(term) { - var name = this.termName; - return (name + '').indexOf(term) === 0; -}; - -Terminal.prototype.handler = function(data) { - this.emit('data', data); -}; - -Terminal.prototype.handleTitle = function(title) { - this.emit('title', title); -}; - -/** - * ESC - */ - -// ESC D Index (IND is 0x84). -Terminal.prototype.index = function() { - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - 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++; - // possibly move the code below to term.reverseScroll(); - // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - // blankLine(true) is xterm/linux behavior - this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); - j = this.rows - 1 - this.scrollBottom; - this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); - // this.maxRange(); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); - } - this.state = normal; -}; - -// ESC c Full Reset (RIS). -Terminal.prototype.reset = function() { - this.options.rows = this.rows; - this.options.cols = this.cols; - Terminal.call(this, this.options); - this.refresh(0, this.rows - 1); -}; - -// ESC H Tab Set (HTS is 0x88). -Terminal.prototype.tabSet = function() { - this.tabs[this.x] = true; - this.state = normal; -}; - -/** - * CSI - */ - -// CSI Ps A -// Cursor Up Ps Times (default = 1) (CUU). -Terminal.prototype.cursorUp = function(params) { - var 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 = 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 = params[0]; - if (param < 1) param = 1; - this.x += param; - if (this.x >= this.cols) { - this.x = this.cols - 1; - } -}; - -// CSI Ps D -// Cursor Backward Ps Times (default = 1) (CUB). -Terminal.prototype.cursorBackward = function(params) { - var 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 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 j; - switch (params[0]) { - case 0: - this.eraseRight(this.x, this.y); - j = this.y + 1; - for (; j < this.rows; j++) { - this.eraseLine(j); - } - break; - case 1: - this.eraseLeft(this.x, this.y); - j = this.y; - while (j--) { - this.eraseLine(j); - } - break; - case 2: + // resize rows j = this.rows; - while (j--) this.eraseLine(j); - 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]) { - case 0: - this.eraseRight(this.x, this.y); - break; - case 1: - this.eraseLeft(this.x, this.y); - break; - case 2: - this.eraseLine(this.y); - 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) { - // Optimize a single SGR0. - if (params.length === 1 && params[0] === 0) { - this.curAttr = this.defAttr; - return; - } - - var l = params.length - , i = 0 - , flags = this.curAttr >> 18 - , fg = (this.curAttr >> 9) & 0x1ff - , bg = this.curAttr & 0x1ff - , p; - - for (; i < l; i++) { - p = params[i]; - if (p >= 30 && p <= 37) { - // fg color 8 - fg = p - 30; - } else if (p >= 40 && p <= 47) { - // bg color 8 - bg = p - 40; - } else if (p >= 90 && p <= 97) { - // fg color 16 - p += 8; - fg = p - 90; - } else if (p >= 100 && p <= 107) { - // bg color 16 - p += 8; - bg = p - 100; - } else if (p === 0) { - // default - flags = this.defAttr >> 18; - fg = (this.defAttr >> 9) & 0x1ff; - bg = this.defAttr & 0x1ff; - // flags = 0; - // fg = 0x1ff; - // bg = 0x1ff; - } else if (p === 1) { - // bold text - flags |= 1; - } else if (p === 4) { - // underlined text - flags |= 2; - } else if (p === 5) { - // blink - flags |= 4; - } else if (p === 7) { - // inverse and positive - // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' - flags |= 8; - } else if (p === 8) { - // invisible - flags |= 16; - } else if (p === 22) { - // not bold - flags &= ~1; - } else if (p === 24) { - // not underlined - flags &= ~2; - } else if (p === 25) { - // not blink - flags &= ~4; - } else if (p === 27) { - // not inverse - flags &= ~8; - } else if (p === 28) { - // not invisible - flags &= ~16; - } else if (p === 39) { - // reset fg - fg = (this.defAttr >> 9) & 0x1ff; - } else if (p === 49) { - // reset bg - bg = this.defAttr & 0x1ff; - } else if (p === 38) { - // fg color 256 - if (params[i + 1] === 2) { - i += 2; - fg = matchColor( - params[i] & 0xff, - params[i + 1] & 0xff, - params[i + 2] & 0xff); - if (fg === -1) fg = 0x1ff; - i += 2; - } else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - fg = p; + 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) { + this.insertRow(); + } + } + } else if (j > y) { + while (j-- > y) { + if (this.lines.length > y + this.ybase) { + this.lines.pop(); + } + if (this.children.length > y) { + el = this.children.pop(); + if (!el) continue; + el.parentNode.removeChild(el); + } + } } - } else if (p === 48) { - // bg color 256 - if (params[i + 1] === 2) { - i += 2; - bg = matchColor( - params[i] & 0xff, - params[i + 1] & 0xff, - params[i + 2] & 0xff); - if (bg === -1) bg = 0x1ff; - i += 2; - } else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - bg = p; + this.rows = y; + + /* + * Make sure that the cursor stays on screen + */ + if (this.y >= y) { + this.y = y - 1; } - } else if (p === 100) { - // reset fg/bg - fg = (this.defAttr >> 9) & 0x1ff; - bg = this.defAttr & 0x1ff; - } else { - this.error('Unknown SGR attribute: %d.', p); - } - } - this.curAttr = (flags << 18) | (fg << 9) | bg; -}; + if (this.x >= x) { + this.x = x - 1; + } -// 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) { - switch (params[0]) { - case 5: - // status report - this.send('\x1b[0n'); - break; - case 6: - // cursor position - this.send('\x1b[' - + (this.y + 1) - + ';' - + (this.x + 1) - + 'R'); - break; - } - } else if (this.prefix === '?') { - // modern xterm doesnt seem to - // respond to any of these except ?6, 6, and 5 - switch (params[0]) { - case 6: - // cursor position - this.send('\x1b[?' - + (this.y + 1) - + ';' - + (this.x + 1) - + 'R'); - break; - case 15: - // no printer - // this.send('\x1b[?11n'); - break; - case 25: - // dont support user defined keys - // this.send('\x1b[?21n'); - break; - case 26: - // north american keyboard - // this.send('\x1b[?27;1;0;0n'); - break; - case 53: - // no dec locator/mouse - // this.send('\x1b[?50n'); - break; - } - } -}; + this.scrollTop = 0; + this.scrollBottom = y - 1; -/** - * Additions - */ - -// CSI Ps @ -// Insert Ps (Blank) Character(s) (default = 1) (ICH). -Terminal.prototype.insertChars = function(params) { - var param, row, j, ch; - - param = params[0]; - if (param < 1) param = 1; - - row = this.y + this.ybase; - j = this.x; - ch = [this.eraseAttr(), ' ']; // xterm - - while (param-- && j < this.cols) { - this.lines[row].splice(j++, 0, ch); - this.lines[row].pop(); - } -}; - -// CSI Ps E -// Cursor Next Line Ps Times (default = 1) (CNL). -// same as CSI Ps B ? -Terminal.prototype.cursorNextLine = function(params) { - var param = params[0]; - if (param < 1) param = 1; - this.y += param; - if (this.y >= this.rows) { - this.y = this.rows - 1; - } - this.x = 0; -}; - -// CSI Ps F -// Cursor Preceding Line Ps Times (default = 1) (CNL). -// reuse CSI Ps A ? -Terminal.prototype.cursorPrecedingLine = function(params) { - var param = params[0]; - if (param < 1) param = 1; - this.y -= param; - if (this.y < 0) this.y = 0; - this.x = 0; -}; - -// CSI Ps G -// Cursor Character Absolute [column] (default = [row,1]) (CHA). -Terminal.prototype.cursorCharAbsolute = function(params) { - var 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; - j = this.rows - 1 + this.ybase - j + 1; - - while (param--) { - // test: echo -e '\e[44m\e[1L\e[0m' - // blankLine(true) - xterm/linux behavior - this.lines.splice(row, 0, this.blankLine(true)); - this.lines.splice(j, 1); - } - - // this.maxRange(); - this.updateRange(this.y); - this.updateRange(this.scrollBottom); -}; - -// 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--) { - // test: echo -e '\e[44m\e[1M\e[0m' - // blankLine(true) - xterm/linux behavior - this.lines.splice(j + 1, 0, this.blankLine(true)); - this.lines.splice(row, 1); - } - - // this.maxRange(); - this.updateRange(this.y); - this.updateRange(this.scrollBottom); -}; - -// CSI Ps P -// Delete Ps Character(s) (default = 1) (DCH). -Terminal.prototype.deleteChars = function(params) { - var param, row, ch; - - param = params[0]; - if (param < 1) param = 1; - - row = this.y + this.ybase; - ch = [this.eraseAttr(), ' ']; // xterm - - while (param--) { - this.lines[row].splice(this.x, 1); - this.lines[row].push(ch); - } -}; - -// CSI Ps X -// Erase Ps Character(s) (default = 1) (ECH). -Terminal.prototype.eraseChars = function(params) { - var param, row, j, ch; - - param = params[0]; - if (param < 1) param = 1; - - row = this.y + this.ybase; - j = this.x; - ch = [this.eraseAttr(), ' ']; // xterm - - while (param-- && j < this.cols) { - this.lines[row][j++] = ch; - } -}; - -// CSI Pm ` Character Position Absolute -// [column] (default = [row,1]) (HPA). -Terminal.prototype.charPosAbsolute = function(params) { - var 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 -// reuse CSI Ps C ? -Terminal.prototype.HPositionRelative = function(params) { - var param = params[0]; - if (param < 1) param = 1; - this.x += param; - if (this.x >= this.cols) { - this.x = this.cols - 1; - } -}; - -// 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. -// More information: -// xterm/charproc.c - line 2012, for more information. -// vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) -Terminal.prototype.sendDeviceAttributes = function(params) { - if (params[0] > 0) return; - - if (!this.prefix) { - if (this.is('xterm') - || this.is('rxvt-unicode') - || this.is('screen')) { - this.send('\x1b[?1;2c'); - } else if (this.is('linux')) { - this.send('\x1b[?6c'); - } - } else if (this.prefix === '>') { - // xterm and urxvt - // seem to spit this - // out around ~370 times (?). - if (this.is('xterm')) { - this.send('\x1b[>0;276;0c'); - } else if (this.is('rxvt-unicode')) { - this.send('\x1b[>85;95;0c'); - } else if (this.is('linux')) { - // not supported by linux console. - // linux console echoes parameters. - this.send(params[0] + 'c'); - } else if (this.is('screen')) { - this.send('\x1b[>83;40003;0c'); - } - } -}; - -// CSI Pm d -// Line Position Absolute [row] (default = [1,column]) (VPA). -Terminal.prototype.linePosAbsolute = function(params) { - var 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 -// reuse CSI Ps B ? -Terminal.prototype.VPositionRelative = function(params) { - var param = params[0]; - if (param < 1) param = 1; - this.y += param; - if (this.y >= this.rows) { - this.y = this.rows - 1; - } -}; - -// 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') { - var l = params.length - , i = 0; - - for (; i < l; i++) { - this.setMode(params[i]); - } - - return; - } - - if (!this.prefix) { - switch (params) { - case 4: - this.insertMode = true; - break; - case 20: - //this.convertEol = true; - break; - } - } else if (this.prefix === '?') { - switch (params) { - case 1: - this.applicationCursor = true; - break; - case 2: - this.setgCharset(0, Terminal.charsets.US); - this.setgCharset(1, Terminal.charsets.US); - this.setgCharset(2, Terminal.charsets.US); - this.setgCharset(3, Terminal.charsets.US); - // set VT100 mode here - break; - case 3: // 132 col mode - this.savedCols = this.cols; - this.resize(132, this.rows); - break; - case 6: - this.originMode = true; - break; - case 7: - this.wraparoundMode = true; - break; - case 12: - // this.cursorBlink = true; - break; - case 66: - this.log('Serial port requested application keypad.'); - this.applicationKeypad = true; - break; - case 9: // X10 Mouse - // no release, no motion, no wheel, no modifiers. - case 1000: // vt200 mouse - // no motion. - // no modifiers, except control on the wheel. - case 1002: // button event mouse - case 1003: // any event mouse - // any event - sends motion events, - // even if there is no button held down. - this.x10Mouse = params === 9; - this.vt200Mouse = params === 1000; - this.normalMouse = params > 1000; - this.mouseEvents = true; - this.element.style.cursor = 'default'; - this.log('Binding to mouse events.'); - break; - case 1004: // send focusin/focusout events - // focusin: ^[[I - // focusout: ^[[O - this.sendFocus = true; - break; - case 1005: // utf8 ext mode mouse - this.utfMouse = true; - // for wide terminals - // simply encodes large values as utf8 characters - break; - case 1006: // sgr ext mode mouse - this.sgrMouse = true; - // for wide terminals - // does not add 32 to fields - // press: ^[[ 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 params === 'object') { - var l = params.length - , i = 0; - - for (; i < l; i++) { - this.resetMode(params[i]); - } - - return; - } - - if (!this.prefix) { - switch (params) { - case 4: - this.insertMode = false; - break; - case 20: - //this.convertEol = false; - break; - } - } else if (this.prefix === '?') { - switch (params) { - case 1: - this.applicationCursor = false; - break; - case 3: - if (this.cols === 132 && this.savedCols) { - this.resize(this.savedCols, this.rows); - } - delete this.savedCols; - break; - case 6: - this.originMode = false; - break; - case 7: - this.wraparoundMode = false; - break; - case 12: - // this.cursorBlink = false; - break; - case 66: - this.log('Switching back to normal keypad.'); - this.applicationKeypad = false; - break; - case 9: // X10 Mouse - case 1000: // vt200 mouse - case 1002: // button event mouse - case 1003: // any event mouse - this.x10Mouse = false; - this.vt200Mouse = false; - this.normalMouse = false; - this.mouseEvents = false; - this.element.style.cursor = ''; - break; - case 1004: // send focusin/focusout events - this.sendFocus = false; - break; - case 1005: // utf8 ext mode mouse - this.utfMouse = false; - break; - case 1006: // sgr ext mode mouse - this.sgrMouse = false; - break; - case 1015: // urxvt ext mode mouse - this.urxvtMouse = false; - break; - case 25: // hide cursor - this.cursorHidden = true; - break; - case 1049: // alt screen buffer cursor - ; // FALL-THROUGH - case 47: // normal screen buffer - case 1047: // normal screen buffer - clearing it first - if (this.normal) { - this.lines = this.normal.lines; - this.ybase = this.normal.ybase; - this.ydisp = this.normal.ydisp; - this.x = this.normal.x; - this.y = this.normal.y; - this.scrollTop = this.normal.scrollTop; - this.scrollBottom = this.normal.scrollBottom; - this.tabs = this.normal.tabs; - this.normal = null; - // if (params === 1049) { - // this.x = this.savedX; - // this.y = this.savedY; - // } - this.refresh(0, this.rows - 1); - this.showCursor(); - } - break; - } - } -}; - -// CSI Ps ; Ps r -// Set Scrolling Region [top;bottom] (default = full size of win- -// dow) (DECSTBM). -// CSI ? Pm r -Terminal.prototype.setScrollRegion = function(params) { - if (this.prefix) return; - this.scrollTop = (params[0] || 1) - 1; - this.scrollBottom = (params[1] || this.rows) - 1; - this.x = 0; - this.y = 0; -}; - -// CSI s -// Save cursor (ANSI.SYS). -Terminal.prototype.saveCursor = function(params) { - this.savedX = this.x; - this.savedY = this.y; -}; - -// CSI u -// Restore cursor (ANSI.SYS). -Terminal.prototype.restoreCursor = function(params) { - this.x = this.savedX || 0; - this.y = this.savedY || 0; -}; - -/** - * Lesser Used - */ - -// CSI Ps I -// Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). -Terminal.prototype.cursorForwardTab = function(params) { - var param = params[0] || 1; - while (param--) { - this.x = this.nextStop(); - } -}; - -// CSI Ps S Scroll up Ps lines (default = 1) (SU). -Terminal.prototype.scrollUp = function(params) { - var param = params[0] || 1; - while (param--) { - this.lines.splice(this.ybase + this.scrollTop, 1); - this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); - } - // this.maxRange(); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); -}; - -// CSI Ps T Scroll down Ps lines (default = 1) (SD). -Terminal.prototype.scrollDown = function(params) { - var param = params[0] || 1; - while (param--) { - this.lines.splice(this.ybase + this.scrollBottom, 1); - this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); - } - // this.maxRange(); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); -}; - -// CSI Ps ; Ps ; Ps ; Ps ; Ps T -// Initiate highlight mouse tracking. Parameters are -// [func;startx;starty;firstrow;lastrow]. See the section Mouse -// Tracking. -Terminal.prototype.initMouseTracking = function(params) { - // Relevant: DECSET 1001 -}; - -// CSI > Ps; Ps T -// Reset one or more features of the title modes to the default -// value. Normally, "reset" disables the feature. It is possi- -// ble to disable the ability to reset features by compiling a -// different default for the title modes into xterm. -// Ps = 0 -> Do not set window/icon labels using hexadecimal. -// Ps = 1 -> Do not query window/icon labels using hexadeci- -// mal. -// Ps = 2 -> Do not set window/icon labels using UTF-8. -// Ps = 3 -> Do not query window/icon labels using UTF-8. -// (See discussion of "Title Modes"). -Terminal.prototype.resetTitleModes = function(params) { - ; -}; - -// CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). -Terminal.prototype.cursorBackwardTab = function(params) { - var param = params[0] || 1; - while (param--) { - this.x = this.prevStop(); - } -}; - -// CSI Ps b Repeat the preceding graphic character Ps times (REP). -Terminal.prototype.repeatPrecedingCharacter = function(params) { - var param = params[0] || 1 - , line = this.lines[this.ybase + this.y] - , ch = line[this.x - 1] || [this.defAttr, ' ']; - - while (param--) line[this.x++] = ch; -}; - -// CSI Ps g Tab Clear (TBC). -// Ps = 0 -> Clear Current Column (default). -// Ps = 3 -> Clear All. -// Potentially: -// Ps = 2 -> Clear Stops on Line. -// http://vt100.net/annarbor/aaa-ug/section6.html -Terminal.prototype.tabClear = function(params) { - var param = params[0]; - if (param <= 0) { - delete this.tabs[this.x]; - } else if (param === 3) { - this.tabs = {}; - } -}; - -// CSI Pm i Media Copy (MC). -// Ps = 0 -> Print screen (default). -// Ps = 4 -> Turn off printer controller mode. -// Ps = 5 -> Turn on printer controller mode. -// CSI ? Pm i -// Media Copy (MC, DEC-specific). -// Ps = 1 -> Print line containing cursor. -// Ps = 4 -> Turn off autoprint mode. -// Ps = 5 -> Turn on autoprint mode. -// Ps = 1 0 -> Print composed display, ignores DECPEX. -// Ps = 1 1 -> Print all pages. -Terminal.prototype.mediaCopy = function(params) { - ; -}; - -// CSI > Ps; Ps m -// Set or reset resource-values used by xterm to decide whether -// to construct escape sequences holding information about the -// modifiers pressed with a given key. The first parameter iden- -// tifies the resource to set/reset. The second parameter is the -// value to assign to the resource. If the second parameter is -// omitted, the resource is reset to its initial value. -// Ps = 1 -> modifyCursorKeys. -// Ps = 2 -> modifyFunctionKeys. -// Ps = 4 -> modifyOtherKeys. -// If no parameters are given, all resources are reset to their -// initial values. -Terminal.prototype.setResources = function(params) { - ; -}; - -// CSI > Ps n -// Disable modifiers which may be enabled via the CSI > Ps; Ps m -// sequence. This corresponds to a resource value of "-1", which -// cannot be set with the other sequence. The parameter identi- -// fies the resource to be disabled: -// Ps = 1 -> modifyCursorKeys. -// Ps = 2 -> modifyFunctionKeys. -// Ps = 4 -> modifyOtherKeys. -// If the parameter is omitted, modifyFunctionKeys is disabled. -// When modifyFunctionKeys is disabled, xterm uses the modifier -// keys to make an extended sequence of functions rather than -// adding a parameter to each function key to denote the modi- -// fiers. -Terminal.prototype.disableModifiers = function(params) { - ; -}; - -// CSI > Ps p -// Set resource value pointerMode. This is used by xterm to -// decide whether to hide the pointer cursor as the user types. -// Valid values for the parameter: -// Ps = 0 -> never hide the pointer. -// Ps = 1 -> hide if the mouse tracking mode is not enabled. -// Ps = 2 -> always hide the pointer. If no parameter is -// given, xterm uses the default, which is 1 . -Terminal.prototype.setPointerMode = function(params) { - ; -}; - -// CSI ! p Soft terminal reset (DECSTR). -// http://vt100.net/docs/vt220-rm/table4-10.html -Terminal.prototype.softReset = function(params) { - this.cursorHidden = false; - this.insertMode = false; - this.originMode = false; - this.wraparoundMode = false; // autowrap - this.applicationKeypad = false; // ? - this.applicationCursor = false; - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - this.curAttr = this.defAttr; - this.x = this.y = 0; // ? - this.charset = null; - this.glevel = 0; // ?? - this.charsets = [null]; // ?? -}; - -// CSI Ps$ p -// Request ANSI mode (DECRQM). For VT300 and up, reply is -// CSI Ps; Pm$ y -// where Ps is the mode number as in RM, and Pm is the mode -// value: -// 0 - not recognized -// 1 - set -// 2 - reset -// 3 - permanently set -// 4 - permanently reset -Terminal.prototype.requestAnsiMode = function(params) { - ; -}; - -// CSI ? Ps$ p -// Request DEC private mode (DECRQM). For VT300 and up, reply is -// CSI ? Ps; Pm$ p -// where Ps is the mode number as in DECSET, Pm is the mode value -// as in the ANSI DECRQM. -Terminal.prototype.requestPrivateMode = function(params) { - ; -}; - -// CSI Ps ; Ps " p -// Set conformance level (DECSCL). Valid values for the first -// parameter: -// Ps = 6 1 -> VT100. -// Ps = 6 2 -> VT200. -// Ps = 6 3 -> VT300. -// Valid values for the second parameter: -// Ps = 0 -> 8-bit controls. -// Ps = 1 -> 7-bit controls (always set for VT100). -// Ps = 2 -> 8-bit controls. -Terminal.prototype.setConformanceLevel = function(params) { - ; -}; - -// CSI Ps q Load LEDs (DECLL). -// Ps = 0 -> Clear all LEDS (default). -// Ps = 1 -> Light Num Lock. -// Ps = 2 -> Light Caps Lock. -// Ps = 3 -> Light Scroll Lock. -// Ps = 2 1 -> Extinguish Num Lock. -// Ps = 2 2 -> Extinguish Caps Lock. -// Ps = 2 3 -> Extinguish Scroll Lock. -Terminal.prototype.loadLEDs = function(params) { - ; -}; - -// CSI Ps SP q -// Set cursor style (DECSCUSR, VT520). -// Ps = 0 -> blinking block. -// Ps = 1 -> blinking block (default). -// Ps = 2 -> steady block. -// Ps = 3 -> blinking underline. -// Ps = 4 -> steady underline. -Terminal.prototype.setCursorStyle = function(params) { - ; -}; - -// CSI Ps " q -// Select character protection attribute (DECSCA). Valid values -// for the parameter: -// Ps = 0 -> DECSED and DECSEL can erase (default). -// Ps = 1 -> DECSED and DECSEL cannot erase. -// Ps = 2 -> DECSED and DECSEL can erase. -Terminal.prototype.setCharProtectionAttr = function(params) { - ; -}; - -// CSI ? Pm r -// Restore DEC Private Mode Values. The value of Ps previously -// saved is restored. Ps values are the same as for DECSET. -Terminal.prototype.restorePrivateValues = function(params) { - ; -}; - -// CSI Pt; Pl; Pb; Pr; Ps$ r -// Change Attributes in Rectangular Area (DECCARA), VT400 and up. -// Pt; Pl; Pb; Pr denotes the rectangle. -// Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.setAttrInRectangle = function(params) { - var t = params[0] - , l = params[1] - , b = params[2] - , r = params[3] - , attr = params[4]; - - var line - , i; - - for (; t < b + 1; t++) { - line = this.lines[this.ybase + t]; - for (i = l; i < r; i++) { - line[i] = [attr, line[i][1]]; - } - } - - // this.maxRange(); - this.updateRange(params[0]); - this.updateRange(params[2]); -}; - - -// CSI Pc; Pt; Pl; Pb; Pr$ x -// Fill Rectangular Area (DECFRA), VT420 and up. -// Pc is the character to use. -// Pt; Pl; Pb; Pr denotes the rectangle. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.fillRectangle = function(params) { - var ch = params[0] - , t = params[1] - , l = params[2] - , b = params[3] - , r = params[4]; - - var line - , i; - - for (; t < b + 1; t++) { - line = this.lines[this.ybase + t]; - for (i = l; i < r; i++) { - line[i] = [line[i][0], String.fromCharCode(ch)]; - } - } - - // this.maxRange(); - this.updateRange(params[1]); - this.updateRange(params[3]); -}; - -// CSI Ps ; Pu ' z -// Enable Locator Reporting (DECELR). -// Valid values for the first parameter: -// Ps = 0 -> Locator disabled (default). -// Ps = 1 -> Locator enabled. -// Ps = 2 -> Locator enabled for one report, then disabled. -// The second parameter specifies the coordinate unit for locator -// reports. -// Valid values for the second parameter: -// Pu = 0 <- or omitted -> default to character cells. -// Pu = 1 <- device physical pixels. -// Pu = 2 <- character cells. -Terminal.prototype.enableLocatorReporting = function(params) { - var val = params[0] > 0; - //this.mouseEvents = val; - //this.decLocator = val; -}; - -// CSI Pt; Pl; Pb; Pr$ z -// Erase Rectangular Area (DECERA), VT400 and up. -// Pt; Pl; Pb; Pr denotes the rectangle. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.eraseRectangle = function(params) { - var t = params[0] - , l = params[1] - , b = params[2] - , r = params[3]; - - var line - , i - , ch; - - ch = [this.eraseAttr(), ' ']; // xterm? - - for (; t < b + 1; t++) { - line = this.lines[this.ybase + t]; - for (i = l; i < r; i++) { - line[i] = ch; - } - } - - // this.maxRange(); - this.updateRange(params[0]); - this.updateRange(params[2]); -}; - - -// CSI P m SP } -// Insert P s Column(s) (default = 1) (DECIC), VT420 and up. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.insertColumns = function() { - var param = params[0] - , l = this.ybase + this.rows - , ch = [this.eraseAttr(), ' '] // xterm? - , i; - - while (param--) { - for (i = this.ybase; i < l; i++) { - this.lines[i].splice(this.x + 1, 0, ch); - this.lines[i].pop(); - } - } - - this.maxRange(); -}; - -// CSI P m SP ~ -// Delete P s Column(s) (default = 1) (DECDC), VT420 and up -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.deleteColumns = function() { - var param = params[0] - , l = this.ybase + this.rows - , ch = [this.eraseAttr(), ' '] // xterm? - , i; - - while (param--) { - for (i = this.ybase; i < l; i++) { - this.lines[i].splice(this.x, 1); - this.lines[i].push(ch); - } - } - - this.maxRange(); -}; - - -Terminal.prototype.copyBuffer = function(lines) { - var lines = lines || this.lines - , out = []; - - for (var y = 0; y < lines.length; y++) { - out[y] = []; - for (var x = 0; x < lines[y].length; x++) { - out[y][x] = [lines[y][x][0], lines[y][x][1]]; - } - } - - return out; -}; - -Terminal.prototype.getCopyTextarea = function(text) { - var textarea = this._copyTextarea - , document = this.document; - - if (!textarea) { - textarea = document.createElement('textarea'); - textarea.style.position = 'absolute'; - textarea.style.left = '-32000px'; - textarea.style.top = '-32000px'; - textarea.style.width = '0px'; - textarea.style.height = '0px'; - textarea.style.opacity = '0'; - textarea.style.backgroundColor = 'transparent'; - textarea.style.borderStyle = 'none'; - textarea.style.outlineStyle = 'none'; - - document.getElementsByTagName('body')[0].appendChild(textarea); - - this._copyTextarea = textarea; - } - - return textarea; -}; - -// NOTE: Only works for primary selection on X11. -// Non-X11 users should use Ctrl-C instead. -Terminal.prototype.copyText = function(text) { - var self = this - , textarea = this.getCopyTextarea(); - - this.emit('copy', text); - - textarea.focus(); - textarea.textContent = text; - textarea.value = text; - textarea.setSelectionRange(0, text.length); - - setTimeout(function() { - self.element.focus(); - self.focus(); - }, 1); -}; - -Terminal.prototype.keyPrefix = function(ev, key) { - if (key === 'k' || key === '&') { - this.destroy(); - } else if (key === 'p' || key === ']') { - this.emit('request paste'); - } else if (key === 'c') { - this.emit('request create'); - } else if (key >= '0' && key <= '9') { - key = +key - 1; - if (!~key) key = 9; - this.emit('request term', key); - } else if (key === 'n') { - this.emit('request term next'); - } else if (key === 'P') { - this.emit('request term previous'); - } else if (key === ':') { - this.emit('request command mode'); - } -}; - -Terminal.prototype.keySelect = function(ev, key) { - this.showCursor(); - - if (key === '\x04') { // ctrl-d - var y = this.ydisp + this.y; - if (this.ydisp === this.ybase) { - // Mimic vim behavior - this.y = Math.min(this.y + (this.rows - 1) / 2 | 0, this.rows - 1); this.refresh(0, this.rows - 1); - } else { - this.scrollDisp((this.rows - 1) / 2 | 0); - } - return; - } - if (key === '\x15') { // ctrl-u - var y = this.ydisp + this.y; - if (this.ydisp === 0) { - // Mimic vim behavior - this.y = Math.max(this.y - (this.rows - 1) / 2 | 0, 0); + this.normal = null; + + this.emit('resize', {terminal: this, cols: x, rows: y}); + }; + + Terminal.prototype.updateRange = function(y) { + if (y < this.refreshStart) this.refreshStart = y; + if (y > this.refreshEnd) this.refreshEnd = y; + // if (y > this.refreshEnd) { + // this.refreshEnd = y; + // if (y > this.rows - 1) { + // this.refreshEnd = this.rows - 1; + // } + // } + }; + + Terminal.prototype.maxRange = function() { + this.refreshStart = 0; + this.refreshEnd = this.rows - 1; + }; + + Terminal.prototype.setupStops = function(i) { + if (i != null) { + if (!this.tabs[i]) { + i = this.prevStop(i); + } + } else { + this.tabs = {}; + i = 0; + } + + for (; i < this.cols; i += 8) { + this.tabs[i] = true; + } + }; + + Terminal.prototype.prevStop = function(x) { + if (x == null) x = this.x; + while (!this.tabs[--x] && x > 0); + return x >= this.cols + ? this.cols - 1 + : x < 0 ? 0 : x; + }; + + Terminal.prototype.nextStop = function(x) { + if (x == null) x = this.x; + while (!this.tabs[++x] && x < this.cols); + return x >= this.cols + ? this.cols - 1 + : x < 0 ? 0 : x; + }; + + Terminal.prototype.eraseRight = function(x, y) { + var line = this.lines[this.ybase + y] + , ch = [this.eraseAttr(), ' ']; // xterm + + + for (; x < this.cols; x++) { + line[x] = ch; + } + + this.updateRange(y); + }; + + Terminal.prototype.eraseLeft = function(x, y) { + var line = this.lines[this.ybase + y] + , ch = [this.eraseAttr(), ' ']; // xterm + + x++; + while (x--) line[x] = ch; + + this.updateRange(y); + }; + + Terminal.prototype.eraseLine = function(y) { + this.eraseRight(0, y); + }; + + Terminal.prototype.blankLine = function(cur) { + var attr = cur + ? this.eraseAttr() + : this.defAttr; + + var ch = [attr, ' '] + , line = [] + , i = 0; + + for (; i < this.cols; i++) { + line[i] = ch; + } + + return line; + }; + + Terminal.prototype.ch = function(cur) { + return cur + ? [this.eraseAttr(), ' '] + : [this.defAttr, ' ']; + }; + + Terminal.prototype.is = function(term) { + var name = this.termName; + return (name + '').indexOf(term) === 0; + }; + + Terminal.prototype.handler = function(data) { + this.emit('data', data); + }; + + Terminal.prototype.handleTitle = function(title) { + this.emit('title', title); + }; + + /** + * ESC + */ + + // ESC D Index (IND is 0x84). + Terminal.prototype.index = function() { + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + 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++; + // possibly move the code below to term.reverseScroll(); + // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + // blankLine(true) is xterm/linux behavior + this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); + j = this.rows - 1 - this.scrollBottom; + this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); + // this.maxRange(); + this.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + } + this.state = normal; + }; + + // ESC c Full Reset (RIS). + Terminal.prototype.reset = function() { + this.options.rows = this.rows; + this.options.cols = this.cols; + Terminal.call(this, this.options); this.refresh(0, this.rows - 1); - } else { - this.scrollDisp(-(this.rows - 1) / 2 | 0); - } - return; - } + }; - if (key === '\x06') { // ctrl-f - var y = this.ydisp + this.y; - this.scrollDisp(this.rows - 1); - return; - } + // ESC H Tab Set (HTS is 0x88). + Terminal.prototype.tabSet = function() { + this.tabs[this.x] = true; + this.state = normal; + }; - if (key === '\x02') { // ctrl-b - var y = this.ydisp + this.y; - this.scrollDisp(-(this.rows - 1)); - return; - } + /** + * CSI + */ - if (key === 'k' || key === '\x1b[A') { - var y = this.ydisp + this.y; - this.y--; - if (this.y < 0) { - this.y = 0; - this.scrollDisp(-1); - } - this.refresh(this.y, this.y + 1); - return; - } + // CSI Ps A + // Cursor Up Ps Times (default = 1) (CUU). + Terminal.prototype.cursorUp = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y -= param; + if (this.y < 0) this.y = 0; + }; - if (key === 'j' || key === '\x1b[B') { - var y = this.ydisp + this.y; - this.y++; - if (this.y >= this.rows) { - this.y = this.rows - 1; - this.scrollDisp(1); - } - this.refresh(this.y - 1, this.y); - return; - } + // CSI Ps B + // Cursor Down Ps Times (default = 1) (CUD). + Terminal.prototype.cursorDown = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + }; - if (key === 'h' || key === '\x1b[D') { - var x = this.x; - this.x--; - if (this.x < 0) { - this.x = 0; - } - this.refresh(this.y, this.y); - return; - } + // CSI Ps C + // Cursor Forward Ps Times (default = 1) (CUF). + Terminal.prototype.cursorForward = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.x += param; + if (this.x >= this.cols) { + this.x = this.cols - 1; + } + }; - if (key === 'l' || key === '\x1b[C') { - var x = this.x; - this.x++; - if (this.x >= this.cols) { - this.x = this.cols - 1; - } - this.refresh(this.y, this.y); - return; - } + // CSI Ps D + // Cursor Backward Ps Times (default = 1) (CUB). + Terminal.prototype.cursorBackward = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.x -= param; + if (this.x < 0) this.x = 0; + }; - if (key === 'w' || key === 'W') { - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; + // CSI Ps ; Ps H + // Cursor Position [row;column] (default = [1,1]) (CUP). + Terminal.prototype.cursorPos = function(params) { + var row, col; - var x = this.x; - var y = this.y; - var yb = this.ydisp; - var saw_space = false; + row = params[0] - 1; - for (;;) { - var line = this.lines[yb + y]; - while (x < this.cols) { - if (line[x][1] <= ' ') { - saw_space = true; - } else if (saw_space) { + 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 j; + switch (params[0]) { + case 0: + this.eraseRight(this.x, this.y); + j = this.y + 1; + for (; j < this.rows; j++) { + this.eraseLine(j); + } break; - } - x++; - } - if (x >= this.cols) x = this.cols - 1; - if (x === this.cols - 1 && line[x][1] <= ' ') { - x = 0; - if (++y >= this.rows) { - y--; - if (++yb > this.ybase) { - yb = this.ybase; - x = this.x; - break; + case 1: + this.eraseLeft(this.x, this.y); + j = this.y; + while (j--) { + this.eraseLine(j); } - } - continue; + break; + case 2: + j = this.rows; + while (j--) this.eraseLine(j); + break; + case 3: + ; // no saved lines + break; } - break; - } + }; - this.x = x, this.y = y; - this.scrollDisp(-this.ydisp + yb); + // 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]) { + case 0: + this.eraseRight(this.x, this.y); + break; + case 1: + this.eraseLeft(this.x, this.y); + break; + case 2: + this.eraseLine(this.y); + break; + } + }; - return; - } + // 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 (key === 'b' || key === 'B') { - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; + // 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. - var x = this.x; - var y = this.y; - var yb = this.ydisp; + // 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. - for (;;) { - var line = this.lines[yb + y]; - var saw_space = x > 0 && line[x][1] > ' ' && line[x - 1][1] > ' '; - while (x >= 0) { - if (line[x][1] <= ' ') { - if (saw_space && (x + 1 < this.cols && line[x + 1][1] > ' ')) { - x++; - break; - } else { - saw_space = true; + // 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) { + // Optimize a single SGR0. + if (params.length === 1 && params[0] === 0) { + this.curAttr = this.defAttr; + return; + } + + var l = params.length + , i = 0 + , flags = this.curAttr >> 18 + , fg = (this.curAttr >> 9) & 0x1ff + , bg = this.curAttr & 0x1ff + , p; + + for (; i < l; i++) { + p = params[i]; + if (p >= 30 && p <= 37) { + // fg color 8 + fg = p - 30; + } else if (p >= 40 && p <= 47) { + // bg color 8 + bg = p - 40; + } else if (p >= 90 && p <= 97) { + // fg color 16 + p += 8; + fg = p - 90; + } else if (p >= 100 && p <= 107) { + // bg color 16 + p += 8; + bg = p - 100; + } else if (p === 0) { + // default + flags = this.defAttr >> 18; + fg = (this.defAttr >> 9) & 0x1ff; + bg = this.defAttr & 0x1ff; + // flags = 0; + // fg = 0x1ff; + // bg = 0x1ff; + } else if (p === 1) { + // bold text + flags |= 1; + } else if (p === 4) { + // underlined text + flags |= 2; + } else if (p === 5) { + // blink + flags |= 4; + } else if (p === 7) { + // inverse and positive + // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + flags |= 8; + } else if (p === 8) { + // invisible + flags |= 16; + } else if (p === 22) { + // not bold + flags &= ~1; + } else if (p === 24) { + // not underlined + flags &= ~2; + } else if (p === 25) { + // not blink + flags &= ~4; + } else if (p === 27) { + // not inverse + flags &= ~8; + } else if (p === 28) { + // not invisible + flags &= ~16; + } else if (p === 39) { + // reset fg + fg = (this.defAttr >> 9) & 0x1ff; + } else if (p === 49) { + // reset bg + bg = this.defAttr & 0x1ff; + } else if (p === 38) { + // fg color 256 + if (params[i + 1] === 2) { + i += 2; + fg = matchColor( + params[i] & 0xff, + params[i + 1] & 0xff, + params[i + 2] & 0xff); + if (fg === -1) fg = 0x1ff; + i += 2; + } else if (params[i + 1] === 5) { + i += 2; + p = params[i] & 0xff; + fg = p; } - } - x--; - } - if (x < 0) x = 0; - if (x === 0 && (line[x][1] <= ' ' || !saw_space)) { - x = this.cols - 1; - if (--y < 0) { - y++; - if (--yb < 0) { - yb++; - x = 0; - break; + } else if (p === 48) { + // bg color 256 + if (params[i + 1] === 2) { + i += 2; + bg = matchColor( + params[i] & 0xff, + params[i + 1] & 0xff, + params[i + 2] & 0xff); + if (bg === -1) bg = 0x1ff; + i += 2; + } else if (params[i + 1] === 5) { + i += 2; + p = params[i] & 0xff; + bg = p; } - } - continue; - } - break; - } - - this.x = x, this.y = y; - this.scrollDisp(-this.ydisp + yb); - - return; - } - - if (key === 'e' || key === 'E') { - var x = this.x + 1; - var y = this.y; - var yb = this.ydisp; - if (x >= this.cols) x--; - - for (;;) { - var line = this.lines[yb + y]; - while (x < this.cols) { - if (line[x][1] <= ' ') { - x++; + } else if (p === 100) { + // reset fg/bg + fg = (this.defAttr >> 9) & 0x1ff; + bg = this.defAttr & 0x1ff; } else { - break; + this.error('Unknown SGR attribute: %d.', p); } } - while (x < this.cols) { - if (line[x][1] <= ' ') { - if (x - 1 >= 0 && line[x - 1][1] > ' ') { - x--; + + this.curAttr = (flags << 18) | (fg << 9) | bg; + }; + + // 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) { + switch (params[0]) { + case 5: + // status report + this.send('\x1b[0n'); break; - } + case 6: + // cursor position + this.send('\x1b[' + + (this.y + 1) + + ';' + + (this.x + 1) + + 'R'); + break; } - x++; - } - if (x >= this.cols) x = this.cols - 1; - if (x === this.cols - 1 && line[x][1] <= ' ') { - x = 0; - if (++y >= this.rows) { - y--; - if (++yb > this.ybase) { - yb = this.ybase; + } else if (this.prefix === '?') { + // modern xterm doesnt seem to + // respond to any of these except ?6, 6, and 5 + switch (params[0]) { + case 6: + // cursor position + this.send('\x1b[?' + + (this.y + 1) + + ';' + + (this.x + 1) + + 'R'); + break; + case 15: + // no printer + // this.send('\x1b[?11n'); + break; + case 25: + // dont support user defined keys + // this.send('\x1b[?21n'); + break; + case 26: + // north american keyboard + // this.send('\x1b[?27;1;0;0n'); + break; + case 53: + // no dec locator/mouse + // this.send('\x1b[?50n'); break; - } } - continue; } - break; - } + }; - this.x = x, this.y = y; - this.scrollDisp(-this.ydisp + yb); + /** + * Additions + */ - return; - } + // CSI Ps @ + // Insert Ps (Blank) Character(s) (default = 1) (ICH). + Terminal.prototype.insertChars = function(params) { + var param, row, j, ch; - if (key === '^' || key === '0') { - var ox = this.x; + param = params[0]; + if (param < 1) param = 1; - if (key === '0') { + row = this.y + this.ybase; + j = this.x; + ch = [this.eraseAttr(), ' ']; // xterm + + while (param-- && j < this.cols) { + this.lines[row].splice(j++, 0, ch); + this.lines[row].pop(); + } + }; + + // CSI Ps E + // Cursor Next Line Ps Times (default = 1) (CNL). + // same as CSI Ps B ? + Terminal.prototype.cursorNextLine = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } this.x = 0; - } else if (key === '^') { - var line = this.lines[this.ydisp + this.y]; - var x = 0; - while (x < this.cols) { - if (line[x][1] > ' ') { - break; + }; + + // CSI Ps F + // Cursor Preceding Line Ps Times (default = 1) (CNL). + // reuse CSI Ps A ? + Terminal.prototype.cursorPrecedingLine = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y -= param; + if (this.y < 0) this.y = 0; + this.x = 0; + }; + + // CSI Ps G + // Cursor Character Absolute [column] (default = [row,1]) (CHA). + Terminal.prototype.cursorCharAbsolute = function(params) { + var 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; + j = this.rows - 1 + this.ybase - j + 1; + + while (param--) { + // test: echo -e '\e[44m\e[1L\e[0m' + // blankLine(true) - xterm/linux behavior + this.lines.splice(row, 0, this.blankLine(true)); + this.lines.splice(j, 1); + } + + // this.maxRange(); + this.updateRange(this.y); + this.updateRange(this.scrollBottom); + }; + + // 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--) { + // test: echo -e '\e[44m\e[1M\e[0m' + // blankLine(true) - xterm/linux behavior + this.lines.splice(j + 1, 0, this.blankLine(true)); + this.lines.splice(row, 1); + } + + // this.maxRange(); + this.updateRange(this.y); + this.updateRange(this.scrollBottom); + }; + + // CSI Ps P + // Delete Ps Character(s) (default = 1) (DCH). + Terminal.prototype.deleteChars = function(params) { + var param, row, ch; + + param = params[0]; + if (param < 1) param = 1; + + row = this.y + this.ybase; + ch = [this.eraseAttr(), ' ']; // xterm + + while (param--) { + this.lines[row].splice(this.x, 1); + this.lines[row].push(ch); + } + }; + + // CSI Ps X + // Erase Ps Character(s) (default = 1) (ECH). + Terminal.prototype.eraseChars = function(params) { + var param, row, j, ch; + + param = params[0]; + if (param < 1) param = 1; + + row = this.y + this.ybase; + j = this.x; + ch = [this.eraseAttr(), ' ']; // xterm + + while (param-- && j < this.cols) { + this.lines[row][j++] = ch; + } + }; + + // CSI Pm ` Character Position Absolute + // [column] (default = [row,1]) (HPA). + Terminal.prototype.charPosAbsolute = function(params) { + var 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 + // reuse CSI Ps C ? + Terminal.prototype.HPositionRelative = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.x += param; + if (this.x >= this.cols) { + this.x = this.cols - 1; + } + }; + + // 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. + // More information: + // xterm/charproc.c - line 2012, for more information. + // vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) + Terminal.prototype.sendDeviceAttributes = function(params) { + if (params[0] > 0) return; + + if (!this.prefix) { + if (this.is('xterm') + || this.is('rxvt-unicode') + || this.is('screen')) { + this.send('\x1b[?1;2c'); + } else if (this.is('linux')) { + this.send('\x1b[?6c'); + } + } else if (this.prefix === '>') { + // xterm and urxvt + // seem to spit this + // out around ~370 times (?). + if (this.is('xterm')) { + this.send('\x1b[>0;276;0c'); + } else if (this.is('rxvt-unicode')) { + this.send('\x1b[>85;95;0c'); + } else if (this.is('linux')) { + // not supported by linux console. + // linux console echoes parameters. + this.send(params[0] + 'c'); + } else if (this.is('screen')) { + this.send('\x1b[>83;40003;0c'); } - x++; } - if (x >= this.cols) x = this.cols - 1; - this.x = x; - } + }; - this.refresh(this.y, this.y); - return; - } - - if (key === '$') { - var ox = this.x; - var line = this.lines[this.ydisp + this.y]; - var x = this.cols - 1; - while (x >= 0) { - x--; - } - if (x < 0) x = 0; - this.x = x; - this.refresh(this.y, this.y); - return; - } - - if (key === 'g' || key === 'G') { - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; - if (key === 'g') { - this.x = 0, this.y = 0; - this.scrollDisp(-this.ydisp); - } else if (key === 'G') { - this.x = 0, this.y = this.rows - 1; - this.scrollDisp(this.ybase); - } - return; - } - - if (key === 'H' || key === 'M' || key === 'L') { - var ox = this.x; - var oy = this.y; - if (key === 'H') { - this.x = 0, this.y = 0; - } else if (key === 'M') { - this.x = 0, this.y = this.rows / 2 | 0; - } else if (key === 'L') { - this.x = 0, this.y = this.rows - 1; - } - this.refresh(oy, oy); - this.refresh(this.y, this.y); - return; - } - - if (key === '{' || key === '}') { - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; - - var line; - var saw_full = false; - var found = false; - var first_is_space = -1; - var y = this.y + (key === '{' ? -1 : 1); - var yb = this.ydisp; - var i; - - if (key === '{') { - if (y < 0) { - y++; - if (yb > 0) yb--; + // CSI Pm d + // Line Position Absolute [row] (default = [1,column]) (VPA). + Terminal.prototype.linePosAbsolute = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y = param - 1; + if (this.y >= this.rows) { + this.y = this.rows - 1; } - } else if (key === '}') { - if (y >= this.rows) { - y--; - if (yb < this.ybase) yb++; + }; + + // 145 65 e * VPR - Vertical Position Relative + // reuse CSI Ps B ? + Terminal.prototype.VPositionRelative = function(params) { + var param = params[0]; + if (param < 1) param = 1; + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; } - } + }; - for (;;) { - line = this.lines[yb + y]; + // 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; - for (i = 0; i < this.cols; i++) { - if (line[i][1] > ' ') { - if (first_is_space === -1) { - first_is_space = 0; + 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') { + var l = params.length + , i = 0; + + for (; i < l; i++) { + this.setMode(params[i]); + } + + return; + } + + if (!this.prefix) { + switch (params) { + case 4: + this.insertMode = true; + break; + case 20: + //this.convertEol = true; + break; + } + } else if (this.prefix === '?') { + switch (params) { + case 1: + this.applicationCursor = true; + break; + case 2: + this.setgCharset(0, Terminal.charsets.US); + this.setgCharset(1, Terminal.charsets.US); + this.setgCharset(2, Terminal.charsets.US); + this.setgCharset(3, Terminal.charsets.US); + // set VT100 mode here + break; + case 3: // 132 col mode + this.savedCols = this.cols; + this.resize(132, this.rows); + break; + case 6: + this.originMode = true; + break; + case 7: + this.wraparoundMode = true; + break; + case 12: + // this.cursorBlink = true; + break; + case 66: + this.log('Serial port requested application keypad.'); + this.applicationKeypad = true; + break; + case 9: // X10 Mouse + // no release, no motion, no wheel, no modifiers. + case 1000: // vt200 mouse + // no motion. + // no modifiers, except control on the wheel. + case 1002: // button event mouse + case 1003: // any event mouse + // any event - sends motion events, + // even if there is no button held down. + this.x10Mouse = params === 9; + this.vt200Mouse = params === 1000; + this.normalMouse = params > 1000; + this.mouseEvents = true; + this.element.style.cursor = 'default'; + this.log('Binding to mouse events.'); + break; + case 1004: // send focusin/focusout events + // focusin: ^[[I + // focusout: ^[[O + this.sendFocus = true; + break; + case 1005: // utf8 ext mode mouse + this.utfMouse = true; + // for wide terminals + // simply encodes large values as utf8 characters + break; + case 1006: // sgr ext mode mouse + this.sgrMouse = true; + // for wide terminals + // does not add 32 to fields + // press: ^[[ 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 params === 'object') { + var l = params.length + , i = 0; + + for (; i < l; i++) { + this.resetMode(params[i]); + } + + return; + } + + if (!this.prefix) { + switch (params) { + case 4: + this.insertMode = false; + break; + case 20: + //this.convertEol = false; + break; + } + } else if (this.prefix === '?') { + switch (params) { + case 1: + this.applicationCursor = false; + break; + case 3: + if (this.cols === 132 && this.savedCols) { + this.resize(this.savedCols, this.rows); + } + delete this.savedCols; + break; + case 6: + this.originMode = false; + break; + case 7: + this.wraparoundMode = false; + break; + case 12: + // this.cursorBlink = false; + break; + case 66: + this.log('Switching back to normal keypad.'); + this.applicationKeypad = false; + break; + case 9: // X10 Mouse + case 1000: // vt200 mouse + case 1002: // button event mouse + case 1003: // any event mouse + this.x10Mouse = false; + this.vt200Mouse = false; + this.normalMouse = false; + this.mouseEvents = false; + this.element.style.cursor = ''; + break; + case 1004: // send focusin/focusout events + this.sendFocus = false; + break; + case 1005: // utf8 ext mode mouse + this.utfMouse = false; + break; + case 1006: // sgr ext mode mouse + this.sgrMouse = false; + break; + case 1015: // urxvt ext mode mouse + this.urxvtMouse = false; + break; + case 25: // hide cursor + this.cursorHidden = true; + break; + case 1049: // alt screen buffer cursor + ; // FALL-THROUGH + case 47: // normal screen buffer + case 1047: // normal screen buffer - clearing it first + if (this.normal) { + this.lines = this.normal.lines; + this.ybase = this.normal.ybase; + this.ydisp = this.normal.ydisp; + this.x = this.normal.x; + this.y = this.normal.y; + this.scrollTop = this.normal.scrollTop; + this.scrollBottom = this.normal.scrollBottom; + this.tabs = this.normal.tabs; + this.normal = null; + // if (params === 1049) { + // this.x = this.savedX; + // this.y = this.savedY; + // } + this.refresh(0, this.rows - 1); + this.showCursor(); + } + break; + } + } + }; + + // CSI Ps ; Ps r + // Set Scrolling Region [top;bottom] (default = full size of win- + // dow) (DECSTBM). + // CSI ? Pm r + Terminal.prototype.setScrollRegion = function(params) { + if (this.prefix) return; + this.scrollTop = (params[0] || 1) - 1; + this.scrollBottom = (params[1] || this.rows) - 1; + this.x = 0; + this.y = 0; + }; + + // CSI s + // Save cursor (ANSI.SYS). + Terminal.prototype.saveCursor = function(params) { + this.savedX = this.x; + this.savedY = this.y; + }; + + // CSI u + // Restore cursor (ANSI.SYS). + Terminal.prototype.restoreCursor = function(params) { + this.x = this.savedX || 0; + this.y = this.savedY || 0; + }; + + /** + * Lesser Used + */ + + // CSI Ps I + // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). + Terminal.prototype.cursorForwardTab = function(params) { + var param = params[0] || 1; + while (param--) { + this.x = this.nextStop(); + } + }; + + // CSI Ps S Scroll up Ps lines (default = 1) (SU). + Terminal.prototype.scrollUp = function(params) { + var param = params[0] || 1; + while (param--) { + this.lines.splice(this.ybase + this.scrollTop, 1); + this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); + } + // this.maxRange(); + this.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; + + // CSI Ps T Scroll down Ps lines (default = 1) (SD). + Terminal.prototype.scrollDown = function(params) { + var param = params[0] || 1; + while (param--) { + this.lines.splice(this.ybase + this.scrollBottom, 1); + this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); + } + // this.maxRange(); + this.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; + + // CSI Ps ; Ps ; Ps ; Ps ; Ps T + // Initiate highlight mouse tracking. Parameters are + // [func;startx;starty;firstrow;lastrow]. See the section Mouse + // Tracking. + Terminal.prototype.initMouseTracking = function(params) { + // Relevant: DECSET 1001 + }; + + // CSI > Ps; Ps T + // Reset one or more features of the title modes to the default + // value. Normally, "reset" disables the feature. It is possi- + // ble to disable the ability to reset features by compiling a + // different default for the title modes into xterm. + // Ps = 0 -> Do not set window/icon labels using hexadecimal. + // Ps = 1 -> Do not query window/icon labels using hexadeci- + // mal. + // Ps = 2 -> Do not set window/icon labels using UTF-8. + // Ps = 3 -> Do not query window/icon labels using UTF-8. + // (See discussion of "Title Modes"). + Terminal.prototype.resetTitleModes = function(params) { + ; + }; + + // CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + Terminal.prototype.cursorBackwardTab = function(params) { + var param = params[0] || 1; + while (param--) { + this.x = this.prevStop(); + } + }; + + // CSI Ps b Repeat the preceding graphic character Ps times (REP). + Terminal.prototype.repeatPrecedingCharacter = function(params) { + var param = params[0] || 1 + , line = this.lines[this.ybase + this.y] + , ch = line[this.x - 1] || [this.defAttr, ' ']; + + while (param--) line[this.x++] = ch; + }; + + // CSI Ps g Tab Clear (TBC). + // Ps = 0 -> Clear Current Column (default). + // Ps = 3 -> Clear All. + // Potentially: + // Ps = 2 -> Clear Stops on Line. + // http://vt100.net/annarbor/aaa-ug/section6.html + Terminal.prototype.tabClear = function(params) { + var param = params[0]; + if (param <= 0) { + delete this.tabs[this.x]; + } else if (param === 3) { + this.tabs = {}; + } + }; + + // CSI Pm i Media Copy (MC). + // Ps = 0 -> Print screen (default). + // Ps = 4 -> Turn off printer controller mode. + // Ps = 5 -> Turn on printer controller mode. + // CSI ? Pm i + // Media Copy (MC, DEC-specific). + // Ps = 1 -> Print line containing cursor. + // Ps = 4 -> Turn off autoprint mode. + // Ps = 5 -> Turn on autoprint mode. + // Ps = 1 0 -> Print composed display, ignores DECPEX. + // Ps = 1 1 -> Print all pages. + Terminal.prototype.mediaCopy = function(params) { + ; + }; + + // CSI > Ps; Ps m + // Set or reset resource-values used by xterm to decide whether + // to construct escape sequences holding information about the + // modifiers pressed with a given key. The first parameter iden- + // tifies the resource to set/reset. The second parameter is the + // value to assign to the resource. If the second parameter is + // omitted, the resource is reset to its initial value. + // Ps = 1 -> modifyCursorKeys. + // Ps = 2 -> modifyFunctionKeys. + // Ps = 4 -> modifyOtherKeys. + // If no parameters are given, all resources are reset to their + // initial values. + Terminal.prototype.setResources = function(params) { + ; + }; + + // CSI > Ps n + // Disable modifiers which may be enabled via the CSI > Ps; Ps m + // sequence. This corresponds to a resource value of "-1", which + // cannot be set with the other sequence. The parameter identi- + // fies the resource to be disabled: + // Ps = 1 -> modifyCursorKeys. + // Ps = 2 -> modifyFunctionKeys. + // Ps = 4 -> modifyOtherKeys. + // If the parameter is omitted, modifyFunctionKeys is disabled. + // When modifyFunctionKeys is disabled, xterm uses the modifier + // keys to make an extended sequence of functions rather than + // adding a parameter to each function key to denote the modi- + // fiers. + Terminal.prototype.disableModifiers = function(params) { + ; + }; + + // CSI > Ps p + // Set resource value pointerMode. This is used by xterm to + // decide whether to hide the pointer cursor as the user types. + // Valid values for the parameter: + // Ps = 0 -> never hide the pointer. + // Ps = 1 -> hide if the mouse tracking mode is not enabled. + // Ps = 2 -> always hide the pointer. If no parameter is + // given, xterm uses the default, which is 1 . + Terminal.prototype.setPointerMode = function(params) { + ; + }; + + // CSI ! p Soft terminal reset (DECSTR). + // http://vt100.net/docs/vt220-rm/table4-10.html + Terminal.prototype.softReset = function(params) { + this.cursorHidden = false; + this.insertMode = false; + this.originMode = false; + this.wraparoundMode = false; // autowrap + this.applicationKeypad = false; // ? + this.applicationCursor = false; + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + this.curAttr = this.defAttr; + this.x = this.y = 0; // ? + this.charset = null; + this.glevel = 0; // ?? + this.charsets = [null]; // ?? + }; + + // CSI Ps$ p + // Request ANSI mode (DECRQM). For VT300 and up, reply is + // CSI Ps; Pm$ y + // where Ps is the mode number as in RM, and Pm is the mode + // value: + // 0 - not recognized + // 1 - set + // 2 - reset + // 3 - permanently set + // 4 - permanently reset + Terminal.prototype.requestAnsiMode = function(params) { + ; + }; + + // CSI ? Ps$ p + // Request DEC private mode (DECRQM). For VT300 and up, reply is + // CSI ? Ps; Pm$ p + // where Ps is the mode number as in DECSET, Pm is the mode value + // as in the ANSI DECRQM. + Terminal.prototype.requestPrivateMode = function(params) { + ; + }; + + // CSI Ps ; Ps " p + // Set conformance level (DECSCL). Valid values for the first + // parameter: + // Ps = 6 1 -> VT100. + // Ps = 6 2 -> VT200. + // Ps = 6 3 -> VT300. + // Valid values for the second parameter: + // Ps = 0 -> 8-bit controls. + // Ps = 1 -> 7-bit controls (always set for VT100). + // Ps = 2 -> 8-bit controls. + Terminal.prototype.setConformanceLevel = function(params) { + ; + }; + + // CSI Ps q Load LEDs (DECLL). + // Ps = 0 -> Clear all LEDS (default). + // Ps = 1 -> Light Num Lock. + // Ps = 2 -> Light Caps Lock. + // Ps = 3 -> Light Scroll Lock. + // Ps = 2 1 -> Extinguish Num Lock. + // Ps = 2 2 -> Extinguish Caps Lock. + // Ps = 2 3 -> Extinguish Scroll Lock. + Terminal.prototype.loadLEDs = function(params) { + ; + }; + + // CSI Ps SP q + // Set cursor style (DECSCUSR, VT520). + // Ps = 0 -> blinking block. + // Ps = 1 -> blinking block (default). + // Ps = 2 -> steady block. + // Ps = 3 -> blinking underline. + // Ps = 4 -> steady underline. + Terminal.prototype.setCursorStyle = function(params) { + ; + }; + + // CSI Ps " q + // Select character protection attribute (DECSCA). Valid values + // for the parameter: + // Ps = 0 -> DECSED and DECSEL can erase (default). + // Ps = 1 -> DECSED and DECSEL cannot erase. + // Ps = 2 -> DECSED and DECSEL can erase. + Terminal.prototype.setCharProtectionAttr = function(params) { + ; + }; + + // CSI ? Pm r + // Restore DEC Private Mode Values. The value of Ps previously + // saved is restored. Ps values are the same as for DECSET. + Terminal.prototype.restorePrivateValues = function(params) { + ; + }; + + // CSI Pt; Pl; Pb; Pr; Ps$ r + // Change Attributes in Rectangular Area (DECCARA), VT400 and up. + // Pt; Pl; Pb; Pr denotes the rectangle. + // Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. + // NOTE: xterm doesn't enable this code by default. + Terminal.prototype.setAttrInRectangle = function(params) { + var t = params[0] + , l = params[1] + , b = params[2] + , r = params[3] + , attr = params[4]; + + var line + , i; + + for (; t < b + 1; t++) { + line = this.lines[this.ybase + t]; + for (i = l; i < r; i++) { + line[i] = [attr, line[i][1]]; + } + } + + // this.maxRange(); + this.updateRange(params[0]); + this.updateRange(params[2]); + }; + + + // CSI Pc; Pt; Pl; Pb; Pr$ x + // Fill Rectangular Area (DECFRA), VT420 and up. + // Pc is the character to use. + // Pt; Pl; Pb; Pr denotes the rectangle. + // NOTE: xterm doesn't enable this code by default. + Terminal.prototype.fillRectangle = function(params) { + var ch = params[0] + , t = params[1] + , l = params[2] + , b = params[3] + , r = params[4]; + + var line + , i; + + for (; t < b + 1; t++) { + line = this.lines[this.ybase + t]; + for (i = l; i < r; i++) { + line[i] = [line[i][0], String.fromCharCode(ch)]; + } + } + + // this.maxRange(); + this.updateRange(params[1]); + this.updateRange(params[3]); + }; + + // CSI Ps ; Pu ' z + // Enable Locator Reporting (DECELR). + // Valid values for the first parameter: + // Ps = 0 -> Locator disabled (default). + // Ps = 1 -> Locator enabled. + // Ps = 2 -> Locator enabled for one report, then disabled. + // The second parameter specifies the coordinate unit for locator + // reports. + // Valid values for the second parameter: + // Pu = 0 <- or omitted -> default to character cells. + // Pu = 1 <- device physical pixels. + // Pu = 2 <- character cells. + Terminal.prototype.enableLocatorReporting = function(params) { + var val = params[0] > 0; + //this.mouseEvents = val; + //this.decLocator = val; + }; + + // CSI Pt; Pl; Pb; Pr$ z + // Erase Rectangular Area (DECERA), VT400 and up. + // Pt; Pl; Pb; Pr denotes the rectangle. + // NOTE: xterm doesn't enable this code by default. + Terminal.prototype.eraseRectangle = function(params) { + var t = params[0] + , l = params[1] + , b = params[2] + , r = params[3]; + + var line + , i + , ch; + + ch = [this.eraseAttr(), ' ']; // xterm? + + for (; t < b + 1; t++) { + line = this.lines[this.ybase + t]; + for (i = l; i < r; i++) { + line[i] = ch; + } + } + + // this.maxRange(); + this.updateRange(params[0]); + this.updateRange(params[2]); + }; + + + // CSI P m SP } + // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. + // NOTE: xterm doesn't enable this code by default. + Terminal.prototype.insertColumns = function() { + var param = params[0] + , l = this.ybase + this.rows + , ch = [this.eraseAttr(), ' '] // xterm? + , i; + + while (param--) { + for (i = this.ybase; i < l; i++) { + this.lines[i].splice(this.x + 1, 0, ch); + this.lines[i].pop(); + } + } + + this.maxRange(); + }; + + // CSI P m SP ~ + // Delete P s Column(s) (default = 1) (DECDC), VT420 and up + // NOTE: xterm doesn't enable this code by default. + Terminal.prototype.deleteColumns = function() { + var param = params[0] + , l = this.ybase + this.rows + , ch = [this.eraseAttr(), ' '] // xterm? + , i; + + while (param--) { + for (i = this.ybase; i < l; i++) { + this.lines[i].splice(this.x, 1); + this.lines[i].push(ch); + } + } + + this.maxRange(); + }; + + + Terminal.prototype.copyBuffer = function(lines) { + var lines = lines || this.lines + , out = []; + + for (var y = 0; y < lines.length; y++) { + out[y] = []; + for (var x = 0; x < lines[y].length; x++) { + out[y][x] = [lines[y][x][0], lines[y][x][1]]; + } + } + + return out; + }; + + Terminal.prototype.getCopyTextarea = function(text) { + var textarea = this._copyTextarea + , document = this.document; + + if (!textarea) { + textarea = document.createElement('textarea'); + textarea.style.position = 'absolute'; + textarea.style.left = '-32000px'; + textarea.style.top = '-32000px'; + textarea.style.width = '0px'; + textarea.style.height = '0px'; + textarea.style.opacity = '0'; + textarea.style.backgroundColor = 'transparent'; + textarea.style.borderStyle = 'none'; + textarea.style.outlineStyle = 'none'; + + document.getElementsByTagName('body')[0].appendChild(textarea); + + this._copyTextarea = textarea; + } + + return textarea; + }; + + // NOTE: Only works for primary selection on X11. + // Non-X11 users should use Ctrl-C instead. + Terminal.prototype.copyText = function(text) { + var self = this + , textarea = this.getCopyTextarea(); + + this.emit('copy', text); + + textarea.focus(); + textarea.textContent = text; + textarea.value = text; + textarea.setSelectionRange(0, text.length); + + setTimeout(function() { + self.element.focus(); + self.focus(); + }, 1); + }; + + Terminal.prototype.keyPrefix = function(ev, key) { + if (key === 'k' || key === '&') { + this.destroy(); + } else if (key === 'p' || key === ']') { + this.emit('request paste'); + } else if (key === 'c') { + this.emit('request create'); + } else if (key >= '0' && key <= '9') { + key = +key - 1; + if (!~key) key = 9; + this.emit('request term', key); + } else if (key === 'n') { + this.emit('request term next'); + } else if (key === 'P') { + this.emit('request term previous'); + } else if (key === ':') { + this.emit('request command mode'); + } + }; + + Terminal.prototype.keySelect = function(ev, key) { + this.showCursor(); + + if (key === '\x04') { // ctrl-d + var y = this.ydisp + this.y; + if (this.ydisp === this.ybase) { + // Mimic vim behavior + this.y = Math.min(this.y + (this.rows - 1) / 2 | 0, this.rows - 1); + this.refresh(0, this.rows - 1); + } else { + this.scrollDisp((this.rows - 1) / 2 | 0); + } + return; + } + + if (key === '\x15') { // ctrl-u + var y = this.ydisp + this.y; + if (this.ydisp === 0) { + // Mimic vim behavior + this.y = Math.max(this.y - (this.rows - 1) / 2 | 0, 0); + this.refresh(0, this.rows - 1); + } else { + this.scrollDisp(-(this.rows - 1) / 2 | 0); + } + return; + } + + if (key === '\x06') { // ctrl-f + var y = this.ydisp + this.y; + this.scrollDisp(this.rows - 1); + return; + } + + if (key === '\x02') { // ctrl-b + var y = this.ydisp + this.y; + this.scrollDisp(-(this.rows - 1)); + return; + } + + if (key === 'k' || key === '\x1b[A') { + var y = this.ydisp + this.y; + this.y--; + if (this.y < 0) { + this.y = 0; + this.scrollDisp(-1); + } + this.refresh(this.y, this.y + 1); + return; + } + + if (key === 'j' || key === '\x1b[B') { + var y = this.ydisp + this.y; + this.y++; + if (this.y >= this.rows) { + this.y = this.rows - 1; + this.scrollDisp(1); + } + this.refresh(this.y - 1, this.y); + return; + } + + if (key === 'h' || key === '\x1b[D') { + var x = this.x; + this.x--; + if (this.x < 0) { + this.x = 0; + } + this.refresh(this.y, this.y); + return; + } + + if (key === 'l' || key === '\x1b[C') { + var x = this.x; + this.x++; + if (this.x >= this.cols) { + this.x = this.cols - 1; + } + this.refresh(this.y, this.y); + return; + } + + if (key === 'w' || key === 'W') { + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; + + var x = this.x; + var y = this.y; + var yb = this.ydisp; + var saw_space = false; + + for (;;) { + var line = this.lines[yb + y]; + while (x < this.cols) { + if (line[x][1] <= ' ') { + saw_space = true; + } else if (saw_space) { + break; + } + x++; } - saw_full = true; - break; - } else if (i === this.cols - 1) { - if (first_is_space === -1) { - first_is_space = 1; - } else if (first_is_space === 0) { - found = true; - } else if (first_is_space === 1) { - if (saw_full) found = true; + if (x >= this.cols) x = this.cols - 1; + if (x === this.cols - 1 && line[x][1] <= ' ') { + x = 0; + if (++y >= this.rows) { + y--; + if (++yb > this.ybase) { + yb = this.ybase; + x = this.x; + break; + } + } + continue; } break; } + + this.x = x, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + return; } - if (found) break; + if (key === 'b' || key === 'B') { + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; - if (key === '{') { - y--; - if (y < 0) { - y++; - if (yb > 0) yb--; - else break; + var x = this.x; + var y = this.y; + var yb = this.ydisp; + + for (;;) { + var line = this.lines[yb + y]; + var saw_space = x > 0 && line[x][1] > ' ' && line[x - 1][1] > ' '; + while (x >= 0) { + if (line[x][1] <= ' ') { + if (saw_space && (x + 1 < this.cols && line[x + 1][1] > ' ')) { + x++; + break; + } else { + saw_space = true; + } + } + x--; + } + if (x < 0) x = 0; + if (x === 0 && (line[x][1] <= ' ' || !saw_space)) { + x = this.cols - 1; + if (--y < 0) { + y++; + if (--yb < 0) { + yb++; + x = 0; + break; + } + } + continue; + } + break; } - } else if (key === '}') { - y++; - if (y >= this.rows) { - y--; - if (yb < this.ybase) yb++; - else break; + + this.x = x, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + return; + } + + if (key === 'e' || key === 'E') { + var x = this.x + 1; + var y = this.y; + var yb = this.ydisp; + if (x >= this.cols) x--; + + for (;;) { + var line = this.lines[yb + y]; + while (x < this.cols) { + if (line[x][1] <= ' ') { + x++; + } else { + break; + } + } + while (x < this.cols) { + if (line[x][1] <= ' ') { + if (x - 1 >= 0 && line[x - 1][1] > ' ') { + x--; + break; + } + } + x++; + } + if (x >= this.cols) x = this.cols - 1; + if (x === this.cols - 1 && line[x][1] <= ' ') { + x = 0; + if (++y >= this.rows) { + y--; + if (++yb > this.ybase) { + yb = this.ybase; + break; + } + } + continue; + } + break; + } + + this.x = x, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + return; + } + + if (key === '^' || key === '0') { + var ox = this.x; + + if (key === '0') { + this.x = 0; + } else if (key === '^') { + var line = this.lines[this.ydisp + this.y]; + var x = 0; + while (x < this.cols) { + if (line[x][1] > ' ') { + break; + } + x++; + } + if (x >= this.cols) x = this.cols - 1; + this.x = x; + } + + this.refresh(this.y, this.y); + return; + } + + if (key === '$') { + var ox = this.x; + var line = this.lines[this.ydisp + this.y]; + var x = this.cols - 1; + while (x >= 0) { + x--; + } + if (x < 0) x = 0; + this.x = x; + this.refresh(this.y, this.y); + return; + } + + if (key === 'g' || key === 'G') { + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; + if (key === 'g') { + this.x = 0, this.y = 0; + this.scrollDisp(-this.ydisp); + } else if (key === 'G') { + this.x = 0, this.y = this.rows - 1; + this.scrollDisp(this.ybase); + } + return; + } + + if (key === 'H' || key === 'M' || key === 'L') { + var ox = this.x; + var oy = this.y; + if (key === 'H') { + this.x = 0, this.y = 0; + } else if (key === 'M') { + this.x = 0, this.y = this.rows / 2 | 0; + } else if (key === 'L') { + this.x = 0, this.y = this.rows - 1; + } + this.refresh(oy, oy); + this.refresh(this.y, this.y); + return; + } + + if (key === '{' || key === '}') { + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; + + var line; + var saw_full = false; + var found = false; + var first_is_space = -1; + var y = this.y + (key === '{' ? -1 : 1); + var yb = this.ydisp; + var i; + + if (key === '{') { + if (y < 0) { + y++; + if (yb > 0) yb--; + } + } else if (key === '}') { + if (y >= this.rows) { + y--; + if (yb < this.ybase) yb++; + } + } + + for (;;) { + line = this.lines[yb + y]; + + for (i = 0; i < this.cols; i++) { + if (line[i][1] > ' ') { + if (first_is_space === -1) { + first_is_space = 0; + } + saw_full = true; + break; + } else if (i === this.cols - 1) { + if (first_is_space === -1) { + first_is_space = 1; + } else if (first_is_space === 0) { + found = true; + } else if (first_is_space === 1) { + if (saw_full) found = true; + } + break; + } + } + + if (found) break; + + if (key === '{') { + y--; + if (y < 0) { + y++; + if (yb > 0) yb--; + else break; + } + } else if (key === '}') { + y++; + if (y >= this.rows) { + y--; + if (yb < this.ybase) yb++; + else break; + } + } + } + + if (!found) { + if (key === '{') { + y = 0; + yb = 0; + } else if (key === '}') { + y = this.rows - 1; + yb = this.ybase; + } + } + + this.x = 0, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + return; + } + + return false; + }; + + /** + * Character Sets + */ + + Terminal.charsets = {}; + + // DEC Special Character and Line Drawing Set. + // http://vt100.net/docs/vt102-ug/table5-13.html + // A lot of curses apps use this if they see TERM=xterm. + // testing: echo -e '\e(0a\e(B' + // The xterm output sometimes seems to conflict with the + // reference above. xterm seems in line with the reference + // when running vttest however. + // The table below now uses xterm's output from vttest. + Terminal.charsets.SCLD = { // (0 + '`': '\u25c6', // '◆' + 'a': '\u2592', // '▒' + 'b': '\u0009', // '\t' + 'c': '\u000c', // '\f' + 'd': '\u000d', // '\r' + 'e': '\u000a', // '\n' + 'f': '\u00b0', // '°' + 'g': '\u00b1', // '±' + 'h': '\u2424', // '\u2424' (NL) + 'i': '\u000b', // '\v' + 'j': '\u2518', // '┘' + 'k': '\u2510', // '┐' + 'l': '\u250c', // '┌' + 'm': '\u2514', // '└' + 'n': '\u253c', // '┼' + 'o': '\u23ba', // '⎺' + 'p': '\u23bb', // '⎻' + 'q': '\u2500', // '─' + 'r': '\u23bc', // '⎼' + 's': '\u23bd', // '⎽' + 't': '\u251c', // '├' + 'u': '\u2524', // '┤' + 'v': '\u2534', // '┴' + 'w': '\u252c', // '┬' + 'x': '\u2502', // '│' + 'y': '\u2264', // '≤' + 'z': '\u2265', // '≥' + '{': '\u03c0', // 'π' + '|': '\u2260', // '≠' + '}': '\u00a3', // '£' + '~': '\u00b7' // '·' + }; + + Terminal.charsets.UK = null; // (A + Terminal.charsets.US = null; // (B (USASCII) + Terminal.charsets.Dutch = null; // (4 + Terminal.charsets.Finnish = null; // (C or (5 + Terminal.charsets.French = null; // (R + Terminal.charsets.FrenchCanadian = null; // (Q + Terminal.charsets.German = null; // (K + Terminal.charsets.Italian = null; // (Y + Terminal.charsets.NorwegianDanish = null; // (E or (6 + Terminal.charsets.Spanish = null; // (Z + Terminal.charsets.Swedish = null; // (H or (7 + Terminal.charsets.Swiss = null; // (= + Terminal.charsets.ISOLatin = null; // /A + + /** + * Helpers + */ + + function on(el, type, handler, capture) { + if (!Array.isArray(el)) { + el = [el]; + } + el.forEach(function (element) { + element.addEventListener(type, handler, capture || false); + }); + } + + function off(el, type, handler, capture) { + el.removeEventListener(type, handler, capture || false); + } + + function cancel(ev, force) { + if (!this.cancelEvents && !force) { + return; + } + ev.preventDefault(); + ev.stopPropagation(); + return false; + } + + function inherits(child, parent) { + function f() { + this.constructor = child; + } + f.prototype = parent.prototype; + child.prototype = new f; + } + + // if bold is broken, we can't + // use it in the terminal. + function isBoldBroken(document) { + var body = document.getElementsByTagName('body')[0]; + var el = document.createElement('span'); + el.innerHTML = 'hello world'; + body.appendChild(el); + var w1 = el.scrollWidth; + el.style.fontWeight = 'bold'; + var w2 = el.scrollWidth; + body.removeChild(el); + return w1 !== w2; + } + + var String = this.String; + var setTimeout = this.setTimeout; + var setInterval = this.setInterval; + + function indexOf(obj, el) { + var i = obj.length; + while (i--) { + if (obj[i] === el) return i; + } + return -1; + } + + function isWide(ch) { + if (ch <= '\uff00') return false; + return (ch >= '\uff01' && ch <= '\uffbe') + || (ch >= '\uffc2' && ch <= '\uffc7') + || (ch >= '\uffca' && ch <= '\uffcf') + || (ch >= '\uffd2' && ch <= '\uffd7') + || (ch >= '\uffda' && ch <= '\uffdc') + || (ch >= '\uffe0' && ch <= '\uffe6') + || (ch >= '\uffe8' && ch <= '\uffee'); + } + + function matchColor(r1, g1, b1) { + var hash = (r1 << 16) | (g1 << 8) | b1; + + if (matchColor._cache[hash] != null) { + return matchColor._cache[hash]; + } + + var ldiff = Infinity + , li = -1 + , i = 0 + , c + , r2 + , g2 + , b2 + , diff; + + for (; i < Terminal.vcolors.length; i++) { + c = Terminal.vcolors[i]; + r2 = c[0]; + g2 = c[1]; + b2 = c[2]; + + diff = matchColor.distance(r1, g1, b1, r2, g2, b2); + + if (diff === 0) { + li = i; + break; + } + + if (diff < ldiff) { + ldiff = diff; + li = i; } } + + return matchColor._cache[hash] = li; } - if (!found) { - if (key === '{') { - y = 0; - yb = 0; - } else if (key === '}') { - y = this.rows - 1; - yb = this.ybase; + matchColor._cache = {}; + + // http://stackoverflow.com/questions/1633828 + matchColor.distance = function(r1, g1, b1, r2, g2, b2) { + return Math.pow(30 * (r1 - r2), 2) + + Math.pow(59 * (g1 - g2), 2) + + Math.pow(11 * (b1 - b2), 2); + }; + + function each(obj, iter, con) { + if (obj.forEach) return obj.forEach(iter, con); + for (var i = 0; i < obj.length; i++) { + iter.call(con, obj[i], i, obj); } } - this.x = 0, this.y = y; - this.scrollDisp(-this.ydisp + yb); - - return; - } - - return false; -}; - -/** - * Character Sets - */ - -Terminal.charsets = {}; - -// DEC Special Character and Line Drawing Set. -// http://vt100.net/docs/vt102-ug/table5-13.html -// A lot of curses apps use this if they see TERM=xterm. -// testing: echo -e '\e(0a\e(B' -// The xterm output sometimes seems to conflict with the -// reference above. xterm seems in line with the reference -// when running vttest however. -// The table below now uses xterm's output from vttest. -Terminal.charsets.SCLD = { // (0 - '`': '\u25c6', // '◆' - 'a': '\u2592', // '▒' - 'b': '\u0009', // '\t' - 'c': '\u000c', // '\f' - 'd': '\u000d', // '\r' - 'e': '\u000a', // '\n' - 'f': '\u00b0', // '°' - 'g': '\u00b1', // '±' - 'h': '\u2424', // '\u2424' (NL) - 'i': '\u000b', // '\v' - 'j': '\u2518', // '┘' - 'k': '\u2510', // '┐' - 'l': '\u250c', // '┌' - 'm': '\u2514', // '└' - 'n': '\u253c', // '┼' - 'o': '\u23ba', // '⎺' - 'p': '\u23bb', // '⎻' - 'q': '\u2500', // '─' - 'r': '\u23bc', // '⎼' - 's': '\u23bd', // '⎽' - 't': '\u251c', // '├' - 'u': '\u2524', // '┤' - 'v': '\u2534', // '┴' - 'w': '\u252c', // '┬' - 'x': '\u2502', // '│' - 'y': '\u2264', // '≤' - 'z': '\u2265', // '≥' - '{': '\u03c0', // 'π' - '|': '\u2260', // '≠' - '}': '\u00a3', // '£' - '~': '\u00b7' // '·' -}; - -Terminal.charsets.UK = null; // (A -Terminal.charsets.US = null; // (B (USASCII) -Terminal.charsets.Dutch = null; // (4 -Terminal.charsets.Finnish = null; // (C or (5 -Terminal.charsets.French = null; // (R -Terminal.charsets.FrenchCanadian = null; // (Q -Terminal.charsets.German = null; // (K -Terminal.charsets.Italian = null; // (Y -Terminal.charsets.NorwegianDanish = null; // (E or (6 -Terminal.charsets.Spanish = null; // (Z -Terminal.charsets.Swedish = null; // (H or (7 -Terminal.charsets.Swiss = null; // (= -Terminal.charsets.ISOLatin = null; // /A - -/** - * Helpers - */ - -function on(el, type, handler, capture) { - if (!Array.isArray(el)) { - el = [el]; - } - el.forEach(function (element) { - element.addEventListener(type, handler, capture || false); - }); -} - -function off(el, type, handler, capture) { - el.removeEventListener(type, handler, capture || false); -} - -function cancel(ev, force) { - if (!this.cancelEvents && !force) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - return false; -} - -function inherits(child, parent) { - function f() { - this.constructor = child; - } - f.prototype = parent.prototype; - child.prototype = new f; -} - -// if bold is broken, we can't -// use it in the terminal. -function isBoldBroken(document) { - var body = document.getElementsByTagName('body')[0]; - var el = document.createElement('span'); - el.innerHTML = 'hello world'; - body.appendChild(el); - var w1 = el.scrollWidth; - el.style.fontWeight = 'bold'; - var w2 = el.scrollWidth; - body.removeChild(el); - return w1 !== w2; -} - -var String = this.String; -var setTimeout = this.setTimeout; -var setInterval = this.setInterval; - -function indexOf(obj, el) { - var i = obj.length; - while (i--) { - if (obj[i] === el) return i; - } - return -1; -} - -function isWide(ch) { - if (ch <= '\uff00') return false; - return (ch >= '\uff01' && ch <= '\uffbe') - || (ch >= '\uffc2' && ch <= '\uffc7') - || (ch >= '\uffca' && ch <= '\uffcf') - || (ch >= '\uffd2' && ch <= '\uffd7') - || (ch >= '\uffda' && ch <= '\uffdc') - || (ch >= '\uffe0' && ch <= '\uffe6') - || (ch >= '\uffe8' && ch <= '\uffee'); -} - -function matchColor(r1, g1, b1) { - var hash = (r1 << 16) | (g1 << 8) | b1; - - if (matchColor._cache[hash] != null) { - return matchColor._cache[hash]; - } - - var ldiff = Infinity - , li = -1 - , i = 0 - , c - , r2 - , g2 - , b2 - , diff; - - for (; i < Terminal.vcolors.length; i++) { - c = Terminal.vcolors[i]; - r2 = c[0]; - g2 = c[1]; - b2 = c[2]; - - diff = matchColor.distance(r1, g1, b1, r2, g2, b2); - - if (diff === 0) { - li = i; - break; + function keys(obj) { + if (Object.keys) return Object.keys(obj); + var key, keys = []; + for (key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + keys.push(key); + } + } + return keys; } - if (diff < ldiff) { - ldiff = diff; - li = i; - } - } + /** + * Expose + */ - return matchColor._cache[hash] = li; -} + Terminal.EventEmitter = EventEmitter; + Terminal.inherits = inherits; + Terminal.on = on; + Terminal.off = off; + Terminal.cancel = cancel; -matchColor._cache = {}; - -// http://stackoverflow.com/questions/1633828 -matchColor.distance = function(r1, g1, b1, r2, g2, b2) { - return Math.pow(30 * (r1 - r2), 2) - + Math.pow(59 * (g1 - g2), 2) - + Math.pow(11 * (b1 - b2), 2); -}; - -function each(obj, iter, con) { - if (obj.forEach) return obj.forEach(iter, con); - for (var i = 0; i < obj.length; i++) { - iter.call(con, obj[i], i, obj); - } -} - -function keys(obj) { - if (Object.keys) return Object.keys(obj); - var key, keys = []; - for (key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - keys.push(key); - } - } - return keys; -} - -/** - * Expose - */ - -Terminal.EventEmitter = EventEmitter; -Terminal.inherits = inherits; -Terminal.on = on; -Terminal.off = off; -Terminal.cancel = cancel; - -if (typeof module !== 'undefined') { - module.exports = Terminal; -} else { - this.Terminal = Terminal; -} - -}).call(function() { - return this || (typeof window !== 'undefined' ? window : global); -}()); \ No newline at end of file + return Terminal; +}); \ No newline at end of file