lib/html/source/javascripts/vendor/term.js in flammarion-0.2.1 vs lib/html/source/javascripts/vendor/term.js in flammarion-0.3.0

- old
+ new

@@ -1,5973 +1,5973 @@ -/** - * term.js - an xterm emulator - * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - * https://github.com/chjj/term.js - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Originally forked from (with the author's permission): - * Fabrice Bellard's javascript vt100 for jslinux: - * http://bellard.org/jslinux/ - * Copyright (c) 2011 Fabrice Bellard - * The original design remains. The terminal itself - * has been extended to include xterm CSI codes, among - * other features. - */ - -;(function() { - -/** - * Terminal Emulation References: - * http://vt100.net/ - * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt - * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * http://invisible-island.net/vttest/ - * http://www.inwap.com/pdp10/ansicode.txt - * http://linux.die.net/man/4/console_codes - * http://linux.die.net/man/7/urxvt - */ - -'use strict'; - -/** - * 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] || []; -}; - -/** - * Stream - */ - -function Stream() { - EventEmitter.call(this); -} - -inherits(Stream, EventEmitter); - -Stream.prototype.pipe = function(dest, options) { - var src = this - , ondata - , onerror - , onend; - - function unbind() { - src.removeListener('data', ondata); - src.removeListener('error', onerror); - src.removeListener('end', onend); - dest.removeListener('error', onerror); - dest.removeListener('close', unbind); - } - - src.on('data', ondata = function(data) { - dest.write(data); - }); - - src.on('error', onerror = function(err) { - unbind(); - if (!this.listeners('error').length) { - throw err; - } - }); - - src.on('end', onend = function() { - dest.end(); - unbind(); - }); - - dest.on('error', onerror); - dest.on('close', unbind); - - dest.emit('pipe', src); - - return dest; -}; - -/** - * States - */ - -var normal = 0 - , escaped = 1 - , csi = 2 - , osc = 3 - , charset = 4 - , dcs = 5 - , ignore = 6 - , UDK = { type: 'udk' }; - -/** - * Terminal - */ - -function Terminal(options) { - var self = this; - - if (!(this instanceof Terminal)) { - return new Terminal(arguments[0], arguments[1], arguments[2]); - } - - Stream.call(this); - - if (typeof options === 'number') { - options = { - cols: arguments[0], - rows: arguments[1], - handler: arguments[2] - }; - } - - options = options || {}; - - each(keys(Terminal.defaults), function(key) { - if (options[key] == null) { - options[key] = Terminal.options[key]; - // Legacy: - 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.slice(0, -2).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]; - - // Act as though we are a node TTY stream: - this.setRawMode; - this.isTTY = true; - this.isRaw = true; - this.columns = this.cols; - this.rows = this.rows; - - 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; - - // select modes - this.prefixMode = false; - this.selectMode = false; - this.visualMode = false; - this.searchMode = false; - this.searchDown; - this.entry = ''; - this.entryPrefix = 'Search: '; - this._real; - this._selected; - this._textarea; - - // 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, Stream); - -/** - * Colors - */ - -// Colors 0-15 -Terminal.tangoColors = [ - // dark: - '#2e3436', - '#cc0000', - '#4e9a06', - '#c4a000', - '#3465a4', - '#75507b', - '#06989a', - '#d3d7cf', - // bright: - '#555753', - '#ef2929', - '#8ae234', - '#fce94f', - '#729fcf', - '#ad7fa8', - '#34e2e2', - '#eeeeec' -]; - -Terminal.xtermColors = [ - // dark: - '#000000', // black - '#cd0000', // red3 - '#00cd00', // green3 - '#cdcd00', // yellow3 - '#0000ee', // blue2 - '#cd00cd', // magenta3 - '#00cdcd', // cyan3 - '#e5e5e5', // gray90 - // bright: - '#7f7f7f', // gray50 - '#ff0000', // red - '#00ff00', // green - '#ffff00', // yellow - '#5c5cff', // rgb:5c/5c/ff - '#ff00ff', // magenta - '#00ffff', // cyan - '#ffffff' // white -]; - -// 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; -})(); - -// Default BG/FG -Terminal.colors[256] = '#000000'; -Terminal.colors[257] = '#f0f0f0'; - -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, - convertEol: false, - termName: 'xterm', - geometry: [80, 24], - cursorBlink: true, - visualBell: false, - popOnBell: false, - scrollback: 1000, - screenKeys: false, - debug: false, - useStyle: 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.focus = null; - -Terminal.prototype.focus = function() { - if (Terminal.focus === this) return; - - if (Terminal.focus) { - Terminal.focus.blur(); - } - - if (this.sendFocus) this.send('\x1b[I'); - this.showCursor(); - - // try { - // this.element.focus(); - // } catch (e) { - // ; - // } - - // this.emit('focus'); - - Terminal.focus = 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'); - - // try { - // this.element.blur(); - // } catch (e) { - // ; - // } - - // this.emit('blur'); - - Terminal.focus = null; -}; - -/** - * Initialize global behavior - */ - -Terminal.prototype.initGlobal = function() { - var document = this.document; - - Terminal._boundDocs = Terminal._boundDocs || []; - if (~indexOf(Terminal._boundDocs, document)) { - return; - } - Terminal._boundDocs.push(document); - - Terminal.bindPaste(document); - - Terminal.bindKeys(document); - - Terminal.bindCopy(document); - - if (this.isMobile) { - this.fixMobile(document); - } - - if (this.useStyle) { - Terminal.insertStyle(document, this.colors[256], this.colors[257]); - } -}; - -/** - * Bind to paste event - */ - -Terminal.bindPaste = function(document) { - // This seems to work well for ctrl-V and middle-click, - // even without the contentEditable workaround. - var window = document.defaultView; - on(window, 'paste', function(ev) { - var term = Terminal.focus; - if (!term) return; - if (ev.clipboardData) { - term.send(ev.clipboardData.getData('text/plain')); - } else if (term.context.clipboardData) { - term.send(term.context.clipboardData.getData('Text')); - } - // Not necessary. Do it anyway for good measure. - term.element.contentEditable = 'inherit'; - return cancel(ev); - }); -}; - -/** - * Global Events for key handling - */ - -Terminal.bindKeys = function(document) { - // We should only need to check `target === body` below, - // but we can check everything for good measure. - on(document, 'keydown', function(ev) { - if (!Terminal.focus) return; - var target = ev.target || ev.srcElement; - if (!target) return; - if (target === Terminal.focus.element - || target === Terminal.focus.context - || target === Terminal.focus.document - || target === Terminal.focus.body - || target === Terminal._textarea - || target === Terminal.focus.parent) { - return Terminal.focus.keyDown(ev); - } - }, true); - - on(document, 'keypress', function(ev) { - if (!Terminal.focus) return; - var target = ev.target || ev.srcElement; - if (!target) return; - if (target === Terminal.focus.element - || target === Terminal.focus.context - || target === Terminal.focus.document - || target === Terminal.focus.body - || target === Terminal._textarea - || target === Terminal.focus.parent) { - return Terminal.focus.keyPress(ev); - } - }, true); - - // If we click somewhere other than a - // terminal, unfocus the terminal. - on(document, 'mousedown', function(ev) { - if (!Terminal.focus) return; - - var el = ev.target || ev.srcElement; - if (!el) return; - - do { - if (el === Terminal.focus.element) return; - } while (el = el.parentNode); - - Terminal.focus.blur(); - }); -}; - -/** - * Copy Selection w/ Ctrl-C (Select Mode) - */ - -Terminal.bindCopy = function(document) { - var window = document.defaultView; - - // if (!('onbeforecopy' in document)) { - // // Copies to *only* the clipboard. - // on(window, 'copy', function fn(ev) { - // var term = Terminal.focus; - // if (!term) return; - // if (!term._selected) return; - // var text = term.grabText( - // term._selected.x1, term._selected.x2, - // term._selected.y1, term._selected.y2); - // term.emit('copy', text); - // ev.clipboardData.setData('text/plain', text); - // }); - // return; - // } - - // Copies to primary selection *and* clipboard. - // NOTE: This may work better on capture phase, - // or using the `beforecopy` event. - on(window, 'copy', function(ev) { - var term = Terminal.focus; - if (!term) return; - if (!term._selected) return; - var textarea = term.getCopyTextarea(); - var text = term.grabText( - term._selected.x1, term._selected.x2, - term._selected.y1, term._selected.y2); - term.emit('copy', text); - textarea.focus(); - textarea.textContent = text; - textarea.value = text; - textarea.setSelectionRange(0, text.length); - setTimeout(function() { - term.element.focus(); - term.focus(); - }, 1); - }); -}; - -/** - * Fix Mobile - */ - -Terminal.prototype.fixMobile = function(document) { - var self = this; - - var 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'; - textarea.autocapitalize = 'none'; - textarea.autocorrect = 'off'; - - document.getElementsByTagName('body')[0].appendChild(textarea); - - Terminal._textarea = textarea; - - setTimeout(function() { - textarea.focus(); - }, 1000); - - if (this.isAndroid) { - on(textarea, 'change', function() { - var value = textarea.textContent || textarea.value; - textarea.value = ''; - textarea.textContent = ''; - self.send(value + '\r'); - }); - } -}; - -/** - * Insert a default style - */ - -Terminal.insertStyle = function(document, bg, fg) { - var style = document.getElementById('term-style'); - if (style) return; - - var head = document.getElementsByTagName('head')[0]; - if (!head) return; - - var style = document.createElement('style'); - style.id = 'term-style'; - - // textContent doesn't work well with IE for <style> elements. - style.innerHTML = '' - + '.terminal {\n' - + ' float: left;\n' - + ' border: ' + bg + ' solid 5px;\n' - + ' font-family: "DejaVu Sans Mono", "Liberation Mono", monospace;\n' - + ' font-size: 11px;\n' - + ' color: ' + fg + ';\n' - + ' background: ' + bg + ';\n' - + '}\n' - + '\n' - + '.terminal-cursor {\n' - + ' color: ' + bg + ';\n' - + ' background: ' + fg + ';\n' - + '}\n'; - - // var out = ''; - // each(Terminal.colors, function(color, i) { - // if (i === 256) { - // out += '\n.term-bg-color-default { background-color: ' + color + '; }'; - // } - // if (i === 257) { - // out += '\n.term-fg-color-default { color: ' + color + '; }'; - // } - // out += '\n.term-bg-color-' + i + ' { background-color: ' + color + '; }'; - // out += '\n.term-fg-color-' + i + ' { color: ' + color + '; }'; - // }); - // style.innerHTML += out + '\n'; - - head.insertBefore(style, head.firstChild); -}; - -/** - * Open Terminal - */ - -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 strings. - 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.isAndroid = !!~this.context.navigator.userAgent.indexOf('Android'); - this.isMobile = this.isIpad || this.isIphone || this.isAndroid; - this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); - } - - // Create our main terminal element. - this.element = this.document.createElement('div'); - this.element.className = 'terminal'; - this.element.style.outline = 'none'; - this.element.setAttribute('tabindex', 0); - this.element.setAttribute('spellcheck', 'false'); - this.element.style.backgroundColor = this.colors[256]; - this.element.style.color = this.colors[257]; - - // Create the lines for our terminal. - this.children = []; - for (; i < this.rows; i++) { - div = this.document.createElement('div'); - this.element.appendChild(div); - this.children.push(div); - } - this.parent.appendChild(this.element); - - // Draw the screen. - this.refresh(0, this.rows - 1); - - if (!('useEvents' in this.options) || this.options.useEvents) { - // Initialize global actions that - // need to be taken on the document. - this.initGlobal(); - } - - if (!('useFocus' in this.options) || this.options.useFocus) { - // Ensure there is a Terminal.focus. - this.focus(); - - // Start blinking the cursor. - this.startBlink(); - - // Bind to DOM events related - // to focus and paste behavior. - on(this.element, 'focus', function() { - self.focus(); - if (self.isMobile) { - Terminal._textarea.focus(); - } - }); - - // This causes slightly funky behavior. - // on(this.element, 'blur', function() { - // self.blur(); - // }); - - on(this.element, 'mousedown', function() { - self.focus(); - }); - - // Clickable paste workaround, using contentEditable. - // This probably shouldn't work, - // ... but it does. Firefox's paste - // event seems to only work for textareas? - on(this.element, 'mousedown', function(ev) { - var button = ev.button != null - ? +ev.button - : ev.which != null - ? ev.which - 1 - : null; - - // Does IE9 do this? - if (self.isMSIE) { - button = button === 1 ? 0 : button === 4 ? 1 : button; - } - - if (button !== 2) return; - - self.element.contentEditable = 'true'; - setTimeout(function() { - self.element.contentEditable = 'inherit'; // 'false'; - }, 1); - }, true); - } - - if (!('useMouse' in this.options) || this.options.useMouse) { - // Listen for mouse events and translate - // them into terminal mouse protocols. - this.bindMouse(); - } - - // this.emit('open'); - - if (!('useFocus' in this.options) || this.options.useFocus) { - // This can be useful for pasting, - // as well as the iPad fix. - setTimeout(function() { - self.element.focus(); - }, 100); - } - - // Figure out whether boldness affects - // the character width of monospace fonts. - if (Terminal.brokenBold == null) { - Terminal.brokenBold = isBoldBroken(this.document); - } - - this.emit('open'); -}; - -Terminal.prototype.setRawMode = function(value) { - this.isRaw = !!value; -}; - -// 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); - - // ensure focus - self.focus(); - - // fix for odd bug - //if (self.vt200Mouse && !self.normalMouse) { - // XXX This seems to break certain programs. - // if (self.vt200Mouse) { - // sendButton({ __proto__: ev, type: 'mouseup' }); - // return 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 cancel(ev); - }); - } - - return cancel(ev); - }); - - //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 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 ? -5 : 5); - } else { - self.scrollDisp(ev.wheelDeltaY > 0 ? -5 : 5); - } - return cancel(ev); - }); -}; - -/** - * Destroy Terminal - */ - -Terminal.prototype.close = -Terminal.prototype.destroySoon = -Terminal.prototype.destroy = function() { - if (this.destroyed) { - return; - } - - if (this._blink) { - clearInterval(this._blink); - delete this._blink; - } - - this.readable = false; - this.writable = false; - this.destroyed = true; - this._events = {}; - - this.handler = function() {}; - this.write = function() {}; - this.end = function() {}; - - if (this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - } - - this.emit('end'); - this.emit('close'); - this.emit('finish'); - this.emit('destroy'); -}; - -/** - * 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; - - if (end - start >= this.rows / 2) { - parent = this.element.parentNode; - if (parent) parent.removeChild(this.element); - } - - 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; - } - - for (; y <= end; y++) { - row = y + this.ydisp; - - line = this.lines[row]; - out = ''; - - if (y === this.y - && this.cursorState - && (this.ydisp === this.ybase || this.selectMode) - && !this.cursorHidden) { - x = this.x; - } else { - x = -1; - } - - attr = this.defAttr; - i = 0; - - for (; i < width; i++) { - data = line[i][0]; - ch = line[i][1]; - - if (i === x) data = -1; - - if (data !== attr) { - if (attr !== this.defAttr) { - out += '</span>'; - } - if (data !== this.defAttr) { - if (data === -1) { - out += '<span class="reverse-video terminal-cursor">'; - } else { - out += '<span style="'; - - bg = data & 0x1ff; - fg = (data >> 9) & 0x1ff; - flags = data >> 18; - - // bold - if (flags & 1) { - if (!Terminal.brokenBold) { - out += 'font-weight:bold;'; - } - // See: XTerm*boldColors - if (fg < 8) fg += 8; - } - - // underline - if (flags & 2) { - out += 'text-decoration:underline;'; - } - - // blink - if (flags & 4) { - if (flags & 2) { - out = out.slice(0, -1); - out += ' blink;'; - } else { - out += 'text-decoration:blink;'; - } - } - - // inverse - if (flags & 8) { - bg = (data >> 9) & 0x1ff; - fg = data & 0x1ff; - // Should inverse just be before the - // above boldColors effect instead? - if ((flags & 1) && fg < 8) fg += 8; - } - - // invisible - if (flags & 16) { - out += 'visibility:hidden;'; - } - - // out += '" class="' - // + 'term-bg-color-' + bg - // + ' ' - // + 'term-fg-color-' + fg - // + '">'; - - if (bg !== 256) { - out += 'background-color:' - + this.colors[bg] - + ';'; - } - - if (fg !== 257) { - out += 'color:' - + this.colors[fg] - + ';'; - } - - out += '">'; - } - } - } - - switch (ch) { - case '&': - out += '&amp;'; - break; - case '<': - out += '&lt;'; - break; - case '>': - out += '&gt;'; - break; - default: - if (ch <= ' ') { - out += '&nbsp;'; - } else { - if (isWide(ch)) i++; - out += ch; - } - break; - } - - attr = data; - } - - if (attr !== this.defAttr) { - out += '</span>'; - } - - this.children[y].innerHTML = out; - } - - if (parent) parent.appendChild(this.element); -}; - -Terminal.prototype._cursorBlink = function() { - if (Terminal.focus !== this) return; - this.cursorState ^= 1; - this.refresh(this.y, this.y); -}; - -Terminal.prototype.showCursor = function() { - if (!this.cursorState) { - this.cursorState = 1; - this.refresh(this.y, this.y); - } else { - // Temporarily disabled: - // this.refreshBlink(); - } -}; - -Terminal.prototype.startBlink = function() { - if (!this.cursorBlink) return; - var self = this; - this._blinker = function() { - self._cursorBlink(); - }; - this._blink = setInterval(this._blinker, 500); -}; - -Terminal.prototype.refreshBlink = function() { - if (!this.cursorBlink || !this._blink) return; - clearInterval(this._blink); - this._blink = setInterval(this._blinker, 500); -}; - -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); -}; - -Terminal.prototype.scrollDisp = function(disp) { - this.ydisp += disp; - - if (this.ydisp > this.ybase) { - this.ydisp = this.ybase; - } else if (this.ydisp < 0) { - this.ydisp = 0; - } - - this.refresh(0, this.rows - 1); -}; - -Terminal.prototype.write = function(data) { - var l = data.length - , i = 0 - , j - , cs - , ch; - - this.refreshStart = this.y; - this.refreshEnd = this.y; - - if (this.ybase !== this.ydisp) { - this.ydisp = this.ybase; - this.maxRange(); - } - - // this.log(JSON.stringify(data.replace(/\x1b/g, '^['))); - - for (; i < l; i++, this.lch = ch) { - ch = data[i]; - switch (this.state) { - case normal: - switch (ch) { - // '\0' - // case '\0': - // case '\200': - // break; - - // '\a' - case '\x07': - this.bell(); - break; - - // '\n', '\v', '\f' - case '\n': - case '\x0b': - case '\x0c': - if (this.convertEol) { - this.x = 0; - } - // TODO: Implement eat_newline_glitch. - // if (this.realX >= this.cols) break; - // this.realX = 0; - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - break; - - // '\r' - case '\r': - this.x = 0; - break; - - // '\b' - case '\x08': - if (this.x > 0) { - 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; - 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.prefix = ''; - this.currentParam = ''; - 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 '.': - 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; - } - 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 ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { - if (this.lch === '\x1b') { - if (typeof this.currentParam === 'string') { - this.currentParam = this.currentParam.slice(0, -1); - } else if (typeof this.currentParam == 'number') { - this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10; - } - } - - 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; - } - - // 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); - } - 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 ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { - // Workarounds: - if (this.prefix === 'tmux;\x1b') { - // `DCS tmux; Pt ST` may contain a Pt with an ST - // XXX Does tmux work this way? - // if (this.lch === '\x1b' & data[i + 1] === '\x1b' && data[i + 2] === '\\') { - // this.currentParam += ch; - // continue; - // } - // Tmux only accepts ST, not BEL: - if (ch === '\x07') { - this.currentParam += ch; - continue; - } - } - - if (this.lch === '\x1b') { - if (typeof this.currentParam === 'string') { - this.currentParam = this.currentParam.slice(0, -1); - } else if (typeof this.currentParam == 'number') { - this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10; - } - } - - this.params.push(this.currentParam); - - var pt = this.params[this.params.length - 1]; - - switch (this.prefix) { - // User-Defined Keys (DECUDK). - // DCS Ps; Ps| Pt ST - case UDK: - this.emit('udk', { - clearAll: this.params[0] === 0, - eraseBelow: this.params[0] === 1, - lockKeys: this.params[1] === 0, - dontLockKeys: this.params[1] === 1, - keyList: (this.params[2] + '').split(';').map(function(part) { - part = part.split('/'); - return { - keyCode: part[0], - hexKeyValue: part[1] - }; - }) - }); - break; - - // Request Status String (DECRQSS). - // DCS $ q Pt ST - // test: echo -e '\eP$q"p\e\\' - case '$q': - var valid = 0; - - switch (pt) { - // DECSCA - // CSI Ps " q - case '"q': - pt = '0"q'; - valid = 1; - break; - - // DECSCL - // CSI Ps ; Ps " p - case '"p': - pt = '61;0"p'; - valid = 1; - break; - - // DECSTBM - // CSI Ps ; Ps r - case 'r': - pt = '' - + (this.scrollTop + 1) - + ';' - + (this.scrollBottom + 1) - + 'r'; - valid = 1; - break; - - // SGR - // CSI Pm m - case 'm': - // TODO: Parse this.curAttr here. - // pt = '0m'; - // valid = 1; - valid = 0; // Not implemented. - break; - - default: - this.error('Unknown DCS Pt: %s.', pt); - valid = 0; // unimplemented - break; - } - - this.send('\x1bP' + valid + '$r' + pt + '\x1b\\'); - break; - - // Set Termcap/Terminfo Data (xterm, experimental). - // DCS + p Pt ST - case '+p': - this.emit('set terminfo', { - name: this.params[0] - }); - break; - - // Request Termcap/Terminfo String (xterm, experimental) - // Regular xterm does not even respond to this sequence. - // This can cause a small glitch in vim. - // DCS + q Pt ST - // test: echo -ne '\eP+q6b64\e\\' - case '+q': - var valid = false; - this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); - break; - - // Implement tmux sequence forwarding is - // someone uses term.js for a multiplexer. - // DCS tmux; ESC Pt ST - case 'tmux;\x1b': - this.emit('passthrough', pt); - break; - - default: - this.error('Unknown DCS prefix: %s.', pt); - break; - } - - this.currentParam = 0; - this.prefix = ''; - this.state = normal; - } else { - this.currentParam += ch; - if (!this.prefix) { - if (/^\d*;\d*\|/.test(this.currentParam)) { - this.prefix = UDK; - this.params = this.currentParam.split(/[;|]/).map(function(n) { - if (!n.length) return 0; - return +n; - }).slice(0, -1); - this.currentParam = ''; - } else if (/^[$+][a-zA-Z]/.test(this.currentParam) - || /^\w+;\x1b/.test(this.currentParam)) { - this.prefix = this.currentParam; - this.currentParam = ''; - } - } - } - break; - - case ignore: - // For PM and APC. - if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { - this.state = normal; - } - break; - } - } - - this.updateRange(this.y); - this.refresh(this.refreshStart, this.refreshEnd); - - return true; -}; - -Terminal.prototype.writeln = function(data) { - return this.write(data + '\r\n'); -}; - -Terminal.prototype.end = function(data) { - var ret = true; - if (data) { - ret = this.write(data); - } - this.destroySoon(); - return ret; -}; - -Terminal.prototype.resume = function() { - ; -}; - -Terminal.prototype.pause = function() { - ; -}; - -// 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.altKey) { - key = '\x17'; - break; - } - if (ev.shiftKey) { - key = '\x08'; // ^H - break; - } - key = '\x7f'; // ^? - break; - // tab - case 9: - if (ev.shiftKey) { - key = '\x1b[Z'; - break; - } - key = '\t'; - break; - // return/enter - case 13: - key = '\r'; - break; - // escape - case 27: - key = '\x1b'; - 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; - } - if (ev.ctrlKey) { - key = '\x1b[5D'; - break; - } - key = '\x1b[D'; - break; - // right-arrow - case 39: - if (this.applicationCursor) { - key = '\x1bOC'; - break; - } - if (ev.ctrlKey) { - key = '\x1b[5C'; - break; - } - key = '\x1b[C'; - break; - // up-arrow - case 38: - if (this.applicationCursor) { - key = '\x1bOA'; - break; - } - if (ev.ctrlKey) { - this.scrollDisp(-1); - return cancel(ev); - } else { - key = '\x1b[A'; - } - break; - // down-arrow - case 40: - if (this.applicationCursor) { - key = '\x1bOB'; - break; - } - if (ev.ctrlKey) { - this.scrollDisp(1); - return 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 cancel(ev); - } else { - key = '\x1b[5~'; - } - break; - // page down - case 34: - if (ev.shiftKey) { - this.scrollDisp(this.rows - 1); - return 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) { - // Ctrl-A - if (this.screenKeys) { - if (!this.prefixMode && !this.selectMode && ev.keyCode === 65) { - this.enterPrefix(); - return cancel(ev); - } - } - // Ctrl-V - if (this.prefixMode && ev.keyCode === 86) { - this.leavePrefix(); - return; - } - // Ctrl-C - if ((this.prefixMode || this.selectMode) && ev.keyCode === 67) { - if (this.visualMode) { - setTimeout(function() { - self.leaveVisual(); - }, 1); - } - return; - } - 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 (ev.altKey) { - 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) return true; - - if (this.prefixMode) { - this.leavePrefix(); - return cancel(ev); - } - - if (this.selectMode) { - this.keySelect(ev, key); - return cancel(ev); - } - - this.emit('keydown', ev); - this.emit('key', key, ev); - - this.showCursor(); - this.handler(key); - - return 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; - - 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); - - if (this.prefixMode) { - this.leavePrefix(); - this.keyPrefix(ev, key); - return false; - } - - if (this.selectMode) { - this.keySelect(ev, key); - return false; - } - - 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() { - this.emit('bell'); - 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; - this.columns = 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) { - line = this.document.createElement('div'); - el.appendChild(line); - this.children.push(line); - } - } - } 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 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); - - // it's a real nightmare trying - // to resize the original - // screen buffer. just set it - // to null for now. - this.normal = null; - - // Act as though we are a node TTY stream: - this.emit('resize'); -}; - -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; -}; - -// back_color_erase feature for xterm. -Terminal.prototype.eraseAttr = function() { - // if (this.is('screen')) return this.defAttr; - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); -}; - -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: - 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; - } - } 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; - } - } 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; -}; - -// 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; - } - } -}; - -/** - * 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: ^[[<b;x;yM - // release: ^[[<b;x;ym - break; - case 1015: // urxvt ext mode mouse - this.urxvtMouse = true; - // for wide terminals - // numbers for fields - // press: ^[[b;x;yM - // motion: ^[[b;x;yT - break; - case 25: // show cursor - this.cursorHidden = false; - break; - case 1049: // alt screen buffer cursor - //this.saveCursor(); - ; // FALL-THROUGH - case 47: // alt screen buffer - case 1047: // alt screen buffer - if (!this.normal) { - var normal = { - lines: this.lines, - ybase: this.ybase, - ydisp: this.ydisp, - x: this.x, - y: this.y, - scrollTop: this.scrollTop, - scrollBottom: this.scrollBottom, - tabs: this.tabs - // XXX save charset(s) here? - // charset: this.charset, - // glevel: this.glevel, - // charsets: this.charsets - }; - this.reset(); - this.normal = normal; - this.showCursor(); - } - break; - } - } -}; - -// CSI Pm l Reset Mode (RM). -// Ps = 2 -> Keyboard Action Mode (AM). -// Ps = 4 -> Replace Mode (IRM). -// Ps = 1 2 -> Send/receive (SRM). -// Ps = 2 0 -> Normal Linefeed (LNM). -// CSI ? Pm l -// DEC Private Mode Reset (DECRST). -// Ps = 1 -> Normal Cursor Keys (DECCKM). -// Ps = 2 -> Designate VT52 mode (DECANM). -// Ps = 3 -> 80 Column Mode (DECCOLM). -// Ps = 4 -> Jump (Fast) Scroll (DECSCLM). -// Ps = 5 -> Normal Video (DECSCNM). -// Ps = 6 -> Normal Cursor Mode (DECOM). -// Ps = 7 -> No Wraparound Mode (DECAWM). -// Ps = 8 -> No Auto-repeat Keys (DECARM). -// Ps = 9 -> Don't send Mouse X & Y on button press. -// Ps = 1 0 -> Hide toolbar (rxvt). -// Ps = 1 2 -> Stop Blinking Cursor (att610). -// Ps = 1 8 -> Don't print form feed (DECPFF). -// Ps = 1 9 -> Limit print to scrolling region (DECPEX). -// Ps = 2 5 -> Hide Cursor (DECTCEM). -// Ps = 3 0 -> Don't show scrollbar (rxvt). -// Ps = 3 5 -> Disable font-shifting functions (rxvt). -// Ps = 4 0 -> Disallow 80 -> 132 Mode. -// Ps = 4 1 -> No more(1) fix (see curses resource). -// Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- -// NRCM). -// Ps = 4 4 -> Turn Off Margin Bell. -// Ps = 4 5 -> No Reverse-wraparound Mode. -// Ps = 4 6 -> Stop Logging. (This is normally disabled by a -// compile-time option). -// Ps = 4 7 -> Use Normal Screen Buffer. -// Ps = 6 6 -> Numeric keypad (DECNKM). -// Ps = 6 7 -> Backarrow key sends delete (DECBKM). -// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and -// release. See the section Mouse Tracking. -// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. -// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. -// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. -// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. -// Ps = 1 0 0 5 -> Disable Extended Mouse Mode. -// Ps = 1 0 1 0 -> Don't scroll to bottom on tty output -// (rxvt). -// Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). -// Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables -// the eightBitInput resource). -// Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- -// Lock keys. (This disables the numLock resource). -// Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. -// (This disables the metaSendsEscape resource). -// Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad -// Delete key. -// Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. -// (This disables the altSendsEscape resource). -// Ps = 1 0 4 0 -> Do not keep selection when not highlighted. -// (This disables the keepSelection resource). -// Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables -// the selectToClipboard resource). -// Ps = 1 0 4 2 -> Disable Urgency window manager hint when -// Control-G is received. (This disables the bellIsUrgent -// resource). -// Ps = 1 0 4 3 -> Disable raising of the window when Control- -// G is received. (This disables the popOnBell resource). -// Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen -// first if in the Alternate Screen. (This may be disabled by -// the titeInhibit resource). -// Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be -// disabled by the titeInhibit resource). -// Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor -// as in DECRC. (This may be disabled by the titeInhibit -// resource). This combines the effects of the 1 0 4 7 and 1 0 -// 4 8 modes. Use this with terminfo-based applications rather -// than the 4 7 mode. -// Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. -// Ps = 1 0 5 1 -> Reset Sun function-key mode. -// Ps = 1 0 5 2 -> Reset HP function-key mode. -// Ps = 1 0 5 3 -> Reset SCO function-key mode. -// Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). -// Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. -// Ps = 2 0 0 4 -> Reset bracketed paste mode. -Terminal.prototype.resetMode = function(params) { - if (typeof 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 ? Pm s -// Save DEC Private Mode Values. Ps values are the same as for -// DECSET. -Terminal.prototype.savePrivateValues = function(params) { - ; -}; - -// CSI Ps ; Ps ; Ps t -// Window manipulation (from dtterm, as well as extensions). -// These controls may be disabled using the allowWindowOps -// resource. Valid values for the first (and any additional -// parameters) are: -// Ps = 1 -> De-iconify window. -// Ps = 2 -> Iconify window. -// Ps = 3 ; x ; y -> Move window to [x, y]. -// Ps = 4 ; height ; width -> Resize the xterm window to -// height and width in pixels. -// Ps = 5 -> Raise the xterm window to the front of the stack- -// ing order. -// Ps = 6 -> Lower the xterm window to the bottom of the -// stacking order. -// Ps = 7 -> Refresh the xterm window. -// Ps = 8 ; height ; width -> Resize the text area to -// [height;width] in characters. -// Ps = 9 ; 0 -> Restore maximized window. -// Ps = 9 ; 1 -> Maximize window (i.e., resize to screen -// size). -// Ps = 1 0 ; 0 -> Undo full-screen mode. -// Ps = 1 0 ; 1 -> Change to full-screen. -// Ps = 1 1 -> Report xterm window state. If the xterm window -// is open (non-iconified), it returns CSI 1 t . If the xterm -// window is iconified, it returns CSI 2 t . -// Ps = 1 3 -> Report xterm window position. Result is CSI 3 -// ; x ; y t -// Ps = 1 4 -> Report xterm window in pixels. Result is CSI -// 4 ; height ; width t -// Ps = 1 8 -> Report the size of the text area in characters. -// Result is CSI 8 ; height ; width t -// Ps = 1 9 -> Report the size of the screen in characters. -// Result is CSI 9 ; height ; width t -// Ps = 2 0 -> Report xterm window's icon label. Result is -// OSC L label ST -// Ps = 2 1 -> Report xterm window's title. Result is OSC l -// label ST -// Ps = 2 2 ; 0 -> Save xterm icon and window title on -// stack. -// Ps = 2 2 ; 1 -> Save xterm icon title on stack. -// Ps = 2 2 ; 2 -> Save xterm window title on stack. -// Ps = 2 3 ; 0 -> Restore xterm icon and window title from -// stack. -// Ps = 2 3 ; 1 -> Restore xterm icon title from stack. -// Ps = 2 3 ; 2 -> Restore xterm window title from stack. -// Ps >= 2 4 -> Resize to Ps lines (DECSLPP). -Terminal.prototype.manipulateWindow = function(params) { - ; -}; - -// CSI Pt; Pl; Pb; Pr; Ps$ t -// Reverse Attributes in Rectangular Area (DECRARA), VT400 and -// up. -// Pt; Pl; Pb; Pr denotes the rectangle. -// Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.reverseAttrInRectangle = function(params) { - ; -}; - -// CSI > Ps; Ps t -// Set one or more features of the title modes. Each parameter -// enables a single feature. -// Ps = 0 -> Set window/icon labels using hexadecimal. -// Ps = 1 -> Query window/icon labels using hexadecimal. -// Ps = 2 -> Set window/icon labels using UTF-8. -// Ps = 3 -> Query window/icon labels using UTF-8. (See dis- -// cussion of "Title Modes") -Terminal.prototype.setTitleModeFeature = function(params) { - ; -}; - -// CSI Ps SP t -// Set warning-bell volume (DECSWBV, VT520). -// Ps = 0 or 1 -> off. -// Ps = 2 , 3 or 4 -> low. -// Ps = 5 , 6 , 7 , or 8 -> high. -Terminal.prototype.setWarningBellVolume = function(params) { - ; -}; - -// CSI Ps SP u -// Set margin-bell volume (DECSMBV, VT520). -// Ps = 1 -> off. -// Ps = 2 , 3 or 4 -> low. -// Ps = 0 , 5 , 6 , 7 , or 8 -> high. -Terminal.prototype.setMarginBellVolume = function(params) { - ; -}; - -// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v -// Copy Rectangular Area (DECCRA, VT400 and up). -// Pt; Pl; Pb; Pr denotes the rectangle. -// Pp denotes the source page. -// Pt; Pl denotes the target location. -// Pp denotes the target page. -// NOTE: xterm doesn't enable this code by default. -Terminal.prototype.copyRectangle = function(params) { - ; -}; - -// CSI Pt ; Pl ; Pb ; Pr ' w -// Enable Filter Rectangle (DECEFR), VT420 and up. -// Parameters are [top;left;bottom;right]. -// Defines the coordinates of a filter rectangle and activates -// it. Anytime the locator is detected outside of the filter -// rectangle, an outside rectangle event is generated and the -// rectangle is disabled. Filter rectangles are always treated -// as "one-shot" events. Any parameters that are omitted default -// to the current locator position. If all parameters are omit- -// ted, any locator motion will be reported. DECELR always can- -// cels any prevous rectangle definition. -Terminal.prototype.enableFilterRectangle = function(params) { - ; -}; - -// CSI Ps x Request Terminal Parameters (DECREQTPARM). -// if Ps is a "0" (default) or "1", and xterm is emulating VT100, -// the control sequence elicits a response of the same form whose -// parameters describe the terminal: -// Ps -> the given Ps incremented by 2. -// Pn = 1 <- no parity. -// Pn = 1 <- eight bits. -// Pn = 1 <- 2 8 transmit 38.4k baud. -// Pn = 1 <- 2 8 receive 38.4k baud. -// Pn = 1 <- clock multiplier. -// Pn = 0 <- STP flags. -Terminal.prototype.requestParameters = function(params) { - ; -}; - -// CSI Ps x Select Attribute Change Extent (DECSACE). -// Ps = 0 -> from start to end position, wrapped. -// Ps = 1 -> from start to end position, wrapped. -// Ps = 2 -> rectangle (exact). -Terminal.prototype.selectChangeExtent = function(params) { - ; -}; - -// 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 Pm ' { -// Select Locator Events (DECSLE). -// Valid values for the first (and any additional parameters) -// are: -// Ps = 0 -> only respond to explicit host requests (DECRQLP). -// (This is default). It also cancels any filter -// rectangle. -// Ps = 1 -> report button down transitions. -// Ps = 2 -> do not report button down transitions. -// Ps = 3 -> report button up transitions. -// Ps = 4 -> do not report button up transitions. -Terminal.prototype.setLocatorEvents = function(params) { - ; -}; - -// CSI Pt; Pl; Pb; Pr$ { -// Selective Erase Rectangular Area (DECSERA), VT400 and up. -// Pt; Pl; Pb; Pr denotes the rectangle. -Terminal.prototype.selectiveEraseRectangle = function(params) { - ; -}; - -// CSI Ps ' | -// Request Locator Position (DECRQLP). -// Valid values for the parameter are: -// Ps = 0 , 1 or omitted -> transmit a single DECLRP locator -// report. - -// If Locator Reporting has been enabled by a DECELR, xterm will -// respond with a DECLRP Locator Report. This report is also -// generated on button up and down events if they have been -// enabled with a DECSLE, or when the locator is detected outside -// of a filter rectangle, if filter rectangles have been enabled -// with a DECEFR. - -// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w - -// Parameters are [event;button;row;column;page]. -// Valid values for the event: -// Pe = 0 -> locator unavailable - no other parameters sent. -// Pe = 1 -> request - xterm received a DECRQLP. -// Pe = 2 -> left button down. -// Pe = 3 -> left button up. -// Pe = 4 -> middle button down. -// Pe = 5 -> middle button up. -// Pe = 6 -> right button down. -// Pe = 7 -> right button up. -// Pe = 8 -> M4 button down. -// Pe = 9 -> M4 button up. -// Pe = 1 0 -> locator outside filter rectangle. -// ``button'' parameter is a bitmask indicating which buttons are -// pressed: -// Pb = 0 <- no buttons down. -// Pb & 1 <- right button down. -// Pb & 2 <- middle button down. -// Pb & 4 <- left button down. -// Pb & 8 <- M4 button down. -// ``row'' and ``column'' parameters are the coordinates of the -// locator position in the xterm window, encoded as ASCII deci- -// mal. -// The ``page'' parameter is not used by xterm, and will be omit- -// ted. -Terminal.prototype.requestLocatorPosition = function(params) { - ; -}; - -// 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(); -}; - -/** - * Prefix/Select/Visual/Search Modes - */ - -Terminal.prototype.enterPrefix = function() { - this.prefixMode = true; -}; - -Terminal.prototype.leavePrefix = function() { - this.prefixMode = false; -}; - -Terminal.prototype.enterSelect = function() { - this._real = { - x: this.x, - y: this.y, - ydisp: this.ydisp, - ybase: this.ybase, - cursorHidden: this.cursorHidden, - lines: this.copyBuffer(this.lines), - write: this.write - }; - this.write = function() {}; - this.selectMode = true; - this.visualMode = false; - this.cursorHidden = false; - this.refresh(this.y, this.y); -}; - -Terminal.prototype.leaveSelect = function() { - this.x = this._real.x; - this.y = this._real.y; - this.ydisp = this._real.ydisp; - this.ybase = this._real.ybase; - this.cursorHidden = this._real.cursorHidden; - this.lines = this._real.lines; - this.write = this._real.write; - delete this._real; - this.selectMode = false; - this.visualMode = false; - this.refresh(0, this.rows - 1); -}; - -Terminal.prototype.enterVisual = function() { - this._real.preVisual = this.copyBuffer(this.lines); - this.selectText(this.x, this.x, this.ydisp + this.y, this.ydisp + this.y); - this.visualMode = true; -}; - -Terminal.prototype.leaveVisual = function() { - this.lines = this._real.preVisual; - delete this._real.preVisual; - delete this._selected; - this.visualMode = false; - this.refresh(0, this.rows - 1); -}; - -Terminal.prototype.enterSearch = function(down) { - this.entry = ''; - this.searchMode = true; - this.searchDown = down; - this._real.preSearch = this.copyBuffer(this.lines); - this._real.preSearchX = this.x; - this._real.preSearchY = this.y; - - var bottom = this.ydisp + this.rows - 1; - for (var i = 0; i < this.entryPrefix.length; i++) { - //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; - //this.lines[bottom][i][1] = this.entryPrefix[i]; - this.lines[bottom][i] = [ - (this.defAttr & ~0x1ff) | 4, - this.entryPrefix[i] - ]; - } - - this.y = this.rows - 1; - this.x = this.entryPrefix.length; - - this.refresh(this.rows - 1, this.rows - 1); -}; - -Terminal.prototype.leaveSearch = function() { - this.searchMode = false; - - if (this._real.preSearch) { - this.lines = this._real.preSearch; - this.x = this._real.preSearchX; - this.y = this._real.preSearchY; - delete this._real.preSearch; - delete this._real.preSearchX; - delete this._real.preSearchY; - } - - this.refresh(this.rows - 1, this.rows - 1); -}; - -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.selectText = function(x1, x2, y1, y2) { - var ox1 - , ox2 - , oy1 - , oy2 - , tmp - , x - , y - , xl - , attr; - - if (this._selected) { - ox1 = this._selected.x1; - ox2 = this._selected.x2; - oy1 = this._selected.y1; - oy2 = this._selected.y2; - - if (oy2 < oy1) { - tmp = ox2; - ox2 = ox1; - ox1 = tmp; - tmp = oy2; - oy2 = oy1; - oy1 = tmp; - } - - if (ox2 < ox1 && oy1 === oy2) { - tmp = ox2; - ox2 = ox1; - ox1 = tmp; - } - - for (y = oy1; y <= oy2; y++) { - x = 0; - xl = this.cols - 1; - if (y === oy1) { - x = ox1; - } - if (y === oy2) { - xl = ox2; - } - for (; x <= xl; x++) { - if (this.lines[y][x].old != null) { - //this.lines[y][x][0] = this.lines[y][x].old; - //delete this.lines[y][x].old; - attr = this.lines[y][x].old; - delete this.lines[y][x].old; - this.lines[y][x] = [attr, this.lines[y][x][1]]; - } - } - } - - y1 = this._selected.y1; - x1 = this._selected.x1; - } - - y1 = Math.max(y1, 0); - y1 = Math.min(y1, this.ydisp + this.rows - 1); - - y2 = Math.max(y2, 0); - y2 = Math.min(y2, this.ydisp + this.rows - 1); - - this._selected = { x1: x1, x2: x2, y1: y1, y2: y2 }; - - if (y2 < y1) { - tmp = x2; - x2 = x1; - x1 = tmp; - tmp = y2; - y2 = y1; - y1 = tmp; - } - - if (x2 < x1 && y1 === y2) { - tmp = x2; - x2 = x1; - x1 = tmp; - } - - for (y = y1; y <= y2; y++) { - x = 0; - xl = this.cols - 1; - if (y === y1) { - x = x1; - } - if (y === y2) { - xl = x2; - } - for (; x <= xl; x++) { - //this.lines[y][x].old = this.lines[y][x][0]; - //this.lines[y][x][0] &= ~0x1ff; - //this.lines[y][x][0] |= (0x1ff << 9) | 4; - attr = this.lines[y][x][0]; - this.lines[y][x] = [ - (attr & ~0x1ff) | ((0x1ff << 9) | 4), - this.lines[y][x][1] - ]; - this.lines[y][x].old = attr; - } - } - - y1 = y1 - this.ydisp; - y2 = y2 - this.ydisp; - - y1 = Math.max(y1, 0); - y1 = Math.min(y1, this.rows - 1); - - y2 = Math.max(y2, 0); - y2 = Math.min(y2, this.rows - 1); - - //this.refresh(y1, y2); - this.refresh(0, this.rows - 1); -}; - -Terminal.prototype.grabText = function(x1, x2, y1, y2) { - var out = '' - , buf = '' - , ch - , x - , y - , xl - , tmp; - - if (y2 < y1) { - tmp = x2; - x2 = x1; - x1 = tmp; - tmp = y2; - y2 = y1; - y1 = tmp; - } - - if (x2 < x1 && y1 === y2) { - tmp = x2; - x2 = x1; - x1 = tmp; - } - - for (y = y1; y <= y2; y++) { - x = 0; - xl = this.cols - 1; - if (y === y1) { - x = x1; - } - if (y === y2) { - xl = x2; - } - for (; x <= xl; x++) { - ch = this.lines[y][x][1]; - if (ch === ' ') { - buf += ch; - continue; - } - if (buf) { - out += buf; - buf = ''; - } - out += ch; - if (isWide(ch)) x++; - } - buf = ''; - out += '\n'; - } - - // If we're not at the end of the - // line, don't add a newline. - for (x = x2, y = y2; x < this.cols; x++) { - if (this.lines[y][x][1] !== ' ') { - out = out.slice(0, -1); - break; - } - } - - return out; -}; - -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'); - } else if (key === '[') { - this.enterSelect(); - } -}; - -Terminal.prototype.keySelect = function(ev, key) { - this.showCursor(); - - if (this.searchMode || key === 'n' || key === 'N') { - return this.keySearch(ev, key); - } - - 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); - } - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } - 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); - } - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } - return; - } - - if (key === '\x06') { // ctrl-f - var y = this.ydisp + this.y; - this.scrollDisp(this.rows - 1); - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } - return; - } - - if (key === '\x02') { // ctrl-b - var y = this.ydisp + this.y; - this.scrollDisp(-(this.rows - 1)); - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } - 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); - } - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } else { - 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); - } - if (this.visualMode) { - this.selectText(this.x, this.x, y, this.ydisp + this.y); - } else { - 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; - } - if (this.visualMode) { - this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); - } else { - 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; - } - if (this.visualMode) { - this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); - } else { - this.refresh(this.y, this.y); - } - return; - } - - if (key === 'v' || key === ' ') { - if (!this.visualMode) { - this.enterVisual(); - } else { - this.leaveVisual(); - } - return; - } - - if (key === 'y') { - if (this.visualMode) { - var text = this.grabText( - this._selected.x1, this._selected.x2, - this._selected.y1, this._selected.y2); - this.copyText(text); - this.leaveVisual(); - // this.leaveSelect(); - } - return; - } - - if (key === 'q' || key === '\x1b') { - if (this.visualMode) { - this.leaveVisual(); - } else { - this.leaveSelect(); - } - 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++; - } - 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); - - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - return; - } - - if (key === 'b' || key === 'B') { - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; - - 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; - } - - this.x = x, this.y = y; - this.scrollDisp(-this.ydisp + yb); - - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - 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); - - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - 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; - } - - if (this.visualMode) { - this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); - } else { - 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) { - if (line[x][1] > ' ') { - if (this.visualMode && x < this.cols - 1) x++; - break; - } - x--; - } - if (x < 0) x = 0; - this.x = x; - if (this.visualMode) { - this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); - } else { - 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); - } - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - 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; - } - if (this.visualMode) { - this.selectText(ox, this.x, this.ydisp + oy, this.ydisp + this.y); - } else { - 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); - - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - return; - } - - if (key === '/' || key === '?') { - if (!this.visualMode) { - this.enterSearch(key === '/'); - } - return; - } - - return false; -}; - -Terminal.prototype.keySearch = function(ev, key) { - if (key === '\x1b') { - this.leaveSearch(); - return; - } - - if (key === '\r' || (!this.searchMode && (key === 'n' || key === 'N'))) { - this.leaveSearch(); - - var entry = this.entry; - - if (!entry) { - this.refresh(0, this.rows - 1); - return; - } - - var ox = this.x; - var oy = this.y; - var oyd = this.ydisp; - - var line; - var found = false; - var wrapped = false; - var x = this.x + 1; - var y = this.ydisp + this.y; - var yb, i; - var up = key === 'N' - ? this.searchDown - : !this.searchDown; - - for (;;) { - line = this.lines[y]; - - while (x < this.cols) { - for (i = 0; i < entry.length; i++) { - if (x + i >= this.cols) break; - if (line[x + i][1] !== entry[i]) { - break; - } else if (line[x + i][1] === entry[i] && i === entry.length - 1) { - found = true; - break; - } - } - if (found) break; - x += i + 1; - } - if (found) break; - - x = 0; - - if (!up) { - y++; - if (y > this.ybase + this.rows - 1) { - if (wrapped) break; - // this.setMessage('Search wrapped. Continuing at TOP.'); - wrapped = true; - y = 0; - } - } else { - y--; - if (y < 0) { - if (wrapped) break; - // this.setMessage('Search wrapped. Continuing at BOTTOM.'); - wrapped = true; - y = this.ybase + this.rows - 1; - } - } - } - - if (found) { - if (y - this.ybase < 0) { - yb = y; - y = 0; - if (yb > this.ybase) { - y = yb - this.ybase; - yb = this.ybase; - } - } else { - yb = this.ybase; - y -= this.ybase; - } - - this.x = x, this.y = y; - this.scrollDisp(-this.ydisp + yb); - - if (this.visualMode) { - this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); - } - return; - } - - // this.setMessage("No matches found."); - this.refresh(0, this.rows - 1); - - return; - } - - if (key === '\b' || key === '\x7f') { - if (this.entry.length === 0) return; - var bottom = this.ydisp + this.rows - 1; - this.entry = this.entry.slice(0, -1); - var i = this.entryPrefix.length + this.entry.length; - //this.lines[bottom][i][1] = ' '; - this.lines[bottom][i] = [ - this.lines[bottom][i][0], - ' ' - ]; - this.x--; - this.refresh(this.rows - 1, this.rows - 1); - this.refresh(this.y, this.y); - return; - } - - if (key.length === 1 && key >= ' ' && key <= '~') { - var bottom = this.ydisp + this.rows - 1; - this.entry += key; - var i = this.entryPrefix.length + this.entry.length - 1; - //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; - //this.lines[bottom][i][1] = key; - this.lines[bottom][i] = [ - (this.defAttr & ~0x1ff) | 4, - key - ]; - this.x++; - this.refresh(this.rows - 1, this.rows - 1); - this.refresh(this.y, this.y); - 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) { - el.addEventListener(type, handler, capture || false); -} - -function off(el, type, handler, capture) { - el.removeEventListener(type, handler, capture || false); -} - -function cancel(ev) { - if (ev.preventDefault) ev.preventDefault(); - ev.returnValue = false; - if (ev.stopPropagation) ev.stopPropagation(); - ev.cancelBubble = true; - 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 terminal = document.createElement('div'); - terminal.className = 'terminal'; - var line = document.createElement('div'); - var el = document.createElement('span'); - el.innerHTML = 'hello world'; - line.appendChild(el); - terminal.appendChild(line); - body.appendChild(terminal); - var w1 = el.scrollWidth; - el.style.fontWeight = 'bold'; - var w2 = el.scrollWidth; - body.removeChild(terminal); - 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; -} - -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.Stream = Stream; -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); -}()); +/** + * term.js - an xterm emulator + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +;(function() { + +/** + * Terminal Emulation References: + * http://vt100.net/ + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * http://invisible-island.net/vttest/ + * http://www.inwap.com/pdp10/ansicode.txt + * http://linux.die.net/man/4/console_codes + * http://linux.die.net/man/7/urxvt + */ + +'use strict'; + +/** + * 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] || []; +}; + +/** + * Stream + */ + +function Stream() { + EventEmitter.call(this); +} + +inherits(Stream, EventEmitter); + +Stream.prototype.pipe = function(dest, options) { + var src = this + , ondata + , onerror + , onend; + + function unbind() { + src.removeListener('data', ondata); + src.removeListener('error', onerror); + src.removeListener('end', onend); + dest.removeListener('error', onerror); + dest.removeListener('close', unbind); + } + + src.on('data', ondata = function(data) { + dest.write(data); + }); + + src.on('error', onerror = function(err) { + unbind(); + if (!this.listeners('error').length) { + throw err; + } + }); + + src.on('end', onend = function() { + dest.end(); + unbind(); + }); + + dest.on('error', onerror); + dest.on('close', unbind); + + dest.emit('pipe', src); + + return dest; +}; + +/** + * States + */ + +var normal = 0 + , escaped = 1 + , csi = 2 + , osc = 3 + , charset = 4 + , dcs = 5 + , ignore = 6 + , UDK = { type: 'udk' }; + +/** + * Terminal + */ + +function Terminal(options) { + var self = this; + + if (!(this instanceof Terminal)) { + return new Terminal(arguments[0], arguments[1], arguments[2]); + } + + Stream.call(this); + + if (typeof options === 'number') { + options = { + cols: arguments[0], + rows: arguments[1], + handler: arguments[2] + }; + } + + options = options || {}; + + each(keys(Terminal.defaults), function(key) { + if (options[key] == null) { + options[key] = Terminal.options[key]; + // Legacy: + 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.slice(0, -2).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]; + + // Act as though we are a node TTY stream: + this.setRawMode; + this.isTTY = true; + this.isRaw = true; + this.columns = this.cols; + this.rows = this.rows; + + 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; + + // select modes + this.prefixMode = false; + this.selectMode = false; + this.visualMode = false; + this.searchMode = false; + this.searchDown; + this.entry = ''; + this.entryPrefix = 'Search: '; + this._real; + this._selected; + this._textarea; + + // 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, Stream); + +/** + * Colors + */ + +// Colors 0-15 +Terminal.tangoColors = [ + // dark: + '#2e3436', + '#cc0000', + '#4e9a06', + '#c4a000', + '#3465a4', + '#75507b', + '#06989a', + '#d3d7cf', + // bright: + '#555753', + '#ef2929', + '#8ae234', + '#fce94f', + '#729fcf', + '#ad7fa8', + '#34e2e2', + '#eeeeec' +]; + +Terminal.xtermColors = [ + // dark: + '#000000', // black + '#cd0000', // red3 + '#00cd00', // green3 + '#cdcd00', // yellow3 + '#0000ee', // blue2 + '#cd00cd', // magenta3 + '#00cdcd', // cyan3 + '#e5e5e5', // gray90 + // bright: + '#7f7f7f', // gray50 + '#ff0000', // red + '#00ff00', // green + '#ffff00', // yellow + '#5c5cff', // rgb:5c/5c/ff + '#ff00ff', // magenta + '#00ffff', // cyan + '#ffffff' // white +]; + +// 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; +})(); + +// Default BG/FG +Terminal.colors[256] = '#000000'; +Terminal.colors[257] = '#f0f0f0'; + +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, + convertEol: false, + termName: 'xterm', + geometry: [80, 24], + cursorBlink: true, + visualBell: false, + popOnBell: false, + scrollback: 1000, + screenKeys: false, + debug: false, + useStyle: 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.focus = null; + +Terminal.prototype.focus = function() { + if (Terminal.focus === this) return; + + if (Terminal.focus) { + Terminal.focus.blur(); + } + + if (this.sendFocus) this.send('\x1b[I'); + this.showCursor(); + + // try { + // this.element.focus(); + // } catch (e) { + // ; + // } + + // this.emit('focus'); + + Terminal.focus = 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'); + + // try { + // this.element.blur(); + // } catch (e) { + // ; + // } + + // this.emit('blur'); + + Terminal.focus = null; +}; + +/** + * Initialize global behavior + */ + +Terminal.prototype.initGlobal = function() { + var document = this.document; + + Terminal._boundDocs = Terminal._boundDocs || []; + if (~indexOf(Terminal._boundDocs, document)) { + return; + } + Terminal._boundDocs.push(document); + + Terminal.bindPaste(document); + + Terminal.bindKeys(document); + + Terminal.bindCopy(document); + + if (this.isMobile) { + this.fixMobile(document); + } + + if (this.useStyle) { + Terminal.insertStyle(document, this.colors[256], this.colors[257]); + } +}; + +/** + * Bind to paste event + */ + +Terminal.bindPaste = function(document) { + // This seems to work well for ctrl-V and middle-click, + // even without the contentEditable workaround. + var window = document.defaultView; + on(window, 'paste', function(ev) { + var term = Terminal.focus; + if (!term) return; + if (ev.clipboardData) { + term.send(ev.clipboardData.getData('text/plain')); + } else if (term.context.clipboardData) { + term.send(term.context.clipboardData.getData('Text')); + } + // Not necessary. Do it anyway for good measure. + term.element.contentEditable = 'inherit'; + return cancel(ev); + }); +}; + +/** + * Global Events for key handling + */ + +Terminal.bindKeys = function(document) { + // We should only need to check `target === body` below, + // but we can check everything for good measure. + on(document, 'keydown', function(ev) { + if (!Terminal.focus) return; + var target = ev.target || ev.srcElement; + if (!target) return; + if (target === Terminal.focus.element + || target === Terminal.focus.context + || target === Terminal.focus.document + || target === Terminal.focus.body + || target === Terminal._textarea + || target === Terminal.focus.parent) { + return Terminal.focus.keyDown(ev); + } + }, true); + + on(document, 'keypress', function(ev) { + if (!Terminal.focus) return; + var target = ev.target || ev.srcElement; + if (!target) return; + if (target === Terminal.focus.element + || target === Terminal.focus.context + || target === Terminal.focus.document + || target === Terminal.focus.body + || target === Terminal._textarea + || target === Terminal.focus.parent) { + return Terminal.focus.keyPress(ev); + } + }, true); + + // If we click somewhere other than a + // terminal, unfocus the terminal. + on(document, 'mousedown', function(ev) { + if (!Terminal.focus) return; + + var el = ev.target || ev.srcElement; + if (!el) return; + + do { + if (el === Terminal.focus.element) return; + } while (el = el.parentNode); + + Terminal.focus.blur(); + }); +}; + +/** + * Copy Selection w/ Ctrl-C (Select Mode) + */ + +Terminal.bindCopy = function(document) { + var window = document.defaultView; + + // if (!('onbeforecopy' in document)) { + // // Copies to *only* the clipboard. + // on(window, 'copy', function fn(ev) { + // var term = Terminal.focus; + // if (!term) return; + // if (!term._selected) return; + // var text = term.grabText( + // term._selected.x1, term._selected.x2, + // term._selected.y1, term._selected.y2); + // term.emit('copy', text); + // ev.clipboardData.setData('text/plain', text); + // }); + // return; + // } + + // Copies to primary selection *and* clipboard. + // NOTE: This may work better on capture phase, + // or using the `beforecopy` event. + on(window, 'copy', function(ev) { + var term = Terminal.focus; + if (!term) return; + if (!term._selected) return; + var textarea = term.getCopyTextarea(); + var text = term.grabText( + term._selected.x1, term._selected.x2, + term._selected.y1, term._selected.y2); + term.emit('copy', text); + textarea.focus(); + textarea.textContent = text; + textarea.value = text; + textarea.setSelectionRange(0, text.length); + setTimeout(function() { + term.element.focus(); + term.focus(); + }, 1); + }); +}; + +/** + * Fix Mobile + */ + +Terminal.prototype.fixMobile = function(document) { + var self = this; + + var 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'; + textarea.autocapitalize = 'none'; + textarea.autocorrect = 'off'; + + document.getElementsByTagName('body')[0].appendChild(textarea); + + Terminal._textarea = textarea; + + setTimeout(function() { + textarea.focus(); + }, 1000); + + if (this.isAndroid) { + on(textarea, 'change', function() { + var value = textarea.textContent || textarea.value; + textarea.value = ''; + textarea.textContent = ''; + self.send(value + '\r'); + }); + } +}; + +/** + * Insert a default style + */ + +Terminal.insertStyle = function(document, bg, fg) { + var style = document.getElementById('term-style'); + if (style) return; + + var head = document.getElementsByTagName('head')[0]; + if (!head) return; + + var style = document.createElement('style'); + style.id = 'term-style'; + + // textContent doesn't work well with IE for <style> elements. + style.innerHTML = '' + + '.terminal {\n' + + ' float: left;\n' + + ' border: ' + bg + ' solid 5px;\n' + + ' font-family: "DejaVu Sans Mono", "Liberation Mono", monospace;\n' + + ' font-size: 11px;\n' + + ' color: ' + fg + ';\n' + + ' background: ' + bg + ';\n' + + '}\n' + + '\n' + + '.terminal-cursor {\n' + + ' color: ' + bg + ';\n' + + ' background: ' + fg + ';\n' + + '}\n'; + + // var out = ''; + // each(Terminal.colors, function(color, i) { + // if (i === 256) { + // out += '\n.term-bg-color-default { background-color: ' + color + '; }'; + // } + // if (i === 257) { + // out += '\n.term-fg-color-default { color: ' + color + '; }'; + // } + // out += '\n.term-bg-color-' + i + ' { background-color: ' + color + '; }'; + // out += '\n.term-fg-color-' + i + ' { color: ' + color + '; }'; + // }); + // style.innerHTML += out + '\n'; + + head.insertBefore(style, head.firstChild); +}; + +/** + * Open Terminal + */ + +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 strings. + 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.isAndroid = !!~this.context.navigator.userAgent.indexOf('Android'); + this.isMobile = this.isIpad || this.isIphone || this.isAndroid; + this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); + } + + // Create our main terminal element. + this.element = this.document.createElement('div'); + this.element.className = 'terminal'; + this.element.style.outline = 'none'; + this.element.setAttribute('tabindex', 0); + this.element.setAttribute('spellcheck', 'false'); + this.element.style.backgroundColor = this.colors[256]; + this.element.style.color = this.colors[257]; + + // Create the lines for our terminal. + this.children = []; + for (; i < this.rows; i++) { + div = this.document.createElement('div'); + this.element.appendChild(div); + this.children.push(div); + } + this.parent.appendChild(this.element); + + // Draw the screen. + this.refresh(0, this.rows - 1); + + if (!('useEvents' in this.options) || this.options.useEvents) { + // Initialize global actions that + // need to be taken on the document. + this.initGlobal(); + } + + if (!('useFocus' in this.options) || this.options.useFocus) { + // Ensure there is a Terminal.focus. + this.focus(); + + // Start blinking the cursor. + this.startBlink(); + + // Bind to DOM events related + // to focus and paste behavior. + on(this.element, 'focus', function() { + self.focus(); + if (self.isMobile) { + Terminal._textarea.focus(); + } + }); + + // This causes slightly funky behavior. + // on(this.element, 'blur', function() { + // self.blur(); + // }); + + on(this.element, 'mousedown', function() { + self.focus(); + }); + + // Clickable paste workaround, using contentEditable. + // This probably shouldn't work, + // ... but it does. Firefox's paste + // event seems to only work for textareas? + on(this.element, 'mousedown', function(ev) { + var button = ev.button != null + ? +ev.button + : ev.which != null + ? ev.which - 1 + : null; + + // Does IE9 do this? + if (self.isMSIE) { + button = button === 1 ? 0 : button === 4 ? 1 : button; + } + + if (button !== 2) return; + + self.element.contentEditable = 'true'; + setTimeout(function() { + self.element.contentEditable = 'inherit'; // 'false'; + }, 1); + }, true); + } + + if (!('useMouse' in this.options) || this.options.useMouse) { + // Listen for mouse events and translate + // them into terminal mouse protocols. + this.bindMouse(); + } + + // this.emit('open'); + + if (!('useFocus' in this.options) || this.options.useFocus) { + // This can be useful for pasting, + // as well as the iPad fix. + setTimeout(function() { + self.element.focus(); + }, 100); + } + + // Figure out whether boldness affects + // the character width of monospace fonts. + if (Terminal.brokenBold == null) { + Terminal.brokenBold = isBoldBroken(this.document); + } + + this.emit('open'); +}; + +Terminal.prototype.setRawMode = function(value) { + this.isRaw = !!value; +}; + +// 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); + + // ensure focus + self.focus(); + + // fix for odd bug + //if (self.vt200Mouse && !self.normalMouse) { + // XXX This seems to break certain programs. + // if (self.vt200Mouse) { + // sendButton({ __proto__: ev, type: 'mouseup' }); + // return 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 cancel(ev); + }); + } + + return cancel(ev); + }); + + //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 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 ? -5 : 5); + } else { + self.scrollDisp(ev.wheelDeltaY > 0 ? -5 : 5); + } + return cancel(ev); + }); +}; + +/** + * Destroy Terminal + */ + +Terminal.prototype.close = +Terminal.prototype.destroySoon = +Terminal.prototype.destroy = function() { + if (this.destroyed) { + return; + } + + if (this._blink) { + clearInterval(this._blink); + delete this._blink; + } + + this.readable = false; + this.writable = false; + this.destroyed = true; + this._events = {}; + + this.handler = function() {}; + this.write = function() {}; + this.end = function() {}; + + if (this.element.parentNode) { + this.element.parentNode.removeChild(this.element); + } + + this.emit('end'); + this.emit('close'); + this.emit('finish'); + this.emit('destroy'); +}; + +/** + * 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; + + if (end - start >= this.rows / 2) { + parent = this.element.parentNode; + if (parent) parent.removeChild(this.element); + } + + 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; + } + + for (; y <= end; y++) { + row = y + this.ydisp; + + line = this.lines[row]; + out = ''; + + if (y === this.y + && this.cursorState + && (this.ydisp === this.ybase || this.selectMode) + && !this.cursorHidden) { + x = this.x; + } else { + x = -1; + } + + attr = this.defAttr; + i = 0; + + for (; i < width; i++) { + data = line[i][0]; + ch = line[i][1]; + + if (i === x) data = -1; + + if (data !== attr) { + if (attr !== this.defAttr) { + out += '</span>'; + } + if (data !== this.defAttr) { + if (data === -1) { + out += '<span class="reverse-video terminal-cursor">'; + } else { + out += '<span style="'; + + bg = data & 0x1ff; + fg = (data >> 9) & 0x1ff; + flags = data >> 18; + + // bold + if (flags & 1) { + if (!Terminal.brokenBold) { + out += 'font-weight:bold;'; + } + // See: XTerm*boldColors + if (fg < 8) fg += 8; + } + + // underline + if (flags & 2) { + out += 'text-decoration:underline;'; + } + + // blink + if (flags & 4) { + if (flags & 2) { + out = out.slice(0, -1); + out += ' blink;'; + } else { + out += 'text-decoration:blink;'; + } + } + + // inverse + if (flags & 8) { + bg = (data >> 9) & 0x1ff; + fg = data & 0x1ff; + // Should inverse just be before the + // above boldColors effect instead? + if ((flags & 1) && fg < 8) fg += 8; + } + + // invisible + if (flags & 16) { + out += 'visibility:hidden;'; + } + + // out += '" class="' + // + 'term-bg-color-' + bg + // + ' ' + // + 'term-fg-color-' + fg + // + '">'; + + if (bg !== 256) { + out += 'background-color:' + + this.colors[bg] + + ';'; + } + + if (fg !== 257) { + out += 'color:' + + this.colors[fg] + + ';'; + } + + out += '">'; + } + } + } + + switch (ch) { + case '&': + out += '&amp;'; + break; + case '<': + out += '&lt;'; + break; + case '>': + out += '&gt;'; + break; + default: + if (ch <= ' ') { + out += '&nbsp;'; + } else { + if (isWide(ch)) i++; + out += ch; + } + break; + } + + attr = data; + } + + if (attr !== this.defAttr) { + out += '</span>'; + } + + this.children[y].innerHTML = out; + } + + if (parent) parent.appendChild(this.element); +}; + +Terminal.prototype._cursorBlink = function() { + if (Terminal.focus !== this) return; + this.cursorState ^= 1; + this.refresh(this.y, this.y); +}; + +Terminal.prototype.showCursor = function() { + if (!this.cursorState) { + this.cursorState = 1; + this.refresh(this.y, this.y); + } else { + // Temporarily disabled: + // this.refreshBlink(); + } +}; + +Terminal.prototype.startBlink = function() { + if (!this.cursorBlink) return; + var self = this; + this._blinker = function() { + self._cursorBlink(); + }; + this._blink = setInterval(this._blinker, 500); +}; + +Terminal.prototype.refreshBlink = function() { + if (!this.cursorBlink || !this._blink) return; + clearInterval(this._blink); + this._blink = setInterval(this._blinker, 500); +}; + +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); +}; + +Terminal.prototype.scrollDisp = function(disp) { + this.ydisp += disp; + + if (this.ydisp > this.ybase) { + this.ydisp = this.ybase; + } else if (this.ydisp < 0) { + this.ydisp = 0; + } + + this.refresh(0, this.rows - 1); +}; + +Terminal.prototype.write = function(data) { + var l = data.length + , i = 0 + , j + , cs + , ch; + + this.refreshStart = this.y; + this.refreshEnd = this.y; + + if (this.ybase !== this.ydisp) { + this.ydisp = this.ybase; + this.maxRange(); + } + + // this.log(JSON.stringify(data.replace(/\x1b/g, '^['))); + + for (; i < l; i++, this.lch = ch) { + ch = data[i]; + switch (this.state) { + case normal: + switch (ch) { + // '\0' + // case '\0': + // case '\200': + // break; + + // '\a' + case '\x07': + this.bell(); + break; + + // '\n', '\v', '\f' + case '\n': + case '\x0b': + case '\x0c': + if (this.convertEol) { + this.x = 0; + } + // TODO: Implement eat_newline_glitch. + // if (this.realX >= this.cols) break; + // this.realX = 0; + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + break; + + // '\r' + case '\r': + this.x = 0; + break; + + // '\b' + case '\x08': + if (this.x > 0) { + 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; + 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.prefix = ''; + this.currentParam = ''; + 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 '.': + 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; + } + 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 ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { + if (this.lch === '\x1b') { + if (typeof this.currentParam === 'string') { + this.currentParam = this.currentParam.slice(0, -1); + } else if (typeof this.currentParam == 'number') { + this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10; + } + } + + 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; + } + + // 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); + } + 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 ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { + // Workarounds: + if (this.prefix === 'tmux;\x1b') { + // `DCS tmux; Pt ST` may contain a Pt with an ST + // XXX Does tmux work this way? + // if (this.lch === '\x1b' & data[i + 1] === '\x1b' && data[i + 2] === '\\') { + // this.currentParam += ch; + // continue; + // } + // Tmux only accepts ST, not BEL: + if (ch === '\x07') { + this.currentParam += ch; + continue; + } + } + + if (this.lch === '\x1b') { + if (typeof this.currentParam === 'string') { + this.currentParam = this.currentParam.slice(0, -1); + } else if (typeof this.currentParam == 'number') { + this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10; + } + } + + this.params.push(this.currentParam); + + var pt = this.params[this.params.length - 1]; + + switch (this.prefix) { + // User-Defined Keys (DECUDK). + // DCS Ps; Ps| Pt ST + case UDK: + this.emit('udk', { + clearAll: this.params[0] === 0, + eraseBelow: this.params[0] === 1, + lockKeys: this.params[1] === 0, + dontLockKeys: this.params[1] === 1, + keyList: (this.params[2] + '').split(';').map(function(part) { + part = part.split('/'); + return { + keyCode: part[0], + hexKeyValue: part[1] + }; + }) + }); + break; + + // Request Status String (DECRQSS). + // DCS $ q Pt ST + // test: echo -e '\eP$q"p\e\\' + case '$q': + var valid = 0; + + switch (pt) { + // DECSCA + // CSI Ps " q + case '"q': + pt = '0"q'; + valid = 1; + break; + + // DECSCL + // CSI Ps ; Ps " p + case '"p': + pt = '61;0"p'; + valid = 1; + break; + + // DECSTBM + // CSI Ps ; Ps r + case 'r': + pt = '' + + (this.scrollTop + 1) + + ';' + + (this.scrollBottom + 1) + + 'r'; + valid = 1; + break; + + // SGR + // CSI Pm m + case 'm': + // TODO: Parse this.curAttr here. + // pt = '0m'; + // valid = 1; + valid = 0; // Not implemented. + break; + + default: + this.error('Unknown DCS Pt: %s.', pt); + valid = 0; // unimplemented + break; + } + + this.send('\x1bP' + valid + '$r' + pt + '\x1b\\'); + break; + + // Set Termcap/Terminfo Data (xterm, experimental). + // DCS + p Pt ST + case '+p': + this.emit('set terminfo', { + name: this.params[0] + }); + break; + + // Request Termcap/Terminfo String (xterm, experimental) + // Regular xterm does not even respond to this sequence. + // This can cause a small glitch in vim. + // DCS + q Pt ST + // test: echo -ne '\eP+q6b64\e\\' + case '+q': + var valid = false; + this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); + break; + + // Implement tmux sequence forwarding is + // someone uses term.js for a multiplexer. + // DCS tmux; ESC Pt ST + case 'tmux;\x1b': + this.emit('passthrough', pt); + break; + + default: + this.error('Unknown DCS prefix: %s.', pt); + break; + } + + this.currentParam = 0; + this.prefix = ''; + this.state = normal; + } else { + this.currentParam += ch; + if (!this.prefix) { + if (/^\d*;\d*\|/.test(this.currentParam)) { + this.prefix = UDK; + this.params = this.currentParam.split(/[;|]/).map(function(n) { + if (!n.length) return 0; + return +n; + }).slice(0, -1); + this.currentParam = ''; + } else if (/^[$+][a-zA-Z]/.test(this.currentParam) + || /^\w+;\x1b/.test(this.currentParam)) { + this.prefix = this.currentParam; + this.currentParam = ''; + } + } + } + break; + + case ignore: + // For PM and APC. + if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') { + this.state = normal; + } + break; + } + } + + this.updateRange(this.y); + this.refresh(this.refreshStart, this.refreshEnd); + + return true; +}; + +Terminal.prototype.writeln = function(data) { + return this.write(data + '\r\n'); +}; + +Terminal.prototype.end = function(data) { + var ret = true; + if (data) { + ret = this.write(data); + } + this.destroySoon(); + return ret; +}; + +Terminal.prototype.resume = function() { + ; +}; + +Terminal.prototype.pause = function() { + ; +}; + +// 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.altKey) { + key = '\x17'; + break; + } + if (ev.shiftKey) { + key = '\x08'; // ^H + break; + } + key = '\x7f'; // ^? + break; + // tab + case 9: + if (ev.shiftKey) { + key = '\x1b[Z'; + break; + } + key = '\t'; + break; + // return/enter + case 13: + key = '\r'; + break; + // escape + case 27: + key = '\x1b'; + 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; + } + if (ev.ctrlKey) { + key = '\x1b[5D'; + break; + } + key = '\x1b[D'; + break; + // right-arrow + case 39: + if (this.applicationCursor) { + key = '\x1bOC'; + break; + } + if (ev.ctrlKey) { + key = '\x1b[5C'; + break; + } + key = '\x1b[C'; + break; + // up-arrow + case 38: + if (this.applicationCursor) { + key = '\x1bOA'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(-1); + return cancel(ev); + } else { + key = '\x1b[A'; + } + break; + // down-arrow + case 40: + if (this.applicationCursor) { + key = '\x1bOB'; + break; + } + if (ev.ctrlKey) { + this.scrollDisp(1); + return 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 cancel(ev); + } else { + key = '\x1b[5~'; + } + break; + // page down + case 34: + if (ev.shiftKey) { + this.scrollDisp(this.rows - 1); + return 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) { + // Ctrl-A + if (this.screenKeys) { + if (!this.prefixMode && !this.selectMode && ev.keyCode === 65) { + this.enterPrefix(); + return cancel(ev); + } + } + // Ctrl-V + if (this.prefixMode && ev.keyCode === 86) { + this.leavePrefix(); + return; + } + // Ctrl-C + if ((this.prefixMode || this.selectMode) && ev.keyCode === 67) { + if (this.visualMode) { + setTimeout(function() { + self.leaveVisual(); + }, 1); + } + return; + } + 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 (ev.altKey) { + 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) return true; + + if (this.prefixMode) { + this.leavePrefix(); + return cancel(ev); + } + + if (this.selectMode) { + this.keySelect(ev, key); + return cancel(ev); + } + + this.emit('keydown', ev); + this.emit('key', key, ev); + + this.showCursor(); + this.handler(key); + + return 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; + + 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); + + if (this.prefixMode) { + this.leavePrefix(); + this.keyPrefix(ev, key); + return false; + } + + if (this.selectMode) { + this.keySelect(ev, key); + return false; + } + + 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() { + this.emit('bell'); + 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; + this.columns = 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) { + line = this.document.createElement('div'); + el.appendChild(line); + this.children.push(line); + } + } + } 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 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); + + // it's a real nightmare trying + // to resize the original + // screen buffer. just set it + // to null for now. + this.normal = null; + + // Act as though we are a node TTY stream: + this.emit('resize'); +}; + +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; +}; + +// back_color_erase feature for xterm. +Terminal.prototype.eraseAttr = function() { + // if (this.is('screen')) return this.defAttr; + return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); +}; + +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: + 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; + } + } 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; + } + } 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; +}; + +// 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; + } + } +}; + +/** + * 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: ^[[<b;x;yM + // release: ^[[<b;x;ym + break; + case 1015: // urxvt ext mode mouse + this.urxvtMouse = true; + // for wide terminals + // numbers for fields + // press: ^[[b;x;yM + // motion: ^[[b;x;yT + break; + case 25: // show cursor + this.cursorHidden = false; + break; + case 1049: // alt screen buffer cursor + //this.saveCursor(); + ; // FALL-THROUGH + case 47: // alt screen buffer + case 1047: // alt screen buffer + if (!this.normal) { + var normal = { + lines: this.lines, + ybase: this.ybase, + ydisp: this.ydisp, + x: this.x, + y: this.y, + scrollTop: this.scrollTop, + scrollBottom: this.scrollBottom, + tabs: this.tabs + // XXX save charset(s) here? + // charset: this.charset, + // glevel: this.glevel, + // charsets: this.charsets + }; + this.reset(); + this.normal = normal; + this.showCursor(); + } + break; + } + } +}; + +// CSI Pm l Reset Mode (RM). +// Ps = 2 -> Keyboard Action Mode (AM). +// Ps = 4 -> Replace Mode (IRM). +// Ps = 1 2 -> Send/receive (SRM). +// Ps = 2 0 -> Normal Linefeed (LNM). +// CSI ? Pm l +// DEC Private Mode Reset (DECRST). +// Ps = 1 -> Normal Cursor Keys (DECCKM). +// Ps = 2 -> Designate VT52 mode (DECANM). +// Ps = 3 -> 80 Column Mode (DECCOLM). +// Ps = 4 -> Jump (Fast) Scroll (DECSCLM). +// Ps = 5 -> Normal Video (DECSCNM). +// Ps = 6 -> Normal Cursor Mode (DECOM). +// Ps = 7 -> No Wraparound Mode (DECAWM). +// Ps = 8 -> No Auto-repeat Keys (DECARM). +// Ps = 9 -> Don't send Mouse X & Y on button press. +// Ps = 1 0 -> Hide toolbar (rxvt). +// Ps = 1 2 -> Stop Blinking Cursor (att610). +// Ps = 1 8 -> Don't print form feed (DECPFF). +// Ps = 1 9 -> Limit print to scrolling region (DECPEX). +// Ps = 2 5 -> Hide Cursor (DECTCEM). +// Ps = 3 0 -> Don't show scrollbar (rxvt). +// Ps = 3 5 -> Disable font-shifting functions (rxvt). +// Ps = 4 0 -> Disallow 80 -> 132 Mode. +// Ps = 4 1 -> No more(1) fix (see curses resource). +// Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- +// NRCM). +// Ps = 4 4 -> Turn Off Margin Bell. +// Ps = 4 5 -> No Reverse-wraparound Mode. +// Ps = 4 6 -> Stop Logging. (This is normally disabled by a +// compile-time option). +// Ps = 4 7 -> Use Normal Screen Buffer. +// Ps = 6 6 -> Numeric keypad (DECNKM). +// Ps = 6 7 -> Backarrow key sends delete (DECBKM). +// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and +// release. See the section Mouse Tracking. +// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. +// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. +// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. +// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. +// Ps = 1 0 0 5 -> Disable Extended Mouse Mode. +// Ps = 1 0 1 0 -> Don't scroll to bottom on tty output +// (rxvt). +// Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). +// Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables +// the eightBitInput resource). +// Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- +// Lock keys. (This disables the numLock resource). +// Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. +// (This disables the metaSendsEscape resource). +// Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad +// Delete key. +// Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. +// (This disables the altSendsEscape resource). +// Ps = 1 0 4 0 -> Do not keep selection when not highlighted. +// (This disables the keepSelection resource). +// Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables +// the selectToClipboard resource). +// Ps = 1 0 4 2 -> Disable Urgency window manager hint when +// Control-G is received. (This disables the bellIsUrgent +// resource). +// Ps = 1 0 4 3 -> Disable raising of the window when Control- +// G is received. (This disables the popOnBell resource). +// Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen +// first if in the Alternate Screen. (This may be disabled by +// the titeInhibit resource). +// Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be +// disabled by the titeInhibit resource). +// Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor +// as in DECRC. (This may be disabled by the titeInhibit +// resource). This combines the effects of the 1 0 4 7 and 1 0 +// 4 8 modes. Use this with terminfo-based applications rather +// than the 4 7 mode. +// Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. +// Ps = 1 0 5 1 -> Reset Sun function-key mode. +// Ps = 1 0 5 2 -> Reset HP function-key mode. +// Ps = 1 0 5 3 -> Reset SCO function-key mode. +// Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). +// Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. +// Ps = 2 0 0 4 -> Reset bracketed paste mode. +Terminal.prototype.resetMode = function(params) { + if (typeof 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 ? Pm s +// Save DEC Private Mode Values. Ps values are the same as for +// DECSET. +Terminal.prototype.savePrivateValues = function(params) { + ; +}; + +// CSI Ps ; Ps ; Ps t +// Window manipulation (from dtterm, as well as extensions). +// These controls may be disabled using the allowWindowOps +// resource. Valid values for the first (and any additional +// parameters) are: +// Ps = 1 -> De-iconify window. +// Ps = 2 -> Iconify window. +// Ps = 3 ; x ; y -> Move window to [x, y]. +// Ps = 4 ; height ; width -> Resize the xterm window to +// height and width in pixels. +// Ps = 5 -> Raise the xterm window to the front of the stack- +// ing order. +// Ps = 6 -> Lower the xterm window to the bottom of the +// stacking order. +// Ps = 7 -> Refresh the xterm window. +// Ps = 8 ; height ; width -> Resize the text area to +// [height;width] in characters. +// Ps = 9 ; 0 -> Restore maximized window. +// Ps = 9 ; 1 -> Maximize window (i.e., resize to screen +// size). +// Ps = 1 0 ; 0 -> Undo full-screen mode. +// Ps = 1 0 ; 1 -> Change to full-screen. +// Ps = 1 1 -> Report xterm window state. If the xterm window +// is open (non-iconified), it returns CSI 1 t . If the xterm +// window is iconified, it returns CSI 2 t . +// Ps = 1 3 -> Report xterm window position. Result is CSI 3 +// ; x ; y t +// Ps = 1 4 -> Report xterm window in pixels. Result is CSI +// 4 ; height ; width t +// Ps = 1 8 -> Report the size of the text area in characters. +// Result is CSI 8 ; height ; width t +// Ps = 1 9 -> Report the size of the screen in characters. +// Result is CSI 9 ; height ; width t +// Ps = 2 0 -> Report xterm window's icon label. Result is +// OSC L label ST +// Ps = 2 1 -> Report xterm window's title. Result is OSC l +// label ST +// Ps = 2 2 ; 0 -> Save xterm icon and window title on +// stack. +// Ps = 2 2 ; 1 -> Save xterm icon title on stack. +// Ps = 2 2 ; 2 -> Save xterm window title on stack. +// Ps = 2 3 ; 0 -> Restore xterm icon and window title from +// stack. +// Ps = 2 3 ; 1 -> Restore xterm icon title from stack. +// Ps = 2 3 ; 2 -> Restore xterm window title from stack. +// Ps >= 2 4 -> Resize to Ps lines (DECSLPP). +Terminal.prototype.manipulateWindow = function(params) { + ; +}; + +// CSI Pt; Pl; Pb; Pr; Ps$ t +// Reverse Attributes in Rectangular Area (DECRARA), VT400 and +// up. +// Pt; Pl; Pb; Pr denotes the rectangle. +// Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7. +// NOTE: xterm doesn't enable this code by default. +Terminal.prototype.reverseAttrInRectangle = function(params) { + ; +}; + +// CSI > Ps; Ps t +// Set one or more features of the title modes. Each parameter +// enables a single feature. +// Ps = 0 -> Set window/icon labels using hexadecimal. +// Ps = 1 -> Query window/icon labels using hexadecimal. +// Ps = 2 -> Set window/icon labels using UTF-8. +// Ps = 3 -> Query window/icon labels using UTF-8. (See dis- +// cussion of "Title Modes") +Terminal.prototype.setTitleModeFeature = function(params) { + ; +}; + +// CSI Ps SP t +// Set warning-bell volume (DECSWBV, VT520). +// Ps = 0 or 1 -> off. +// Ps = 2 , 3 or 4 -> low. +// Ps = 5 , 6 , 7 , or 8 -> high. +Terminal.prototype.setWarningBellVolume = function(params) { + ; +}; + +// CSI Ps SP u +// Set margin-bell volume (DECSMBV, VT520). +// Ps = 1 -> off. +// Ps = 2 , 3 or 4 -> low. +// Ps = 0 , 5 , 6 , 7 , or 8 -> high. +Terminal.prototype.setMarginBellVolume = function(params) { + ; +}; + +// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v +// Copy Rectangular Area (DECCRA, VT400 and up). +// Pt; Pl; Pb; Pr denotes the rectangle. +// Pp denotes the source page. +// Pt; Pl denotes the target location. +// Pp denotes the target page. +// NOTE: xterm doesn't enable this code by default. +Terminal.prototype.copyRectangle = function(params) { + ; +}; + +// CSI Pt ; Pl ; Pb ; Pr ' w +// Enable Filter Rectangle (DECEFR), VT420 and up. +// Parameters are [top;left;bottom;right]. +// Defines the coordinates of a filter rectangle and activates +// it. Anytime the locator is detected outside of the filter +// rectangle, an outside rectangle event is generated and the +// rectangle is disabled. Filter rectangles are always treated +// as "one-shot" events. Any parameters that are omitted default +// to the current locator position. If all parameters are omit- +// ted, any locator motion will be reported. DECELR always can- +// cels any prevous rectangle definition. +Terminal.prototype.enableFilterRectangle = function(params) { + ; +}; + +// CSI Ps x Request Terminal Parameters (DECREQTPARM). +// if Ps is a "0" (default) or "1", and xterm is emulating VT100, +// the control sequence elicits a response of the same form whose +// parameters describe the terminal: +// Ps -> the given Ps incremented by 2. +// Pn = 1 <- no parity. +// Pn = 1 <- eight bits. +// Pn = 1 <- 2 8 transmit 38.4k baud. +// Pn = 1 <- 2 8 receive 38.4k baud. +// Pn = 1 <- clock multiplier. +// Pn = 0 <- STP flags. +Terminal.prototype.requestParameters = function(params) { + ; +}; + +// CSI Ps x Select Attribute Change Extent (DECSACE). +// Ps = 0 -> from start to end position, wrapped. +// Ps = 1 -> from start to end position, wrapped. +// Ps = 2 -> rectangle (exact). +Terminal.prototype.selectChangeExtent = function(params) { + ; +}; + +// 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 Pm ' { +// Select Locator Events (DECSLE). +// Valid values for the first (and any additional parameters) +// are: +// Ps = 0 -> only respond to explicit host requests (DECRQLP). +// (This is default). It also cancels any filter +// rectangle. +// Ps = 1 -> report button down transitions. +// Ps = 2 -> do not report button down transitions. +// Ps = 3 -> report button up transitions. +// Ps = 4 -> do not report button up transitions. +Terminal.prototype.setLocatorEvents = function(params) { + ; +}; + +// CSI Pt; Pl; Pb; Pr$ { +// Selective Erase Rectangular Area (DECSERA), VT400 and up. +// Pt; Pl; Pb; Pr denotes the rectangle. +Terminal.prototype.selectiveEraseRectangle = function(params) { + ; +}; + +// CSI Ps ' | +// Request Locator Position (DECRQLP). +// Valid values for the parameter are: +// Ps = 0 , 1 or omitted -> transmit a single DECLRP locator +// report. + +// If Locator Reporting has been enabled by a DECELR, xterm will +// respond with a DECLRP Locator Report. This report is also +// generated on button up and down events if they have been +// enabled with a DECSLE, or when the locator is detected outside +// of a filter rectangle, if filter rectangles have been enabled +// with a DECEFR. + +// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w + +// Parameters are [event;button;row;column;page]. +// Valid values for the event: +// Pe = 0 -> locator unavailable - no other parameters sent. +// Pe = 1 -> request - xterm received a DECRQLP. +// Pe = 2 -> left button down. +// Pe = 3 -> left button up. +// Pe = 4 -> middle button down. +// Pe = 5 -> middle button up. +// Pe = 6 -> right button down. +// Pe = 7 -> right button up. +// Pe = 8 -> M4 button down. +// Pe = 9 -> M4 button up. +// Pe = 1 0 -> locator outside filter rectangle. +// ``button'' parameter is a bitmask indicating which buttons are +// pressed: +// Pb = 0 <- no buttons down. +// Pb & 1 <- right button down. +// Pb & 2 <- middle button down. +// Pb & 4 <- left button down. +// Pb & 8 <- M4 button down. +// ``row'' and ``column'' parameters are the coordinates of the +// locator position in the xterm window, encoded as ASCII deci- +// mal. +// The ``page'' parameter is not used by xterm, and will be omit- +// ted. +Terminal.prototype.requestLocatorPosition = function(params) { + ; +}; + +// 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(); +}; + +/** + * Prefix/Select/Visual/Search Modes + */ + +Terminal.prototype.enterPrefix = function() { + this.prefixMode = true; +}; + +Terminal.prototype.leavePrefix = function() { + this.prefixMode = false; +}; + +Terminal.prototype.enterSelect = function() { + this._real = { + x: this.x, + y: this.y, + ydisp: this.ydisp, + ybase: this.ybase, + cursorHidden: this.cursorHidden, + lines: this.copyBuffer(this.lines), + write: this.write + }; + this.write = function() {}; + this.selectMode = true; + this.visualMode = false; + this.cursorHidden = false; + this.refresh(this.y, this.y); +}; + +Terminal.prototype.leaveSelect = function() { + this.x = this._real.x; + this.y = this._real.y; + this.ydisp = this._real.ydisp; + this.ybase = this._real.ybase; + this.cursorHidden = this._real.cursorHidden; + this.lines = this._real.lines; + this.write = this._real.write; + delete this._real; + this.selectMode = false; + this.visualMode = false; + this.refresh(0, this.rows - 1); +}; + +Terminal.prototype.enterVisual = function() { + this._real.preVisual = this.copyBuffer(this.lines); + this.selectText(this.x, this.x, this.ydisp + this.y, this.ydisp + this.y); + this.visualMode = true; +}; + +Terminal.prototype.leaveVisual = function() { + this.lines = this._real.preVisual; + delete this._real.preVisual; + delete this._selected; + this.visualMode = false; + this.refresh(0, this.rows - 1); +}; + +Terminal.prototype.enterSearch = function(down) { + this.entry = ''; + this.searchMode = true; + this.searchDown = down; + this._real.preSearch = this.copyBuffer(this.lines); + this._real.preSearchX = this.x; + this._real.preSearchY = this.y; + + var bottom = this.ydisp + this.rows - 1; + for (var i = 0; i < this.entryPrefix.length; i++) { + //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; + //this.lines[bottom][i][1] = this.entryPrefix[i]; + this.lines[bottom][i] = [ + (this.defAttr & ~0x1ff) | 4, + this.entryPrefix[i] + ]; + } + + this.y = this.rows - 1; + this.x = this.entryPrefix.length; + + this.refresh(this.rows - 1, this.rows - 1); +}; + +Terminal.prototype.leaveSearch = function() { + this.searchMode = false; + + if (this._real.preSearch) { + this.lines = this._real.preSearch; + this.x = this._real.preSearchX; + this.y = this._real.preSearchY; + delete this._real.preSearch; + delete this._real.preSearchX; + delete this._real.preSearchY; + } + + this.refresh(this.rows - 1, this.rows - 1); +}; + +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.selectText = function(x1, x2, y1, y2) { + var ox1 + , ox2 + , oy1 + , oy2 + , tmp + , x + , y + , xl + , attr; + + if (this._selected) { + ox1 = this._selected.x1; + ox2 = this._selected.x2; + oy1 = this._selected.y1; + oy2 = this._selected.y2; + + if (oy2 < oy1) { + tmp = ox2; + ox2 = ox1; + ox1 = tmp; + tmp = oy2; + oy2 = oy1; + oy1 = tmp; + } + + if (ox2 < ox1 && oy1 === oy2) { + tmp = ox2; + ox2 = ox1; + ox1 = tmp; + } + + for (y = oy1; y <= oy2; y++) { + x = 0; + xl = this.cols - 1; + if (y === oy1) { + x = ox1; + } + if (y === oy2) { + xl = ox2; + } + for (; x <= xl; x++) { + if (this.lines[y][x].old != null) { + //this.lines[y][x][0] = this.lines[y][x].old; + //delete this.lines[y][x].old; + attr = this.lines[y][x].old; + delete this.lines[y][x].old; + this.lines[y][x] = [attr, this.lines[y][x][1]]; + } + } + } + + y1 = this._selected.y1; + x1 = this._selected.x1; + } + + y1 = Math.max(y1, 0); + y1 = Math.min(y1, this.ydisp + this.rows - 1); + + y2 = Math.max(y2, 0); + y2 = Math.min(y2, this.ydisp + this.rows - 1); + + this._selected = { x1: x1, x2: x2, y1: y1, y2: y2 }; + + if (y2 < y1) { + tmp = x2; + x2 = x1; + x1 = tmp; + tmp = y2; + y2 = y1; + y1 = tmp; + } + + if (x2 < x1 && y1 === y2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + + for (y = y1; y <= y2; y++) { + x = 0; + xl = this.cols - 1; + if (y === y1) { + x = x1; + } + if (y === y2) { + xl = x2; + } + for (; x <= xl; x++) { + //this.lines[y][x].old = this.lines[y][x][0]; + //this.lines[y][x][0] &= ~0x1ff; + //this.lines[y][x][0] |= (0x1ff << 9) | 4; + attr = this.lines[y][x][0]; + this.lines[y][x] = [ + (attr & ~0x1ff) | ((0x1ff << 9) | 4), + this.lines[y][x][1] + ]; + this.lines[y][x].old = attr; + } + } + + y1 = y1 - this.ydisp; + y2 = y2 - this.ydisp; + + y1 = Math.max(y1, 0); + y1 = Math.min(y1, this.rows - 1); + + y2 = Math.max(y2, 0); + y2 = Math.min(y2, this.rows - 1); + + //this.refresh(y1, y2); + this.refresh(0, this.rows - 1); +}; + +Terminal.prototype.grabText = function(x1, x2, y1, y2) { + var out = '' + , buf = '' + , ch + , x + , y + , xl + , tmp; + + if (y2 < y1) { + tmp = x2; + x2 = x1; + x1 = tmp; + tmp = y2; + y2 = y1; + y1 = tmp; + } + + if (x2 < x1 && y1 === y2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + + for (y = y1; y <= y2; y++) { + x = 0; + xl = this.cols - 1; + if (y === y1) { + x = x1; + } + if (y === y2) { + xl = x2; + } + for (; x <= xl; x++) { + ch = this.lines[y][x][1]; + if (ch === ' ') { + buf += ch; + continue; + } + if (buf) { + out += buf; + buf = ''; + } + out += ch; + if (isWide(ch)) x++; + } + buf = ''; + out += '\n'; + } + + // If we're not at the end of the + // line, don't add a newline. + for (x = x2, y = y2; x < this.cols; x++) { + if (this.lines[y][x][1] !== ' ') { + out = out.slice(0, -1); + break; + } + } + + return out; +}; + +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'); + } else if (key === '[') { + this.enterSelect(); + } +}; + +Terminal.prototype.keySelect = function(ev, key) { + this.showCursor(); + + if (this.searchMode || key === 'n' || key === 'N') { + return this.keySearch(ev, key); + } + + 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); + } + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } + 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); + } + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } + return; + } + + if (key === '\x06') { // ctrl-f + var y = this.ydisp + this.y; + this.scrollDisp(this.rows - 1); + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } + return; + } + + if (key === '\x02') { // ctrl-b + var y = this.ydisp + this.y; + this.scrollDisp(-(this.rows - 1)); + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } + 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); + } + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } else { + 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); + } + if (this.visualMode) { + this.selectText(this.x, this.x, y, this.ydisp + this.y); + } else { + 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; + } + if (this.visualMode) { + this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); + } else { + 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; + } + if (this.visualMode) { + this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); + } else { + this.refresh(this.y, this.y); + } + return; + } + + if (key === 'v' || key === ' ') { + if (!this.visualMode) { + this.enterVisual(); + } else { + this.leaveVisual(); + } + return; + } + + if (key === 'y') { + if (this.visualMode) { + var text = this.grabText( + this._selected.x1, this._selected.x2, + this._selected.y1, this._selected.y2); + this.copyText(text); + this.leaveVisual(); + // this.leaveSelect(); + } + return; + } + + if (key === 'q' || key === '\x1b') { + if (this.visualMode) { + this.leaveVisual(); + } else { + this.leaveSelect(); + } + 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++; + } + 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); + + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + return; + } + + if (key === 'b' || key === 'B') { + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; + + 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; + } + + this.x = x, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + 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); + + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + 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; + } + + if (this.visualMode) { + this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); + } else { + 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) { + if (line[x][1] > ' ') { + if (this.visualMode && x < this.cols - 1) x++; + break; + } + x--; + } + if (x < 0) x = 0; + this.x = x; + if (this.visualMode) { + this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); + } else { + 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); + } + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + 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; + } + if (this.visualMode) { + this.selectText(ox, this.x, this.ydisp + oy, this.ydisp + this.y); + } else { + 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); + + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + return; + } + + if (key === '/' || key === '?') { + if (!this.visualMode) { + this.enterSearch(key === '/'); + } + return; + } + + return false; +}; + +Terminal.prototype.keySearch = function(ev, key) { + if (key === '\x1b') { + this.leaveSearch(); + return; + } + + if (key === '\r' || (!this.searchMode && (key === 'n' || key === 'N'))) { + this.leaveSearch(); + + var entry = this.entry; + + if (!entry) { + this.refresh(0, this.rows - 1); + return; + } + + var ox = this.x; + var oy = this.y; + var oyd = this.ydisp; + + var line; + var found = false; + var wrapped = false; + var x = this.x + 1; + var y = this.ydisp + this.y; + var yb, i; + var up = key === 'N' + ? this.searchDown + : !this.searchDown; + + for (;;) { + line = this.lines[y]; + + while (x < this.cols) { + for (i = 0; i < entry.length; i++) { + if (x + i >= this.cols) break; + if (line[x + i][1] !== entry[i]) { + break; + } else if (line[x + i][1] === entry[i] && i === entry.length - 1) { + found = true; + break; + } + } + if (found) break; + x += i + 1; + } + if (found) break; + + x = 0; + + if (!up) { + y++; + if (y > this.ybase + this.rows - 1) { + if (wrapped) break; + // this.setMessage('Search wrapped. Continuing at TOP.'); + wrapped = true; + y = 0; + } + } else { + y--; + if (y < 0) { + if (wrapped) break; + // this.setMessage('Search wrapped. Continuing at BOTTOM.'); + wrapped = true; + y = this.ybase + this.rows - 1; + } + } + } + + if (found) { + if (y - this.ybase < 0) { + yb = y; + y = 0; + if (yb > this.ybase) { + y = yb - this.ybase; + yb = this.ybase; + } + } else { + yb = this.ybase; + y -= this.ybase; + } + + this.x = x, this.y = y; + this.scrollDisp(-this.ydisp + yb); + + if (this.visualMode) { + this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); + } + return; + } + + // this.setMessage("No matches found."); + this.refresh(0, this.rows - 1); + + return; + } + + if (key === '\b' || key === '\x7f') { + if (this.entry.length === 0) return; + var bottom = this.ydisp + this.rows - 1; + this.entry = this.entry.slice(0, -1); + var i = this.entryPrefix.length + this.entry.length; + //this.lines[bottom][i][1] = ' '; + this.lines[bottom][i] = [ + this.lines[bottom][i][0], + ' ' + ]; + this.x--; + this.refresh(this.rows - 1, this.rows - 1); + this.refresh(this.y, this.y); + return; + } + + if (key.length === 1 && key >= ' ' && key <= '~') { + var bottom = this.ydisp + this.rows - 1; + this.entry += key; + var i = this.entryPrefix.length + this.entry.length - 1; + //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; + //this.lines[bottom][i][1] = key; + this.lines[bottom][i] = [ + (this.defAttr & ~0x1ff) | 4, + key + ]; + this.x++; + this.refresh(this.rows - 1, this.rows - 1); + this.refresh(this.y, this.y); + 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) { + el.addEventListener(type, handler, capture || false); +} + +function off(el, type, handler, capture) { + el.removeEventListener(type, handler, capture || false); +} + +function cancel(ev) { + if (ev.preventDefault) ev.preventDefault(); + ev.returnValue = false; + if (ev.stopPropagation) ev.stopPropagation(); + ev.cancelBubble = true; + 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 terminal = document.createElement('div'); + terminal.className = 'terminal'; + var line = document.createElement('div'); + var el = document.createElement('span'); + el.innerHTML = 'hello world'; + line.appendChild(el); + terminal.appendChild(line); + body.appendChild(terminal); + var w1 = el.scrollWidth; + el.style.fontWeight = 'bold'; + var w2 = el.scrollWidth; + body.removeChild(terminal); + 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; +} + +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.Stream = Stream; +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); +}());