diff --git a/static/js/terminal/index.js b/static/js/terminal/index.js index dfd135e..180e47c 100644 --- a/static/js/terminal/index.js +++ b/static/js/terminal/index.js @@ -1,197 +1,2714 @@ /** - * Created by nuintun on 2015/11/24. + * Created by nuintun on 2015/11/30. */ 'use strict'; -var states = require('./lib/states'); - -module.exports = Terminal; - /** - * iterator - * @param from - * @param iterator - * @param context + * range function for numbers [a, .., b-1] + * + * @param {number} a + * @param {number} b + * @return {Array} */ -function iterator(from, iterator, context){ - for (var key in from) { - if (from.hasOwnProperty(key)) { - iterator.call(context, key, from[key]); - } +function r(a, b){ + var c = b - a; + var arr = new Array(c); + + while (c--) { + arr[c] = --b; } + + return arr; } /** - * Terminal - * @param options - * @returns {Terminal} + * Add a transition to the transition table. + * + * @param table - table to add transition + * @param {number} inp - input character code + * @param {number} state - current state + * @param {number=} action - action to be taken + * @param {number=} next - next state + */ +function add(table, inp, state, action, next){ + table[state << 8 | inp] = ((action | 0) << 4) | ((next === undefined) ? state : next); +} + +/** + * Add multiple transitions to the transition table. + * + * @param table - table to add transition + * @param {Array} inps - array of input character codes + * @param {number} state - current state + * @param {number=} action - action to be taken + * @param {number=} next - next state + */ +function add_list(table, inps, state, action, next){ + for (var i = 0; i < inps.length; i++) + add(table, inps[i], state, action, next); +} + +/** global definition of printables and executables */ +var PRINTABLES = r(0x20, 0x7f); +var EXECUTABLES = r(0x00, 0x18); +EXECUTABLES.push(0x19); +EXECUTABLES.concat(r(0x1c, 0x20)); + +/** + * create the standard transition table - used by all parser instances + * + * table[state << 8 | character code] = action << 4 | next state + * + * - states are numbers from 0 to 13 + * - control character codes defined from 0 to 159 (C0 and C1) + * - actions are numbers from 0 to 14 + * - any higher character than 159 is handled by the 'error' action + * + * state replacements (14 states): + * 'GROUND' -> 0 + * 'ESCAPE' -> 1 + * 'ESCAPE_INTERMEDIATE' -> 2 + * 'CSI_ENTRY' -> 3 + * 'CSI_PARAM' -> 4 + * 'CSI_INTERMEDIATE' -> 5 + * 'CSI_IGNORE' -> 6 + * 'SOS_PM_APC_STRING' -> 7 + * 'OSC_STRING' -> 8 + * 'DCS_ENTRY' -> 9 + * 'DCS_PARAM' -> 10 + * 'DCS_IGNORE' -> 11 + * 'DCS_INTERMEDIATE' -> 12 + * 'DCS_PASSTHROUGH' -> 13 + * + * action replacements (15 actions): + * 'no action' -> 0 + * 'error' -> 1 + * 'print' -> 2 + * 'execute' -> 3 + * 'osc_start' -> 4 + * 'osc_put' -> 5 + * 'osc_end' -> 6 + * 'csi_dispatch' -> 7 + * 'param' -> 8 + * 'collect' -> 9 + * 'esc_dispatch' -> 10 + * 'clear' -> 11 + * 'dcs_hook' -> 12 + * 'dcs_put' -> 13 + * 'dcs_unhook' -> 14 + */ +var TRANSITION_TABLE = (function (){ + var table = new Uint8Array(4095); + + // table with default transition [any] --> [error, GROUND] + for (var state = 0; state < 14; ++state) { + for (var code = 0; code < 160; ++code) { + table[state << 8 | code] = 16; + } + } + + // apply transitions + // printables + add_list(table, PRINTABLES, 0, 2); + + // global anywhere rules + for (state = 0; state < 14; ++state) { + add_list(table, [0x18, 0x1a, 0x99, 0x9a], state, 3, 0); + add_list(table, r(0x80, 0x90), state, 3, 0); + add_list(table, r(0x90, 0x98), state, 3, 0); + add(table, 0x9c, state, 0, 0); // ST as terminator + add(table, 0x1b, state, 11, 1); // ESC + add(table, 0x9d, state, 4, 8); // OSC + add_list(table, [0x98, 0x9e, 0x9f], state, 0, 7); + add(table, 0x9b, state, 11, 3); // CSI + add(table, 0x90, state, 11, 9); // DCS + } + + // rules for executables and 7f + add_list(table, EXECUTABLES, 0, 3); + add_list(table, EXECUTABLES, 1, 3); + add(table, 0x7f, 1); + add_list(table, EXECUTABLES, 8); + add_list(table, EXECUTABLES, 3, 3); + add(table, 0x7f, 3); + add_list(table, EXECUTABLES, 4, 3); + add(table, 0x7f, 4); + add_list(table, EXECUTABLES, 6, 3); + add_list(table, EXECUTABLES, 5, 3); + add(table, 0x7f, 5); + add_list(table, EXECUTABLES, 2, 3); + add(table, 0x7f, 2); + // osc + add(table, 0x5d, 1, 4, 8); + add_list(table, PRINTABLES, 8, 5); + add(table, 0x7f, 8, 5); + add_list(table, [0x9c, 0x1b, 0x18, 0x1a, 0x07], 8, 6, 0); + add_list(table, r(0x1c, 0x20), 8, 0); + // sos/pm/apc does nothing + add_list(table, [0x58, 0x5e, 0x5f], 1, 0, 7); + add_list(table, PRINTABLES, 7); + add_list(table, EXECUTABLES, 7); + add(table, 0x9c, 7, 0, 0); + // csi entries + add(table, 0x5b, 1, 11, 3); + add_list(table, r(0x40, 0x7f), 3, 7, 0); + add_list(table, r(0x30, 0x3a), 3, 8, 4); + add(table, 0x3b, 3, 8, 4); + add_list(table, [0x3c, 0x3d, 0x3e, 0x3f], 3, 9, 4); + add_list(table, r(0x30, 0x3a), 4, 8); + add(table, 0x3b, 4, 8); + add_list(table, r(0x40, 0x7f), 4, 7, 0); + add_list(table, [0x3a, 0x3c, 0x3d, 0x3e, 0x3f], 4, 0, 6); + add_list(table, r(0x20, 0x40), 6); + add(table, 0x7f, 6); + add_list(table, r(0x40, 0x7f), 6, 0, 0); + add(table, 0x3a, 3, 0, 6); + add_list(table, r(0x20, 0x30), 3, 9, 5); + add_list(table, r(0x20, 0x30), 5, 9); + add_list(table, r(0x30, 0x40), 5, 0, 6); + add_list(table, r(0x40, 0x7f), 5, 7, 0); + add_list(table, r(0x20, 0x30), 4, 9, 5); + // esc_intermediate + add_list(table, r(0x20, 0x30), 1, 9, 2); + add_list(table, r(0x20, 0x30), 2, 9); + add_list(table, r(0x30, 0x7f), 2, 10, 0); + add_list(table, r(0x30, 0x50), 1, 10, 0); + add_list(table, [0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5a, 0x5c], 1, 10, 0); + add_list(table, r(0x60, 0x7f), 1, 10, 0); + // dcs entry + add(table, 0x50, 1, 11, 9); + add_list(table, EXECUTABLES, 9); + add(table, 0x7f, 9); + add_list(table, r(0x1c, 0x20), 9); + add_list(table, r(0x20, 0x30), 9, 9, 12); + add(table, 0x3a, 9, 0, 11); + add_list(table, r(0x30, 0x3a), 9, 8, 10); + add(table, 0x3b, 9, 8, 10); + add_list(table, [0x3c, 0x3d, 0x3e, 0x3f], 9, 9, 10); + add_list(table, EXECUTABLES, 11); + add_list(table, r(0x20, 0x80), 11); + add_list(table, r(0x1c, 0x20), 11); + add_list(table, EXECUTABLES, 10); + add(table, 0x7f, 10); + add_list(table, r(0x1c, 0x20), 10); + add_list(table, r(0x30, 0x3a), 10, 8); + add(table, 0x3b, 10, 8); + add_list(table, [0x3a, 0x3c, 0x3d, 0x3e, 0x3f], 10, 0, 11); + add_list(table, r(0x20, 0x30), 10, 9, 12); + add_list(table, EXECUTABLES, 12); + add(table, 0x7f, 12); + add_list(table, r(0x1c, 0x20), 12); + add_list(table, r(0x20, 0x30), 12, 9); + add_list(table, r(0x30, 0x40), 12, 0, 11); + add_list(table, r(0x40, 0x7f), 12, 12, 13); + add_list(table, r(0x40, 0x7f), 10, 12, 13); + add_list(table, r(0x40, 0x7f), 9, 12, 13); + add_list(table, EXECUTABLES, 13, 13); + add_list(table, PRINTABLES, 13, 13); + add(table, 0x7f, 13); + add_list(table, [0x1b, 0x9c], 13, 14, 0); + + return table; +})(); + +/** + * helper for param conversion + * @param {string} params - params string with ; + * @return {Array} + */ +function parse_params(params){ + // params are separated by ';' + // empty defaults to 0 + var p = params.split(';'); + + for (var i = 0; i < p.length; ++i) { + p[i] = Number(p[i]); + } + + return p; +} + +/** + * AnsiParser - Parser for ANSI terminal escape sequences. + * + * @param {Object=} terminal * @constructor */ -function Terminal(options){ - options = options || {}; +function AnsiParser(terminal){ + this.initial_state = 0; // 'GROUND' is default + this.current_state = this.initial_state; - if (!(this instanceof Terminal)) return new Terminal(options); + // global non pushable buffers for multiple parse invocations + this.osc = ''; + this.params = ''; + this.collected = ''; - // inherits - iterator(Terminal.defaults, function (key, value){ - if (options.hasOwnProperty(key)) { - this[key] = options[key]; - } else { - this[key] = value; - options[key] = value; + // back reference to terminal + this.term = terminal || {}; + var instructions = [ + 'inst_p', 'inst_o', 'inst_x', 'inst_c', + 'inst_e', 'inst_H', 'inst_P', 'inst_U', 'inst_E' + ]; + + for (var i = 0; i < instructions.length; ++i) { + if (!(instructions[i] in this.term)) { + this.term[instructions[i]] = function (){}; } - }, this); - - // set colors - if (Array.isArray(options.colors)) { - if (options.colors.length === 8) { - options.colors = options.colors.concat(Terminal.colors.slice(8)); - this.vcolors = Terminal.makeVcolors(options.colors); - } else if (options.colors.length === 16) { - options.colors = options.colors.concat(Terminal.colors.slice(16)); - this.vcolors = Terminal.makeVcolors(options.colors); - } else if (options.colors.length === 10) { - options.colors = options.colors.slice(0, -2).concat(Terminal.colors.slice(8, -2), options.colors.slice(-2)); - this.vcolors = Terminal.makeVcolors(options.colors); - } else if (options.colors.length === 18) { - options.colors = options.colors.slice(0, -2).concat(Terminal.colors.slice(16, -2), options.colors.slice(-2)); - this.vcolors = Terminal.makeVcolors(options.colors); - } else { - options.colors = Terminal.colors.slice(); - this.vcolors = Terminal.vcolors.slice(); - } - } else { - options.colors = Terminal.colors.slice(); - this.vcolors = Terminal.vcolors.slice(); } - - this.colors = options.colors; - this.background = options.background || Terminal.defaultColors.background; - this.foreground = options.foreground || Terminal.defaultColors.foreground; - - // set screen size - options.cols = options.cols || Terminal.geometry[0]; - options.rows = options.rows || Terminal.geometry[1]; - this.cols = options.cols; - this.rows = options.rows; - - // set on data callback - options.ondata = typeof options.ondata === 'function' ? options.ondata : function (){}; - this.ondata = options.ondata; - - // set on title callback - options.ontitle = typeof options.ontitle === 'function' ? options.ontitle : function (){}; - this.ontitle = options.ontitle; - - // set convert end of line - options.convertEOL = options.convertEOL === true; - this.convertEOL = options.convertEOL; - - // set options - this.options = options; - - // set property - this.x = 0; - this.y = 0; - this.ybase = 0; - this.ydisp = 0; - this.cursorState = 0; - this.state = states.normal; - this.queue = ''; - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - - // Modes - this.applicationKeypad = false; - this.originMode = false; - this.insertMode = false; - this.wraparoundMode = false; - this.normal = null; - - // charset - this.charset = null; - this.gcharset = null; - this.glevel = 0; - this.charsets = [null]; - - // misc - this.screen = null; - this.children = []; - this.refreshStart = null; - this.refreshEnd = null; - this.savedX = null; - this.savedY = null; - this.savedCols = null; - - // stream - this.readable = true; - this.writable = true; - - // set attr - this.defAttr = (257 << 9) | 256; - this.curAttr = this.defAttr; - - // set params - this.params = []; - this.currentParam = 0; - this.prefix = ''; - this.postfix = ''; - - // set lines - this.lines = []; - - // set tabs - this.tabs = null; - - var i = this.rows; - - while (i--) { - this.lines.push(this.blankLine()); - } - - this.setupStops(); } -require('./lib/colors')(Terminal); -require('./lib/options')(Terminal); +/** + * Reset the parser. + */ +AnsiParser.prototype.reset = function (){ + this.current_state = this.initial_state; + this.osc = ''; + this.params = ''; + this.collected = ''; +}; -require('./lib/open')(Terminal); -require('./lib/refresh')(Terminal); -require('./lib/close')(Terminal); +/** + * Parse string s. + * @param {string} s + */ +AnsiParser.prototype.parse = function (s){ + var error = false; + var c, code, transition; + var current_state = this.current_state; -require('./lib/write')(Terminal); + // local buffers + var printed = ''; + var dcs = ''; + var osc = this.osc; + var collected = this.collected; + var params = this.params; -require('./lib/setgLevel'); -require('./lib/setgCharset'); + // process input string + for (var i = 0; i < s.length; ++i) { + c = s.charAt(i); + code = c.charCodeAt(0); -require('./lib/debug')(Terminal); + if (code < 0xa0) { + transition = TRANSITION_TABLE[current_state << 8 | code]; + } else { + transition = 16; + } -require('./lib/stops')(Terminal); + switch (transition >> 4) { + case 0: + // no action + break; + case 1: + // error + // NOTE: real error recovery is not implemented + // handle unicode chars in write buffers w'o state change + if (code > 0x9f) { + switch (current_state) { + case 0: + // GROUND -> add char to print string + printed += c; + break; + case 8: + // OSC_STRING -> add char to osc string + osc += c; + transition |= 8; + break; + case 6: + // CSI_IGNORE -> ignore char + transition |= 6; + break; + case 11: + // DCS_IGNORE -> ignore char + transition |= 11; + break; + case 13: + // DCS_PASSTHROUGH -> add char to dcs + dcs += c; + transition |= 13; + break; + default: + error = true; + } + } else { + error = true; + } -require('./lib/erase')(Terminal); -require('./lib/blankLine')(Terminal); -require('./lib/range')(Terminal); -require('./lib/util')(Terminal); + if (error) { + if ( + this.term.inst_E({ + pos: i, // position in parse string + character: c, // wrong character + state: current_state, // in state + print: printed, // print buffer + dcs: dcs, // dcs buffer + osc: osc, // osc buffer + collect: collected, // collect buffer + params: params // params buffer + }) + ) { + return; + } + error = false; + } + break; + case 2: + // print + printed += c; + break; + case 3: + // execute + if (printed) { + this.term.inst_p(printed); + } -require('./lib/cursor')(Terminal); -require('./lib/focused')(Terminal); + printed = ''; -require('./lib/scrollDisp')(Terminal); + this.term.inst_x(c); + break; + case 7: + // csi_dispatch + this.term.inst_c(collected, parse_params(params), c); + break; + case 8: + // param + params += c; + break; + case 9: + // collect + collected += c; + break; + case 10: + // esc_dispatch + this.term.inst_e(collected, c); + break; + case 11: + // clear + if (printed) { + this.term.inst_p(printed); + } -require('./lib/resize')(Terminal); -require('./lib/esc/index.js')(Terminal); -require('./lib/esc/reset.js')(Terminal); -require('./lib/esc/tabSet.js')(Terminal); + printed = ''; + osc = ''; + params = ''; + collected = ''; + dcs = ''; + break; + case 4: + // osc_start + if (printed) { + this.term.inst_p(printed); + } -require('./lib/charsets.js')(Terminal); -require('./lib/csi/charAttributes')(Terminal); -require('./lib/csi/erase')(Terminal); -require('./lib/csi/insert-delete')(Terminal); -require('./lib/csi/position')(Terminal); -require('./lib/csi/cursor')(Terminal); -require('./lib/csi/repeatPrecedingCharacter')(Terminal); -require('./lib/csi/tabClear')(Terminal); -require('./lib/csi/softReset')(Terminal); -require('./lib/csi/scroll')(Terminal); -require('./lib/csi/device')(Terminal); + printed = ''; + osc = ''; + break; + case 5: + // osc_put + osc += c; + break; + case 6: + // osc_end + if (osc && code !== 0x18 && code !== 0x1a) { + this.term.inst_o(osc); + } + + if (code === 0x1b) { + transition |= 1; + } + + osc = ''; + params = ''; + collected = ''; + dcs = ''; + break; + // dcs_hook + case 12: + this.term.inst_H(collected, parse_params(params), c); + break; + // dcs_put + case 13: + dcs += c; + break; + // dcs_unhook + case 14: + if (dcs) { + this.term.inst_P(dcs); + } + + this.term.inst_U(); + + if (code === 0x1b) { + transition |= 1; + } + + osc = ''; + params = ''; + collected = ''; + dcs = ''; + break; + } + + current_state = transition & 15; + } + + // push leftover pushable buffers to terminal + if (!current_state && printed) { + this.term.inst_p(printed); + } else if (current_state === 13 && dcs) { + this.term.inst_P(dcs); + } + + // save non pushable buffers + this.osc = osc; + this.collected = collected; + this.params = params; + + // save state + this.current_state = current_state; +}; + +/** + * AnsiTerminal - an offscreen xterm like terminal. + * + * TODO: + * - unicode tests + * - move box printing chars to frontend + * - create output methods for TChar and AnsiTerminal + * - bracketed paste mode + * - tabs, tab stops, tab width, tab output + * - tons of DCS codes + * - advanced tests, vttest + * - mouse handling goes here (via registering handler callbacks) + * - test cases + */ + +/** + * wcswidth + * + * taken from: + * - http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + * - wcwidth node module + */ +var _WIDTH_COMBINING = [ + [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], + [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], + [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], + [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], + [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], + [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], + [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], + [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], + [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], + [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], + [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], + [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], + [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], + [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], + [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], + [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], + [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], + [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], + [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], + [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], + [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], + [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], + [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], + [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], + [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], + [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], + [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], + [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], + [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], + [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], + [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], + [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], + [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], + [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], + [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], + [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], + [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], + [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], + [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] +]; + +function _width_bisearch(ucs){ + var mid; + var min = 0; + var max = _WIDTH_COMBINING.length - 1; + + if (ucs < _WIDTH_COMBINING[0][0] || ucs > _WIDTH_COMBINING[max][1]) { + return false; + } + + while (max >= min) { + mid = Math.floor((min + max) / 2); + + if (ucs > _WIDTH_COMBINING[mid][1]) { + min = mid + 1; + } else if (ucs < _WIDTH_COMBINING[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + + return false; +} + +function _width_wcwidth(ucs, opts){ + // test for 8-bit control characters + if (ucs === 0) { + return opts.nul; + } + + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { + return opts.control; + } + + // binary search in table of non-spacing characters + if (_width_bisearch(ucs)) { + return 0; + } + + // if we arrive here, ucs is not a combining or C0/C1 control character + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || // Hangul Jamo init. consonants + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || // CJK ... Yi + (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables + (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compatibility Ideographs + (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms + (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compatibility Forms + (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +var _WIDTH_DEFAULTS = { nul: 0, control: 0 }; + +// wcswidth(string[, opts]) - number of taken terminal cells by a string (printed space) +function wcswidth(str, opts){ + // FIXME + opts = _WIDTH_DEFAULTS; + + if (typeof str !== 'string') { + return _width_wcwidth(str, opts); + } + + var s = 0; + + for (var i = 0; i < str.length; i++) { + var n = _width_wcwidth(str.charCodeAt(i), opts); + + if (n < 0) { + return -1; + } + + s += n; + } + + return s; +} + +/** + * TChar - terminal character with attributes. + * + * Bits of text attr: + * 1-8 BG / BG red + * 9-16 FG / FG red + * 17 bold + * 18 italic + * 19 underline + * 20 blink + * 21 inverse + * 22 conceal + * 23 cursor + * 24 + * 25 BG set + * 26 BG RGB mode + * 27 FG set + * 28 FG RGB mode + * 29-32 + * + * Bits of gb: + * 1-8 BG blue + * 9-16 FG blue + * 17-24 BG green + * 25-32 FG green + * + * @param {string} value - An unicode character (multiple if surrogate or combining). + * @param {number} [attr] - Cell attributes as integer. + * @param {number} [gb] - Green and blue part of RGB as integer. + * @param {number} [width] - Terminal cells taken by this character. + * @constructor + */ +function TChar(value, attr, gb, width){ + this.value = value; + this.attr = attr | 0; + this.gb = gb | 0; + this.width = (width === undefined) ? 1 : width; +} + +TChar.prototype.clone = function (){ + return new TChar(this.value, this.attr, this.gb, this.width); +}; + +/** + * getAttributes + * @return {object} Object with attributes in a readable manner. + */ +TChar.prototype.getAttributes = function (){ + var colorbits = this.attr >>> 24; + var r = this.attr & 65535; + var g = this.gb >>> 16; + var b = this.gb & 65535; + var bits = this.attr >>> 16 & 255; + + return { + bold: !!(bits & 1), + italic: !!(bits & 2), + underline: !!(bits & 4), + blink: !!(bits & 8), + inverse: !!(bits & 16), + conceal: !!(bits & 32), + // TODO cursor + // cursor: !!(bits & 64), + foreground: { + set: !!(colorbits & 4), + RGB: !!(colorbits & 8), + color: [r >>> 8, g >>> 8, b >>> 8] + }, + background: { + set: !!(colorbits & 1), + RGB: !!(colorbits & 2), + color: [r & 255, g & 255, b & 255] + } + } +}; + +/** + * setAttributes + * @param attributes + */ +TChar.prototype.setAttributes = function (attributes){ + var attr = this.attr; + + ['bold', 'italic', 'underline', 'blink', 'inverse', 'conceal'].map(function (key, i){ + attr = attributes[key] ? attr | (2 << (15 + i)) : attr & ~(2 << (15 + i)); + }); + + if (attributes['foreground']) { + var foreground = attributes['foreground']; + + attr = foreground['set'] ? attr | (2 << 25) : attr & ~(2 << 25); + attr = foreground['RGB'] ? attr | (2 << 26) : attr & ~(2 << 26); + + if (Array.isArray(foreground['color'])) { + attr = (attr & ~65280) | (foreground['color'][0] << 8); + this.gb = (this.gb & ~4278190080) | (foreground['color'][1] << 24); + this.gb = (this.gb & ~65280) | (foreground['color'][2] << 8); + } + } + + if (attributes['background']) { + var background = attributes['background']; + + attr = background['set'] ? attr | (2 << 23) : attr & ~(2 << 23); + attr = background['RGB'] ? attr | (2 << 24) : attr & ~(2 << 24); + + if (Array.isArray(background['color'])) { + attr = (attr & ~255) | (background['color'][0]); + this.gb = (this.gb & ~16711680) | (background['color'][1] << 16); + this.gb = (this.gb & ~255) | background['color'][2]; + } + } + + this.attr = attr; +}; + +TChar.prototype.toString = function (){ + return this.value; +}; + +var _uniqueId = 0; + +/** + * Row + * @constructor + */ +function Row(){ + this.uniqueId = _uniqueId++ | 0; + this.version = 0; + this.cells = []; +} + +/** + * ScreenBuffer - represents a terminal screen with cols and rows. + * + * .buffer is an array of length rows and contains the row objects. + * + * @param cols + * @param rows + * @param scrollLength + * @constructor + */ +function ScreenBuffer(cols, rows, scrollLength){ + this.rows = rows; + this.cols = cols; + this.scrollLength = scrollLength | 0; + + this.buffer = []; + this.scrollbuffer = []; + + this.reset(); +} + +ScreenBuffer.prototype.reset = function (){ + this.buffer = []; + this.scrollbuffer = []; + + var row; + + for (var i = 0; i < this.rows; ++i) { + row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('')); + } + + this.buffer.push(row); + } +}; + +ScreenBuffer.prototype.appendToScrollBuffer = function (row){ + this.scrollbuffer.push(row); + + while (this.scrollbuffer.length > this.scrollLength) { + this.scrollbuffer.shift(); + } +}; + +ScreenBuffer.prototype.fetchFromScrollBuffer = function (){ + return this.scrollbuffer.pop(); +}; + +ScreenBuffer.prototype.resize = function (cols, rows, cursor){ + // xterm behavior - shrink: + // delete higher rows til cursor then lowest to scrollbuffer + // xterm behavior - enlarge: + // fill lowest from scrollbuffer then append new at end + + // assume xterm handles alternate buffer the same way + // in respect of the cursor position but w/o scrolling + + // shrink height + if (rows < this.rows) { + while (this.buffer.length > rows) { + if (this.buffer.length > cursor.row + 1) { + this.buffer.pop(); + } else { + this.appendToScrollBuffer(this.buffer.shift()); + cursor.row -= 1; + } + } + } + + // enlarge height + if (rows > this.rows) { + while (this.buffer.length < rows) { + var row = this.fetchFromScrollBuffer(); + + if (row) { + this.buffer.unshift(row); + cursor.row += 1; + } else { + row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('')); + } + + this.buffer.push(row); + } + } + } + + if (cursor.row >= rows) { + cursor.row = rows - 1; + } + + var i; + + // shrink width + if (cols < this.cols) { + for (i = 0; i < this.buffer.length; ++i) { + var remove = this.cols - cols; + + do { + this.buffer[i].cells.pop(); + } while (--remove); + + this.buffer[i].version++; + } + + for (i = 0; i < this.scrollbuffer.length; ++i) { + remove = this.cols - cols; + + do { + this.scrollbuffer[i].cells.pop(); + } while (--remove); + + this.scrollbuffer[i].version++; + } + } + + // enlarge width + if (cols > this.cols) { + for (i = 0; i < this.buffer.length; ++i) { + var append = cols - this.cols; + + do { + this.buffer[i].cells.push(new TChar('')); + } while (--append); + + this.buffer[i].version++; + } + + for (i = 0; i < this.scrollbuffer.length; ++i) { + append = cols - this.cols; + + do { + this.scrollbuffer[i].cells.push(new TChar('')); + } while (--append); + + this.scrollbuffer[i].version++; + } + } + + if (cursor.col >= cols) + cursor.col = cols - 1; + + this.rows = rows; + this.cols = cols; +}; + +/** minimal support for switching charsets (only basic drawing symbols supported) */ +var CHARSET_0 = { + '`': '◆', 'a': '▒', 'b': '␉', 'c': '␌', 'd': '␍', + 'e': '␊', 'f': '°', 'g': '±', 'h': '␤', 'i': '␋', + 'j': '┘', 'k': '┐', 'l': '┌', 'm': '└', 'n': '┼', + 'o': '⎺', 'p': '⎻', 'q': '─', 'r': '⎼', 's': '⎽', + 't': '├', 'u': '┤', 'v': '┴', 'w': '┬', 'x': '│', + 'y': '≤', 'z': '≥', '{': 'π', '|': '≠', '}': '£', '~': '°' +}; + +/** fix: box drawing bold */ +// FIXME: should this go into frontend? +// since most monospace fonts can't handle bold on these right we have to +// switch to to corrensponding unicode character +var BOXSYMBOLS_BOLD = { + '─': '━', '│': '┃', '┄': '┅', '┆': '┇', '┈': '┉', '┊': '┋', + '┌': '┏', '┐': '┓', '└': '┗', '┘': '┛', '├': '┣', '┤': '┫', + '┬': '┳', '┴': '┻', '┼': '╋', '╌': '╍', '╎': '╏' +}; + +var TERM_STRING = { + CSI: '\u001b[', SS3: '\u001bO', DCS: '\u001bP', ST: '\u001b\\', + OSC: '\u001b]', PM: '\u001b^', APC: '\u001b_' +}; + +/** + * AnsiTerminal - an offscreen terminal. + * + * @param {number} cols - columns of the terminal. + * @param {number} rows - rows of the terminal. + * @param {number} scrollLength - lines of scrollbuffer. + * @constructor + */ +function AnsiTerminal(cols, rows, scrollLength){ + if (!(this instanceof AnsiTerminal)) { + return new AnsiTerminal(cols, rows, scrollLength); + } + + this.rows = rows; + this.cols = cols; + this.scrollLength = scrollLength | 0; + // callback for writing back to stream + this.send = function (data){}; + // callback for sending console beep + this.beep = function (tone, duration){}; + // announce changes in mouse handling + this.changedMouseHandling = function (mode, protocol){}; + + // init ansi parser + this.AnsiParser = new AnsiParser(this); + + this.reset(); +} + +/** + * write + * @param data + */ +AnsiTerminal.prototype.write = function (data){ + this.AnsiParser.parse(data); +}; + +/** Hard reset of the terminal. */ +AnsiTerminal.prototype.reset = function (){ + this.normal_screen = new ScreenBuffer(this.cols, this.rows, this.scrollLength); + this.alternate_screen = new ScreenBuffer(this.cols, this.rows, 0); + this.screen = this.normal_screen; + this.normal_cursor = { col: 0, row: 0 }; + this.alternate_cursor = { col: 0, row: 0 }; + this.cursor = this.normal_cursor; + this.charset = null; + this.textattributes = 0; + this.colors = 0; + this.charattributes = 0; + this.reverse_video = false; + + this.cursor_key_mode = false; + this.show_cursor = true; + // terminal title set by OSR + this.title = ''; + this.cursor_save = null; + // IRM (default replace) + this.insert_mode = false; + // CSI?12l + this.blinking_cursor = false; + // for DECSTBM + this.scrolling_top = 0; + // for DECSTBM + this.scrolling_bottom = this.rows; + // DECAWM + this.autowrap = true; + // LNM + this.newline_mode = false; + this.tab_width = 8; + // for REP + this.last_char = ''; + // tracking modes for mouse 0=off, (9, 1000, 1001, 1002, 1003) + this.mouse_mode = 0; + // 0 (normal), 1005 (utf-8), 1006 (sgr), 1015 (decimal) + this.mouse_protocol = 0; + + // mouse events + this.mouseDown = { 1: false, 2: false, 3: false, 4: false }; + // unicode and fullwidth support + // remainder of 0 width char (combining characters) + this._rem_g = ''; + // remainder of surrogates + this._rem_c = ''; + + // new wrapping behavior + this.wrap = false; + this.row_wrap = false; +}; + +/** + * toSting + * @param [type] + * @returns {string} representation of active buffer. + */ +AnsiTerminal.prototype.toString = function (type){ + var i, j; + var s = ''; + + if (type === 'html') { + var node; + + for (i = 0; i < this.screen.buffer.length; ++i) { + for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { + node = this.screen.buffer[i].cells[j]; + + if (node.value) { + console.log(node.value, ': ', styles(node)); + } + } + } + } else { + for (i = 0; i < this.screen.buffer.length; ++i) { + // FIXME: quick and dirty fill up from left + var last_nonspace = 0; + + for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { + if (this.screen.buffer[i].cells[j].value) { + last_nonspace = j; + } + } + + for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { + s += (last_nonspace > j) + ? (this.screen.buffer[i].cells[j].value || ' ') + : this.screen.buffer[i].cells[j].value; + } + + s += '\n'; + } + } + + return s; +}; + +/** + * Resize terminal to cols x rows. + * + * @param cols + * @param rows + */ +AnsiTerminal.prototype.resize = function (cols, rows){ + // skip insane values + if ((cols < 2) || (rows < 2)) { + return false; + } + + // normal scroll buffer + //this._resize(cols, rows, this.normal_buffer, this.normal_cursor, true); + this.normal_screen.resize(cols, rows, this.normal_cursor); + + // alternative buffer + //this._resize(cols, rows, this.alternate_buffer, this.alternate_cursor, false); + this.alternate_screen.resize(cols, rows, this.alternate_cursor); + + // set new rows / cols to terminal + this.rows = rows; + this.cols = cols; + // FIXME: how to deal with scrolling area? - simply reset for now + this.scrolling_top = 0; + this.scrolling_bottom = this.rows; + + // if cursor got saved before we need to overwrite the saved values + if (this.cursor_save) { + this.DECSC(); + } +}; + +/** + * Propagate mouse action to terminal emulator. + * + * @param {string} type - type of action ('mousedown', 'mouseup', 'mousemove' or 'wheel') + * @param {number} button - button number + * @param {number} col - column the mouse action took place. + * @param {number} row - row the mouse action took place. + */ +AnsiTerminal.prototype.mouseAction = function (type, button, col, row){ + if (!this.mouse_mode) { + return; + } + + if (this.mouse_mode === 9 && (type !== 'mousedown' || type !== 'wheel')) { + return; + } + + if (this.mouse_mode === 1000 && type === 'mousemove') { + return; + } + + if (this.mouse_mode === 1002 && type === 'mousemove' && !this.mouseDown[button]) { + return; + } + + // if we made it this far we got a legal mouse action and process it further + // special state switch for mousemove after mousedown in 1002 + if (this.mouse_mode === 1002) { + if (type == 'mousedown') { + this.mouseDown[button] = true; + } else if (type == 'mouseup') { + this.mouseDown[button] = false; + } + } + + switch (this.mouse_protocol) { + case 0: + break; + case 1005: + break; + case 1006: + break; + case 1015: + break; + default: + console.log('mouse protocol' + this.mouse_protocol + 'not implemented'); + } +}; + +/** + * Implementation of the parser instructions + */ + +/** + * inst_p - handle printable character. + * + * @param {string} s + */ +AnsiTerminal.prototype.inst_p = function (s){ + if (this.debug) { + console.log('inst_p', s); + } + + var c = ''; + var code = 0; + var low = 0; + var width = 1; + + // add leftover surrogate high + if (this._rem_c) { + s += this._rem_c; + this._rem_c = ''; + } + + for (var i = 0; i < s.length; ++i) { + c = s.charAt(i); + code = s.charCodeAt(i); + + // surrogate high + if (0xD800 <= code && code <= 0xDBFF) { + low = s.charCodeAt(i + 1); + + if (low !== low) { + this._rem_c = c; + + return; + } + + code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; + c += s.charAt(i + 1); + } + + // surrogate low - already handled + if (0xDC00 <= code && code <= 0xDFFF) { + continue; + } + + width = wcswidth(code); + + if (width == 2 && (width + this.cursor.col) > this.cols) { + if (this.autowrap) { + this.wrap = true; + } else { + continue; + } + } + + if (this.wrap && width) { + this.cursor.col = 0; + this.cursor.row++; + this.wrap = false; + } + + if (this.cursor.row >= this.scrolling_bottom) { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom, 0, row); + + var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; + + if (!this.scrolling_top) { + this.screen.appendToScrollBuffer(scrolled_out); + } + + this.cursor.row--; + } + + this.screen.buffer[this.cursor.row].version++; + + // combining characters + if (!width && this.cursor.col) { + this.screen.buffer[this.cursor.row].cells[this.cursor.col - 1].value += c; + } else { + c = (this.charset) ? (this.charset[c] || c) : c; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = c; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = this.charattributes; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].gb = this.colors; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].width = width; + + // fix box drawing -- this is a really ugly problem - FIXME: goes into frontend + if (c >= '\u2500' && c <= '\u2547') { + if (this.textattributes && (this.textattributes & 65536)) { + this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = BOXSYMBOLS_BOLD[c] || c; + + // unset bold here, but set intense instead if applicable + var attr = this.charattributes & ~65536; + + if (attr & 67108864 && !(attr & 134217728) && (attr >>> 8 & 255) < 8) { + attr |= 2048; + } + + this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = attr; + } + } + + this.cursor.col += 1; + } + + if (width === 2) { + this.screen.buffer[this.cursor.row].cells[this.cursor.col].width = 0; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = ''; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = this.charattributes; + this.screen.buffer[this.cursor.row].cells[this.cursor.col].gb = this.colors; + this.cursor.col += 1; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 2; + + if (this.autowrap) { + this.wrap = true; + } + } + } + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + + if (this.autowrap) { + this.wrap = true; + } + } + } +}; + +AnsiTerminal.prototype.inst_o = function (s){ + if (this.debug) { + console.log('inst_o', s); + } + + this.last_char = ''; + this.wrap = false; + + if (s.charAt(0) == '0') { + this.title = s.slice(2); + } else { + console.log('inst_o unhandled:', s); + } +}; + +AnsiTerminal.prototype.inst_x = function (flag){ + if (this.debug) { + console.log('inst_x', flag.charCodeAt(0), flag); + } + + this.last_char = ''; + this.wrap = false; + + switch (flag) { + case '\n': + this.cursor.row++; + + if (this.cursor.row >= this.scrolling_bottom) { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom, 0, row); + + var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; + + if (!this.scrolling_top) { + this.screen.appendToScrollBuffer(scrolled_out); + } + + this.cursor.row--; + } + + if (this.newline_mode) { + this.cursor.col = 0; + } + + if (this.cursor.col >= this.cols) { + this.cursor.col--; + } + break; + case '\r': + this.cursor.col = 0; + break; + case '\t': + this.CHT([0]); + break; + case '\x07': + this.beep(); + break; + case '\x08': + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } + + this.cursor.col -= 1; + + if (this.cursor.col < 0) { + this.cursor.col = 0; + } + break; + case '\x0b': + this.inst_x('\n'); + break; + case '\x0c': + this.inst_x('\n'); + break; + // activate G1 + case '\x0e': + this.charset = CHARSET_0; + break; + // activate G0 FIXME + case '\x0f': + this.charset = null; + break; + // TODO + case '\x11': + console.log('unhandled DC1 (XON)'); + break; + // DC2 + case '\x12': + break; + // TODO + case '\x13': + console.log('unhandled DC3 (XOFF)'); + break; + // DC4 + case '\x14': + break; + default: + console.log('inst_x unhandled:', flag.charCodeAt(0)); + } +}; + +/** + * missing (from xterm) + * + * CSI Ps g Tab Clear (TBC). + * (more to come...) + * + */ +AnsiTerminal.prototype.inst_c = function (collected, params, flag){ + if (this.debug) { + console.log('inst_c', collected, params, flag); + } + + // hack for getting REP working + if (flag != 'b') { + this.last_char = ''; + } + + this.wrap = false; + + switch (collected) { + case '': + switch (flag) { + case '@': + return this.ICH(params); + case 'E': + return this.CNL(params); + case 'F': + return this.CPL(params); + case 'G': + return this.CHA(params); + case 'D': + return this.CUB(params); + case 'B': + return this.CUD(params); + case 'C': + return this.CUF(params); + case 'A': + return this.CUU(params); + case 'I': + return this.CHT(params); + case 'Z': + return this.CBT(params); + case 'f': + case 'H': + return this.CUP(params); + case 'P': + return this.DCH(params); + case 'J': + return this.ED(params); + case 'K': + return this.EL(params); + case 'L': + return this.IL(params); + case 'M': + return this.DL(params); + case 'S': + return this.SU(params); + case 'T': + return this.SD(params); + case 'X': + return this.ECH(params); + case 'a': + return this.HPR(params); + case 'b': + return this.REP(params); + case 'e': + return this.VPR(params); + case 'd': + return this.VPA(params); + // DA1 + case 'c': + return this.send(TERM_STRING['CSI'] + '?64;1;2;6;9;15;18;21;22c'); + case 'h': + return this.high(collected, params); + case 'l': + return this.low(collected, params); + case 'm': + return this.SGR(params); + case 'n': + return this.DSR(collected, params); + case 'r': + return this.DECSTBM(params); + case '`': + return this.HPA(params); + default : + console.log('inst_c unhandled:', collected, params, flag); + } + break; + case '?': + switch (flag) { + // DECSED as normal ED + case 'J': + return this.ED(params); + // DECSEL as normal EL + case 'K': + return this.EL(params); + case 'h': + return this.high(collected, params); + case 'l': + return this.low(collected, params); + case 'n': + return this.DSR(collected, params); + default : + console.log('inst_c unhandled:', collected, params, flag); + } + break; + case '>': + switch (flag) { + // DA2 + case 'c': + return this.send(TERM_STRING['CSI'] + '>41;1;0c'); + default : + console.log('inst_c unhandled:', collected, params, flag); + } + break; + case '!': + switch (flag) { + case 'p': + return this.DECSTR(); + default : + console.log('inst_c unhandled:', collected, params, flag); + } + break; + default : + console.log('inst_c unhandled:', collected, params, flag); + } +}; + +AnsiTerminal.prototype.inst_e = function (collected, flag){ + if (this.debug) { + console.log('inst_e', collected, flag); + } + + this.last_char = ''; + this.wrap = false; + + switch (flag) { + // complete ESC codes from xterm: + // ESC H Tab Set ( HTS is 0x88). // TODO + // ESC N Single Shift Select of G2 Character Set ( SS2 is 0x8e). This affects next character only. + // ESC O Single Shift Select of G3 Character Set ( SS3 is 0x8f). This affects next character only. + // ESC P Device Control String ( DCS is 0x90). + // ESC V Start of Guarded Area ( SPA is 0x96). + // ESC W End of Guarded Area ( EPA is 0x97). + // ESC X Start of String ( SOS is 0x98). + // ESC Z Return Terminal ID (DECID is 0x9a). Obsolete form of CSI c (DA). + // case 'F': // (SP) 7-bit controls (S7C1T) - not supported + // case 'G': // (SP) 8-bit controls (S8C1T) - not supported + // case 'L': // (SP) Set ANSI conformance level 1 (dpANS X3.134.1) - not supported + // case 'M': // (SP) Set ANSI conformance level 2 (dpANS X3.134.1) - not supported + // case 'N': // (SP) Set ANSI conformance level 3 (dpANS X3.134.1) - not supported + // case '3': // (#) DEC double-height line, top half (DECDHL) - not supported + // case '4': // (#) DEC double-height line, bottom half (DECDHL) - not supported + // case '5': // (#) DEC single-width line (DECSWL) - not supported + // case '6': // (#) DEC double-width line (DECDWL) - not supported + // case '8': // (#) DEC Screen Alignment Test (DECALN) - not supported + // case '@': // (%) Select default character set. That is ISO 8859-1 (ISO 2022) - not supported + // case 'G': // (%) Select UTF-8 character set (ISO 2022) - not supported + // (() Designate G0 Character Set (ISO 2022, VT100) + // more flags: A B < %5 > 4 C 5 R f Q 9 K Y ` E 6 %6 Z H 7 = + // more collected: ) G1, * G2, + G3, - G1, . G2, / G3 + case '0': + if (collected == '(' || collected == ')') this.charset = CHARSET_0; + break; + // always reset charset + case 'B': + this.charset = null; + break; + // case '6': // Back Index (DECBI), VT420 and up - not supported + // Save Cursor (DECSC) + case '7': + return this.DECSC(); + // Restore Cursor (DECRC) + case '8': + return this.DECRC(); + // case '9': // Forward Index (DECFI), VT420 and up - not supported + // case '=': // Application Keypad (DECKPAM) // TODO + // case '>': // Normal Keypad (DECKPNM) // TODO + // case 'F': // Cursor to lower left corner of screen // TODO + // Full Reset (RIS) http://vt100.net/docs/vt220-rm/chapter4.html + case 'c': + return this.reset(); + // case 'l': // Memory Lock (per HP terminals). Locks memory above the cursor. - not supported + // case 'm': // Memory Unlock (per HP terminals). - not supported + // case 'n': // Invoke the G2 Character Set as GL (LS2). - not supported + // case 'o': // Invoke the G3 Character Set as GL (LS3). - not supported + // case '|': // Invoke the G3 Character Set as GR (LS3R). - not supported + // case '}': // Invoke the G2 Character Set as GR (LS2R). - not supported + // case '~': // Invoke the G1 Character Set as GR (LS1R). - not supported + case 'E': + return this.NEL(); + case 'D': + return this.IND(); + // ESC M Reverse Index ( RI is 0x8d). + case 'M': + return this.RI(); + default : + console.log('inst_e unhandled:', collected, flag); + } +}; + +AnsiTerminal.prototype.inst_H = function (collected, params, flag){ + console.log('inst_H unhandled:', collected, params, flag); + + this.last_char = ''; + this.wrap = false; +}; + +AnsiTerminal.prototype.inst_P = function (data){ + console.log('inst_P unhandled:', data); + + this.last_char = ''; + this.wrap = false; +}; + +AnsiTerminal.prototype.inst_U = function (){ + console.log('inst_U unhandled'); + + this.last_char = ''; + this.wrap = false; +}; + +/** + * functionality implementation + * * + * cheatsheets: + * - http://www.inwap.com/pdp10/ansicode.txt + * - overview http://www.vt100.net/docs/vt510-rm/chapter4 + * - http://paulbourke.net/dataformats/ascii/ + * - mouse support: http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html + * - sequences: http://docs2.attachmate.com/verastream/vhi/7.6sp1/en/index.jsp?topic=%2Fcom.attachmate.vhi.help%2Fhtml%2Freference%2Fcontrol_functions_sortbysequ.xhtml + */ + +/** + * unhandled sequences: (mc - mouse support) + * "inst_c unhandled:" "?" Array [ 2004 ] "h" bracketed paste mode https://cirw.in/blog/bracketed-paste + * + */ + + // scroll down - http://vt100.net/docs/vt510-rm/SD + // FIXME: apply new buffer logic +AnsiTerminal.prototype.SD = function (params){ + var lines = (params[0]) ? params[0] : 1; + + do { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_top, 0, row); + this.screen.buffer.splice(this.scrolling_bottom, 1); + } while (--lines); +}; + +// scroll up - http://vt100.net/docs/vt510-rm/SU +// FIXME: apply new buffer logic +AnsiTerminal.prototype.SU = function (params){ + var lines = (params[0]) ? params[0] : 1; + + do { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom, 0, row); + this.screen.buffer.splice(this.scrolling_top, 1); + } while (--lines); +}; + +// repeat - Repeat the preceding graphic character P s times (REP). +// FIXME: hacky solution with this.last_char +AnsiTerminal.prototype.REP = function (params){ + var s = ''; + var c = this.last_char; + var n = (params[0]) ? params[0] : 1; + + if (c) { + do { + s += c; + } while (--n); + + // for max col we need to set col to width to take + // advantage of the autowrapping in inst_p + // FIXME: not true anymore + if (this.cursor.col == this.cols - 1) { + this.cursor.col = this.cols; + } + + this.inst_p(s); + + this.last_char = ''; + } +}; + +// next line - http://vt100.net/docs/vt510-rm/NEL +AnsiTerminal.prototype.NEL = function (){ + this.cursor.row += 1; + + if (this.cursor.row >= this.scrolling_bottom) { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom, 0, row); + + var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; + + if (!this.scrolling_top) { + this.screen.appendToScrollBuffer(scrolled_out); + } + + this.cursor.row -= 1; + } + + this.cursor.col = 0; +}; + +// index - http://vt100.net/docs/vt510-rm/IND +AnsiTerminal.prototype.IND = function (){ + this.cursor.row += 1; + + if (this.cursor.row >= this.scrolling_bottom) { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom, 0, row); + + var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; + + if (!this.scrolling_top) { + this.screen.appendToScrollBuffer(scrolled_out); + } + + this.cursor.row -= 1; + } +}; + +// vertical position relative - http://vt100.net/docs/vt510-rm/VPR +AnsiTerminal.prototype.VPR = function (params){ + this.cursor.row += ((params[0]) ? params[0] : 1); + + if (this.cursor.row >= this.rows) { + this.cursor.row = this.rows - 1; + } +}; + +// horizontal position relative - http://vt100.net/docs/vt510-rm/HPR +AnsiTerminal.prototype.HPR = function (params){ + this.cursor.col += ((params[0]) ? params[0] : 1); + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// horizontal position absolute - http://vt100.net/docs/vt510-rm/HPA +AnsiTerminal.prototype.HPA = function (params){ + this.cursor.col = ((params[0]) ? params[0] : 1) - 1; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// cursor backward tabulation - http://vt100.net/docs/vt510-rm/CBT +AnsiTerminal.prototype.CBT = function (params){ + this.cursor.col = (Math.floor((this.cursor.col - 1) / this.tab_width) + + 1 - ((params[0]) ? params[0] : 1)) * this.tab_width; + + if (this.cursor.col < 0) { + this.cursor.col = 0; + } +}; + +// cursor horizontal forward tabulation - http://vt100.net/docs/vt510-rm/CHT +AnsiTerminal.prototype.CHT = function (params){ + this.cursor.col = (Math.floor(this.cursor.col / this.tab_width) + + ((params[0]) ? params[0] : 1)) * this.tab_width; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// cursor previous line - http://vt100.net/docs/vt510-rm/CPL +AnsiTerminal.prototype.CPL = function (params){ + this.cursor.row -= (params[0]) ? params[0] : 1; + + if (this.cursor.row < 0) { + this.cursor.row = 0; + } + + this.cursor.col = 0; +}; + +// cursor next line - http://vt100.net/docs/vt510-rm/CNL +AnsiTerminal.prototype.CNL = function (params){ + this.cursor.row += (params[0]) ? params[0] : 1; + + if (this.cursor.row >= this.rows) { + this.cursor.row = this.rows - 1; + } + + this.cursor.col = 0; +}; + +// delete line - http://vt100.net/docs/vt510-rm/DL +AnsiTerminal.prototype.DL = function (params){ + var lines = params[0] || 1; + + do { + this.screen.buffer.splice(this.cursor.row, 1); + + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_bottom - 1, 0, row); + } while (--lines); + + // see http://vt100.net/docs/vt220-rm/chapter4.html + this.cursor.col = 0; +}; + +// insert character - http://vt100.net/docs/vt510-rm/ICH +AnsiTerminal.prototype.ICH = function (params){ + var chars = params[0] || 1; + + do { + // FIXME ugly code - do splicing only once + this.screen.buffer[this.cursor.row].cells + .splice(this.cursor.col, 0, new TChar('', this.textattributes, this.colors)); + this.screen.buffer[this.cursor.row].cells.pop(); + } while (--chars); +}; + +// Vertical Line Position Absolute - http://vt100.net/docs/vt510-rm/VPA +AnsiTerminal.prototype.VPA = function (params){ + this.cursor.row = ((params[0]) ? params[0] : 1) - 1; + + if (this.cursor.row >= this.rows) { + this.cursor.row = this.rows - 1; + } +}; + +// erase character - http://vt100.net/docs/vt510-rm/ECH +AnsiTerminal.prototype.ECH = function (params){ + var erase = ((params[0]) ? params[0] : 1) + this.cursor.col; + + erase = (this.cols < erase) ? this.cols : erase; + + for (var i = this.cursor.col; i < erase; ++i) { + this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); + } + + this.screen.buffer[this.cursor.row].version++; +}; + +// Insert Line - http://vt100.net/docs/vt510-rm/IL +AnsiTerminal.prototype.IL = function (params){ + var lines = (params[0]) ? params[0] : 1; + + // FIXME ugly code - less splice possible? + do { + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.cursor.row, 0, row); + this.screen.buffer.splice(this.scrolling_bottom, 1); + } while (--lines); + + // see http://vt100.net/docs/vt220-rm/chapter4.html + this.cursor.col = 0; +}; + +// Set Top and Bottom Margins - http://vt100.net/docs/vt510-rm/DECSTBM +AnsiTerminal.prototype.DECSTBM = function (params){ + var top = params[0] - 1 || 0; + var bottom = params[1] || this.rows; + + top = (top < 0) ? 0 : ((top >= this.rows) ? (this.rows - 1) : top); + bottom = (bottom > this.rows) ? (this.rows) : bottom; + + if (bottom > top) { + this.scrolling_top = top; + this.scrolling_bottom = bottom; + } + + // always set cursor to top (seems xterm always does this - bug?) + this.cursor.row = 0; +}; + +// soft terminal reset - http://vt100.net/docs/vt510-rm/DECSTR +AnsiTerminal.prototype.DECSTR = function (){ + // DECTCEM Text cursor enable --> Cursor enabled. + this.show_cursor = true; + // IRM Insert/replace --> Replace mode. + this.insert_mode = false; + // DECOM Origin --> Absolute (cursor origin at upper-left of screen.) TODO do we need this? + this.CUP(); // at least move cursor home + // DECAWM Autowrap --> No autowrap. TODO: really to false? + //this.autowrap = false; + // DECNRCM National replacement character set --> Multinational set. - unsupported + // KAM Keyboard action --> Unlocked. TODO + // DECNKM Numeric keypad --> Numeric characters. TODO + // DECCKM Cursor keys --> Normal (arrow keys). + this.cursor_key_mode = false; + // DECSTBM Set top and bottom margins --> Top margin = 1; bottom margin = page length. + this.DECSTBM([]); + // G0, G1, G2, G3, GL, GR --> Default settings. - unsupported + this.charset = null; // reset at least to unicode + // SGR Select graphic rendition --> Normal rendition. + this.SGR([0]); + // DECSCA Select character attribute --> Normal (erasable by DECSEL and DECSED). TODO do we need this? + // DECSC Save cursor state --> Home position. + this.DECSC(); + // DECAUPSS Assign user preference supplemental set --> Set selected in Set-Up. - unsupported + // DECSASD Select active status display --> Main display. TODO do we need this? + // DECKPM Keyboard position mode --> Character codes. TODO do we need this? + // DECRLM Cursor direction --> Reset (Left-to-right), regardless of NVR setting. TODO + // DECPCTERM PC Term mode --> Always reset. TODO do we need this? + // TODO: do we need to reset LNM? +}; + +// reverse index +AnsiTerminal.prototype.RI = function (){ + this.cursor.row -= 1; + + if (this.cursor.row < this.scrolling_top) { + this.cursor.row = this.scrolling_top; + + var row = new Row(); + + for (var j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer.splice(this.scrolling_top, 0, row); + this.screen.buffer.splice(this.scrolling_bottom, 1); + } +}; + +// save curor - http://vt100.net/docs/vt510-rm/DECSC +AnsiTerminal.prototype.DECSC = function (){ + var save = {}; + + save['cursor'] = { row: this.cursor.row, col: this.cursor.col }; + save['textattributes'] = this.textattributes; + save['charattributes'] = this.charattributes; + this.cursor_save = save; + // FIXME: this.colors +}; + +// restore cursor - http://vt100.net/docs/vt510-rm/DECRC +AnsiTerminal.prototype.DECRC = function (){ + // FIXME: this.colors + if (this.cursor_save) { + // load data back + this.cursor.col = this.cursor_save['cursor'].col; + this.cursor.row = this.cursor_save['cursor'].row; + this.textattributes = this.cursor_save['textattributes']; + this.charattributes = this.cursor_save['charattributes']; + } else { + // see http://vt100.net/docs/vt510-rm/DECRC + this.CUP(); + this.ED([2]); + this.textattributes = 0; + this.charattributes = 0; + } +}; + +AnsiTerminal.prototype.high = function (collected, params){ + // TODO: separate DEC and ANSI + for (var i = 0; i < params.length; ++i) { + switch (params[i]) { + // DECCKM + case 1: + this.cursor_key_mode = true; + break; + case 4: + if (!collected) { + // IRM + this.insert_mode = true; + } else { + // DECSCLM?? + console.log('unhandled high', collected, params[i]); + } + break; + case 7: + if (collected == '?') { + // DECAWM (should be default?) + this.autowrap = true; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 12: + if (collected == '?') { + this.blinking_cursor = true; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 20: + if (!collected) { + // LNM + this.newline_mode = true; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 25: + this.show_cursor = true; + // DECTCEM (default) + break; + // printer stuff not supported + case 43: + case 44: + case 45: + case 46: + case 47: + // end printer stuff + break; + /* mouse handling + * - 5 exclusive mouse modes: + * 9 X10 (press only) + * 1000 press and release events + * 1001 hilite mouse tracking (??) + * 1002 cell motion tracking (press, move on pressed, release) + * 1003 all (press, move, release) + * - exclusive formatting: + * 1005 utf-8 mouse mode + * 1006 sgr mouse mode + * 1015 urxvt mouse mode (decimal) + * - special focus event: 1004 (CSI I / CSI O) + * */ + case 9: + case 1000: + case 1001: + case 1002: + case 1003: + this.mouse_mode = params[i]; + this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); + break; + //case 1004: // focusIn/Out events + case 1005: + case 1006: + case 1015: + this.mouse_protocol = params[i]; + this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); + break; + // alt buffer + case 1049: + this.screen = this.alternate_screen; + this.cursor = this.alternate_cursor; + break; + default: + console.log('unhandled high', collected, params[i]); + } + } +}; + +AnsiTerminal.prototype.low = function (collected, params){ + // TODO: separate DEC and ANSI + for (var i = 0; i < params.length; ++i) { + switch (params[i]) { + case 1: + this.cursor_key_mode = false; + // DECCKM (default) + break; + case 4: + // IRM (default) + if (!collected) { + this.insert_mode = false; + } else { + console.log('unhandled low', collected, params[i]); + } + break; + case 7: + if (collected == '?') { + // DECAWM (default) + this.autowrap = false; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 12: + if (collected == '?') { + this.blinking_cursor = false; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 20: + if (!collected) { + // LNM (default) + this.newline_mode = false; + } else { + console.log('unhandled high', collected, params[i]); + } + break; + case 25: + this.show_cursor = false; + // DECTCEM + break; + // printer stuff not supported + case 43: + case 44: + case 45: + case 46: + case 47: + // end printer stuff + break; + case 9: + case 1000: + case 1001: + case 1002: + case 1003: + this.mouse_mode = 0; + this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); + break; + //case 1004: // focusIn/Out events + case 1005: + case 1006: + case 1015: + this.mouse_protocol = 0; + this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); + break; + case 1049: + this.screen = this.normal_screen; + this.cursor = this.normal_cursor; + break; + default: + console.log('unhandled low', collected, params[i]); + } + } +}; + +// device status reports - http://vt100.net/docs/vt510-rm/DSR +// cursor position report - http://vt100.net/docs/vt510-rm/CPR +// FIXME: split and rename to DSR and CPR +AnsiTerminal.prototype.DSR = function (collected, params){ + switch (params[0]) { + // DSR - just send 'OK' + case 5: + this.send(TERM_STRING['CSI'] + '0n'); + break; + // cursor position report + case 6: + this.send(TERM_STRING['CSI'] + collected + (this.cursor.row + 1) + ';' + (this.cursor.col + 1) + 'R'); + break; + // DSR-DIR data integrity report - just send 'ready, no errors' + case 75: + this.send(TERM_STRING['CSI'] + '?70n'); + break; + default: + console.log('unhandled DSR', collected, params); + } +}; + +// cursor horizontal absolute - http://vt100.net/docs/vt510-rm/CHA +AnsiTerminal.prototype.CHA = function (params){ + this.cursor.col = ((params) ? (params[0] || 1) : 1) - 1; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// cursor backward - http://vt100.net/docs/vt510-rm/CUB +AnsiTerminal.prototype.CUB = function (params){ + this.cursor.col -= (params) ? (params[0] || 1) : 1; + + if (this.cursor.col < 0) { + this.cursor.col = 0; + } +}; + +// cursor down - http://vt100.net/docs/vt510-rm/CUD +AnsiTerminal.prototype.CUD = function (params){ + this.cursor.row += (params) ? (params[0] || 1) : 1; + + if (this.cursor.row >= this.rows) { + this.cursor.row = this.rows - 1; + } +}; + +// cursor forward - http://vt100.net/docs/vt510-rm/CUF +AnsiTerminal.prototype.CUF = function (params){ + this.cursor.col += (params) ? (params[0] || 1) : 1; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// cursor up - http://vt100.net/docs/vt510-rm/CUU +AnsiTerminal.prototype.CUU = function (params){ + this.cursor.row -= (params) ? (params[0] || 1) : 1; + + if (this.cursor.row < 0) { + this.cursor.row = 0; + } +}; + +// cursor position - http://vt100.net/docs/vt510-rm/CUP +AnsiTerminal.prototype.CUP = function (params){ + this.cursor.row = ((params) ? (params[0] || 1) : 1) - 1; + + if (this.cursor.row >= this.rows) { + this.cursor.row = this.rows - 1; + } + + this.cursor.col = ((params) ? (params[1] || 1) : 1) - 1; + + if (this.cursor.col >= this.cols) { + this.cursor.col = this.cols - 1; + } +}; + +// delete character - http://vt100.net/docs/vt510-rm/DCH +AnsiTerminal.prototype.DCH = function (params){ + var removed = this.screen.buffer[this.cursor.row].cells + .splice(this.cursor.col, (params) ? (params[0] || 1) : 1); + + for (var i = 0; i < removed.length; ++i) { + this.screen.buffer[this.cursor.row].cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer[this.cursor.row].version++; +}; + +// erase in display - http://vt100.net/docs/vt510-rm/ED +AnsiTerminal.prototype.ED = function (params){ + var i, j, row; + + switch ((params) ? params[0] : 0) { + case 0: + // from cursor to end of display + // remove to line end + this.EL([0]); + // clear lower lines + for (i = this.cursor.row + 1; i < this.rows; ++i) { + row = new Row(); + + for (j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer[i] = row; + } + break; + case 1: + // from top of display to cursor + // clear upper lines + for (i = 0; i < this.cursor.row; ++i) { + row = new Row(); + + for (j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer[i] = row; + } + + // clear line up to cursor + this.EL([1]); + break; + case 2: + // complete display + for (i = 0; i < this.rows; ++i) { + row = new Row(); + + for (j = 0; j < this.cols; ++j) { + row.cells.push(new TChar('', this.textattributes, this.colors)); + } + + this.screen.buffer[i] = row; + } + break; + } +}; + +// erase in line - http://vt100.net/docs/vt510-rm/EL +AnsiTerminal.prototype.EL = function (params){ + var i; + + switch ((params) ? params[0] : 0) { + case 0: + // cursor to end of line + for (i = this.cursor.col; i < this.cols; ++i) { + this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); + } + + this.screen.buffer[this.cursor.row].version++; + break; + case 1: + // beginning of line to cursor + for (i = 0; i <= this.cursor.col; ++i) { + this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); + } + + this.screen.buffer[this.cursor.row].version++; + break; + case 2: + // complete line + for (i = 0; i < this.cols; ++i) { + this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); + } + + this.screen.buffer[this.cursor.row].version++; + break; + } +}; + +// select graphic rendition - http://vt100.net/docs/vt510-rm/SGR +AnsiTerminal.prototype.SGR = function (params){ + // load global attributes and colors + var attr = this.textattributes; + var colors = this.colors; + + var ext_colors = 0; + var RGB_mode = false; + var counter = 0; + + // put reverse video mode in attributes + // used in charattributes but not in global textattributes + // to mimick xterm behavior + if (this.reverse_video) { + attr |= 1048576; + } + + for (var i = 0; i < params.length; ++i) { + // special treatment for extended colors + if (ext_colors) { + // first run in ext_colors gives color mode + // sets counter to determine max consumed params + if (!counter) { + switch (params[i]) { + case 2: + RGB_mode = true; + // eval up to 3 params + counter = 3; + // fg set SET+RGB: |(1<<26)|(1<<27) + // bg set SET+RGB: |(1<<24)|(1<<25) + attr |= (ext_colors == 38) ? 201326592 : 50331648; + break; + case 5: + RGB_mode = false; + // eval only 1 param + counter = 1; + // fg clear RGB, set SET: &~(1<<27)|(1<<26) + // bg clear RGB, set SET: &~(1<<25)|(1<<24) + attr = (ext_colors == 38) + ? (attr & ~134217728) | 67108864 + : (attr & ~33554432) | 16777216; + break; + default: + // unkown mode identifier, breaks ext_color mode + console.log('sgr unknown extended color mode:', ext_colors[1]); + ext_colors = 0; + } + continue; + } + if (RGB_mode) { + switch (counter) { + case 3: + // red + attr = (ext_colors == 38) + ? (attr & ~65280) | (params[i] << 8) + : (attr & ~255) | params[i]; + break; + case 2: + // green + colors = (ext_colors == 38) + ? (colors & ~4278190080) | (params[i] << 24) + : (colors & ~16711680) | (params[i] << 16); + break; + case 1: + // blue + colors = (ext_colors == 38) + ? (colors & ~65280) | (params[i] << 8) + : (colors & ~255) | params[i]; + } + } else { + // 256 color mode + // uses only lower bytes of attribute + attr = (ext_colors == 38) + ? (attr & ~65280) | (params[i] << 8) + : (attr & ~255) | params[i]; + } + counter -= 1; + + if (!counter) { + ext_colors = 0; + } + continue; + } + + switch (params[i]) { + case 0: + attr = 0; + break; + // bold on + case 1: + attr |= 65536; + break; + // not supported (faint) + case 2: + break; + // italic on + case 3: + attr |= 131072; + break; + // underline on + case 4: + attr |= 262144; + break; + // blink on + case 5: + attr |= 524288; + break; + // only one blinking speed + case 6: + attr |= 524288; + break; + // inverted on + case 7: + attr |= 1048576; + break; + // conceal on + case 8: + attr |= 2097152; + break; + // not supported (crossed out) + case 9: + break; + // not supported (font selection) + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + break; + // not supported (fraktur) + case 20: + break; + case 21: + // not supported (bold: off or underline: double) + break; + // bold off + case 22: + attr &= ~65536; + break; + // italic off + case 23: + attr &= ~131072; + break; + // underline off + case 24: + attr &= ~262144; + break; + // blink off + case 25: + attr &= ~524288; + break; + // reserved + case 26: + break; + // inverted off + case 27: + attr &= ~1048576; + break; + // conceal off + case 28: + attr &= ~2097152; + break; + // not supported (not crossed out) + case 29: + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + // clear fg RGB, nullify fg, set fg SET, color + // -134283009 = ~(1<<27) & ~(255<<8) + attr = (attr & -134283009) | 67108864 | (params[i] % 10 << 8); + break; + case 38: + ext_colors = 38; + break; + // default foreground color + case 39: + // fg set to false (1<<26) + attr &= ~67108864; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + // clear bg RGB, nullify bg, set bg SET, color + // -33554688 = ~(1<<25) & ~255 + attr = (attr & -33554688) | 16777216 | params[i] % 10; + break; + case 48: + ext_colors = 48; + break; + // default background color + case 49: + // bg set to false + attr &= ~16777216; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + // same as 37 but with |8 in color + attr = (attr & -134283009) | 67108864 | (params[i] % 10 | 8 << 8); + break; + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + // same as 47 but with |8 in color + attr = (attr & -33554688) | 16777216 | params[i] % 10 | 8; + break; + default: + console.log('sgr unknown:', params[i]); + } + } + + // apply new attributes + // charattributes differs only in reverse mode + // for now from textattributes + this.charattributes = attr; + + // set reverse video and delete it from attributes + this.reverse_video = !!(attr & 1048576); + attr &= ~1048576; + + // set new global attributes + this.textattributes = attr; + this.colors = colors; +}; + +/** + * hex + * @param c + * @returns {*} + */ +function hex(c){ + c = c.toString(16); + + return c.length < 2 ? '0' + c : c; +} + +/** + * 256 colors + */ +var COLORS = (function colors256(){ + var i; + var r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; + + // copy colors + var colors = [ + // 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 + ]; + + // 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)); + } + + return colors; +}()); + +/** + * char styles + * @param node + * @returns {{wide: boolean}} + */ +function styles(node){ + var color; + var gb = node.gb; + var attr = node.attr; + var attributes = node.getAttributes(); + var foreground = attributes.foreground; + var background = attributes.background; + var styles = { wide: node.width === 2 }; + + [ + 'bold', 'italic', 'underline', + 'blink', 'inverse', 'conceal' + ].forEach(function (key){ + styles[key] = attributes[key]; + }); + + styles.frgb = foreground.RGB; + styles.grgb = background.RGB; + + if (foreground.set && !foreground.RGB) { + if (attributes.inverse) { + if (attributes.bold) { + styles.background = COLORS[(attr >>> 8 & 255) | 8]; + } else { + styles.background = COLORS[attr >>> 8 & 255]; + } + } else { + if (attributes.bold) { + styles.foreground = COLORS[(attr >>> 8 & 255) | 8]; + } else { + styles.foreground = COLORS[attr >>> 8 & 255]; + } + } + } + + if (background.set && !background.RGB) { + if (attributes.inverse) { + styles.foreground = COLORS[attr & 255]; + } else { + styles.background = COLORS[attr & 255]; + } + } + + if (foreground.set && foreground.RGB) { + color = '#' + hex(attr >>> 8 & 255) + hex(gb >>> 24) + hex(gb >>> 8 & 255); + + if (attributes.inverse) { + styles.background = color; + } else { + styles.foreground = color; + } + } + + if (background.set && background.RGB) { + color = '#' + hex(attr & 255) + hex(gb >>> 16 & 255) + hex(gb & 255); + + if (attributes.inverse) { + styles.foreground = color; + } else { + styles.background = color; + } + } + + return styles; +} + +var colors = ''; + +COLORS.forEach(function (color, index){ + colors += '\r\n' + index + ': ' + color; +}); + +console.log(colors); diff --git a/static/js/terminal/lib/blankLine.js b/static/js/terminal/lib/blankLine.js deleted file mode 100644 index 059f503..0000000 --- a/static/js/terminal/lib/blankLine.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * blankLine - * @param [cur] - * @returns {Array} - */ - Terminal.prototype.blankLine = function (cur){ - var attr = cur ? this.eraseAttr() : this.defAttr; - var ch = [attr, ' ']; - var line = []; - var i = 0; - - for (; i < this.cols; i++) { - line[i] = ch; - } - - return line; - }; -}; diff --git a/static/js/terminal/lib/charsets.js b/static/js/terminal/lib/charsets.js deleted file mode 100644 index 4587605..0000000 --- a/static/js/terminal/lib/charsets.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - 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 -}; diff --git a/static/js/terminal/lib/close.js b/static/js/terminal/lib/close.js deleted file mode 100644 index c72724d..0000000 --- a/static/js/terminal/lib/close.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * close - */ - Terminal.prototype.close = function (){ - this.lines = []; - this.children = []; - this.readable = false; - this.writable = false; - this.write = function (){}; - this.ondata = function (){}; - this.ontitle = function (){}; - - if (this.screen) { - var parent = this.screen.parentNode; - - if (parent) { - parent.removeChild(this.screen); - } - - this.screen = null; - } - }; -}; diff --git a/static/js/terminal/lib/colors.js b/static/js/terminal/lib/colors.js deleted file mode 100644 index 2bbfe21..0000000 --- a/static/js/terminal/lib/colors.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // Default colors - Terminal.defaultColors = { - // Colors 0-15 - colors: [ - // 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 - ], - // Default background color - background: '#181818', - // Default foreground color - foreground: '#ffffff' - }; - - // Colors 16-255 - // Much thanks to TooTallNate for writing this. - Terminal.makeColors = function (colors){ - var i; - var r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - - // copy colors - colors = colors.slice(); - - // 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; - }; - - // Vcolors 0-255 - Terminal.makeVcolors = function (colors){ - var color; - var i = 0; - var out = []; - - for (; i < 256; i++) { - color = parseInt(colors[i].substring(1), 16); - - out.push([(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff]); - } - - return out; - }; - - // Colors 0-255 - Terminal.colors = Terminal.makeColors(Terminal.defaultColors.colors); - // Vcolors 0-255 - Terminal.vcolors = Terminal.makeVcolors(Terminal.colors); -}; diff --git a/static/js/terminal/lib/csi/charAttributes.js b/static/js/terminal/lib/csi/charAttributes.js deleted file mode 100644 index 92ad7bb..0000000 --- a/static/js/terminal/lib/csi/charAttributes.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * matchColor - * @param vcolors - * @param r1 - * @param g1 - * @param b1 - * @returns {*} - */ - function matchColor(vcolors, r1, g1, b1){ - var hash = (r1 << 16) | (g1 << 8) | b1; - - if (matchColor._cache.hasOwnProperty(hash + '')) { - return matchColor._cache[hash]; - } - - var i = 0; - var li = -1; - var ldiff = Infinity; - var c, r2, g2, b2, diff; - - for (; i < vcolors.length; i++) { - c = 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); - }; - - // 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 p; - var i = 0; - var l = params.length; - var flags = this.curAttr >> 18; - var fg = (this.curAttr >> 9) & 0x1ff; - var bg = this.curAttr & 0x1ff; - - 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(this.vcolors, 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(this.vcolors, 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; - }; -}; diff --git a/static/js/terminal/lib/csi/cursor.js b/static/js/terminal/lib/csi/cursor.js deleted file mode 100644 index e01419d..0000000 --- a/static/js/terminal/lib/csi/cursor.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // CSI s - // Save cursor (ANSI.SYS). - Terminal.prototype.saveCursor = function (){ - this.savedX = this.x; - this.savedY = this.y; - }; - - // CSI u - // Restore cursor (ANSI.SYS). - Terminal.prototype.restoreCursor = function (){ - this.x = this.savedX || 0; - this.y = this.savedY || 0; - }; - - // 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 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 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 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(); - } - }; -}; diff --git a/static/js/terminal/lib/csi/device.js b/static/js/terminal/lib/csi/device.js deleted file mode 100644 index 89233ae..0000000 --- a/static/js/terminal/lib/csi/device.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Created by nuintun on 2015/11/25. - */ - - -'use strict'; - -module.exports = function (Terminal){ - // 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; - } - } - }; - - // 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'); - } - } - }; -}; diff --git a/static/js/terminal/lib/csi/erase.js b/static/js/terminal/lib/csi/erase.js deleted file mode 100644 index 7d18cf8..0000000 --- a/static/js/terminal/lib/csi/erase.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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; - } - }; -}; diff --git a/static/js/terminal/lib/csi/insert-delete.js b/static/js/terminal/lib/csi/insert-delete.js deleted file mode 100644 index bbb8e97..0000000 --- a/static/js/terminal/lib/csi/insert-delete.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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.curAttr, ' ']; // xterm - - while (param-- && j < this.cols) { - this.lines[row].splice(j++, 0, ch); - this.lines[row].pop(); - } - }; - - // 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 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 (params){ - var i; - var param = params[0]; - var l = this.ybase + this.rows; - var ch = [this.eraseAttr(), ' ']; // xterm - - 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 (params){ - var i; - var param = params[0]; - var l = this.ybase + this.rows; - var ch = [this.eraseAttr(), ' ']; // xterm - - while (param--) { - for (i = this.ybase; i < l; i++) { - this.lines[i].splice(this.x, 1); - this.lines[i].push(ch); - } - } - - this.maxRange(); - }; - - // 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.curAttr, ' ']; // 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.curAttr, ' ']; // xterm - - while (param-- && j < this.cols) { - this.lines[row][j++] = ch; - } - }; -}; diff --git a/static/js/terminal/lib/csi/mode.js b/static/js/terminal/lib/csi/mode.js deleted file mode 100644 index 790b0e9..0000000 --- a/static/js/terminal/lib/csi/mode.js +++ /dev/null @@ -1,416 +0,0 @@ -/** - * Created by nuintun on 2015/11/25. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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 (Array.isArray(params)) { - var i = 0; - var l = params.length; - - 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; - // 132 col mode - case 3: - // -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.applicationKeypad = true; - break; - // X10 Mouse - case 9: - // vt200 mouse - // no release, no motion, no wheel, no modifiers. - case 1000: - // no motion. - // no modifiers, except control on the wheel. - // button event mouse - case 1002: - // any event mouse - case 1003: - // 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.screen.style.cursor = 'default'; - break; - // send focusin/focusout events - case 1004: - // focusin: ^[[I - // focusout: ^[[O - // -this.sendFocus = true; - break; - // utf8 ext mode mouse - case 1005: - // -this.utfMouse = true; - // for wide terminals - // simply encodes large values as utf8 characters - break; - // sgr ext mode mouse - case 1006: - // -this.sgrMouse = true; - // for wide terminals - // does not add 32 to fields - // press: ^[[ Keyboard Action Mode (AM). - // Ps = 4 -> Replace Mode (IRM). - // Ps = 1 2 -> Send/receive (SRM). - // Ps = 2 0 -> Normal Linefeed (LNM). - // CSI ? Pm l - // DEC Private Mode Reset (DECRST). - // Ps = 1 -> Normal Cursor Keys (DECCKM). - // Ps = 2 -> Designate VT52 mode (DECANM). - // Ps = 3 -> 80 Column Mode (DECCOLM). - // Ps = 4 -> Jump (Fast) Scroll (DECSCLM). - // Ps = 5 -> Normal Video (DECSCNM). - // Ps = 6 -> Normal Cursor Mode (DECOM). - // Ps = 7 -> No Wraparound Mode (DECAWM). - // Ps = 8 -> No Auto-repeat Keys (DECARM). - // Ps = 9 -> Don't send Mouse X & Y on button press. - // Ps = 1 0 -> Hide toolbar (rxvt). - // Ps = 1 2 -> Stop Blinking Cursor (att610). - // Ps = 1 8 -> Don't print form feed (DECPFF). - // Ps = 1 9 -> Limit print to scrolling region (DECPEX). - // Ps = 2 5 -> Hide Cursor (DECTCEM). - // Ps = 3 0 -> Don't show scrollbar (rxvt). - // Ps = 3 5 -> Disable font-shifting functions (rxvt). - // Ps = 4 0 -> Disallow 80 -> 132 Mode. - // Ps = 4 1 -> No more(1) fix (see curses resource). - // Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- - // NRCM). - // Ps = 4 4 -> Turn Off Margin Bell. - // Ps = 4 5 -> No Reverse-wraparound Mode. - // Ps = 4 6 -> Stop Logging. (This is normally disabled by a - // compile-time option). - // Ps = 4 7 -> Use Normal Screen Buffer. - // Ps = 6 6 -> Numeric keypad (DECNKM). - // Ps = 6 7 -> Backarrow key sends delete (DECBKM). - // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and - // release. See the section Mouse Tracking. - // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. - // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. - // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. - // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. - // Ps = 1 0 0 5 -> Disable Extended Mouse Mode. - // Ps = 1 0 1 0 -> Don't scroll to bottom on tty output - // (rxvt). - // Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). - // Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables - // the eightBitInput resource). - // Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- - // Lock keys. (This disables the numLock resource). - // Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. - // (This disables the metaSendsEscape resource). - // Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad - // Delete key. - // Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. - // (This disables the altSendsEscape resource). - // Ps = 1 0 4 0 -> Do not keep selection when not highlighted. - // (This disables the keepSelection resource). - // Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables - // the selectToClipboard resource). - // Ps = 1 0 4 2 -> Disable Urgency window manager hint when - // Control-G is received. (This disables the bellIsUrgent - // resource). - // Ps = 1 0 4 3 -> Disable raising of the window when Control- - // G is received. (This disables the popOnBell resource). - // Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen - // first if in the Alternate Screen. (This may be disabled by - // the titeInhibit resource). - // Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be - // disabled by the titeInhibit resource). - // Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor - // as in DECRC. (This may be disabled by the titeInhibit - // resource). This combines the effects of the 1 0 4 7 and 1 0 - // 4 8 modes. Use this with terminfo-based applications rather - // than the 4 7 mode. - // Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. - // Ps = 1 0 5 1 -> Reset Sun function-key mode. - // Ps = 1 0 5 2 -> Reset HP function-key mode. - // Ps = 1 0 5 3 -> Reset SCO function-key mode. - // Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). - // Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. - // Ps = 2 0 0 4 -> Reset bracketed paste mode. - Terminal.prototype.resetMode = function (params){ - if (Array.isArray(params)) { - var i = 0; - var l = params.length; - - 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 2: - // reset charset mode here - break; - case 3: - // -if (this.cols === 132 && this.savedCols) { - // - this.resize(this.savedCols, this.rows); - // -} - - // -this.savedCols = null; - break; - case 6: - this.originMode = false; - break; - case 7: - this.wraparoundMode = false; - break; - case 12: - this.cursorBlink = false; - break; - case 66: - this.applicationKeypad = false; - break; - // X10 Mouse - case 9: - // vt200 mouse - case 1000: - // button event mouse - case 1002: - // any event mouse - case 1003: - // -this.x10Mouse = false; - // -this.vt200Mouse = false; - // -this.normalMouse = false; - // -this.mouseEvents = false; - // -this.screen.style.cursor = ''; - break; - // send focusin/focusout events - case 1004: - // -this.sendFocus = false; - break; - // utf8 ext mode mouse - case 1005: - // -this.utfMouse = false; - break; - // sgr ext mode mouse - case 1006: - // -this.sgrMouse = false; - break; - // urxvt ext mode mouse - case 1015: - // -this.urxvtMouse = false; - break; - // hide cursor - case 25: - this.cursor = false; - - this.hideCursor(); - break; - // alt screen buffer cursor - case 1049: - // FALL-THROUGH - // normal screen buffer - clearing it first - case 47: - // normal screen buffer - clearing it first - case 1047: - 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; - - this.refresh(0, this.rows - 1); - this.showCursor(); - } - break; - } - } - }; -}; diff --git a/static/js/terminal/lib/csi/position.js b/static/js/terminal/lib/csi/position.js deleted file mode 100644 index 8a04458..0000000 --- a/static/js/terminal/lib/csi/position.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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 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; - } - }; -}; diff --git a/static/js/terminal/lib/csi/repeatPrecedingCharacter.js b/static/js/terminal/lib/csi/repeatPrecedingCharacter.js deleted file mode 100644 index 48494e2..0000000 --- a/static/js/terminal/lib/csi/repeatPrecedingCharacter.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // CSI Ps b Repeat the preceding graphic character Ps times (REP). - Terminal.prototype.repeatPrecedingCharacter = function (params){ - var param = params[0] || 1; - var line = this.lines[this.ybase + this.y]; - var ch = line[this.x - 1] || [this.defAttr, ' ']; - - while (param--) line[this.x++] = ch; - }; -}; diff --git a/static/js/terminal/lib/csi/scroll.js b/static/js/terminal/lib/csi/scroll.js deleted file mode 100644 index 1d5c151..0000000 --- a/static/js/terminal/lib/csi/scroll.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Created by nuintun on 2015/11/25. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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.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.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); - }; - - // 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.x = 0; - this.y = 0; - this.scrollTop = (params[0] || 1) - 1; - this.scrollBottom = (params[1] || this.rows) - 1; - }; -}; diff --git a/static/js/terminal/lib/csi/softReset.js b/static/js/terminal/lib/csi/softReset.js deleted file mode 100644 index b4b9e5c..0000000 --- a/static/js/terminal/lib/csi/softReset.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // CSI ! p Soft terminal reset (DECSTR). - // http://vt100.net/docs/vt220-rm/table4-10.html - Terminal.prototype.softReset = function (){ - this.insertMode = false; - this.originMode = false; - // autowrap - this.wraparoundMode = false; - this.applicationKeypad = 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]; - }; -}; diff --git a/static/js/terminal/lib/csi/tabClear.js b/static/js/terminal/lib/csi/tabClear.js deleted file mode 100644 index 2ea649b..0000000 --- a/static/js/terminal/lib/csi/tabClear.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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 = {}; - } - }; -}; diff --git a/static/js/terminal/lib/cursor.js b/static/js/terminal/lib/cursor.js deleted file mode 100644 index c45c4b1..0000000 --- a/static/js/terminal/lib/cursor.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * showCursor - */ - Terminal.prototype.showCursor = function (){ - if (this.cursor && !this._cursor) { - this._cursor = true; - this.cursorState = 1; - - this.refresh(this.y, this.y); - - if (this.cursorBlink && !this._blink && this._blinker) { - this._blink = setInterval(this._blinker, this.cursorBlinkSpeed); - } - } - }; - - /** - * hideCursor - */ - Terminal.prototype.hideCursor = function (){ - if (this._cursor) { - delete this._cursor; - - this.cursorState = 0; - - clearInterval(this._blink); - - delete this._blink; - - if (!this.cursorBlink) { - delete this._blinker; - } - - this.refresh(this.y, this.y); - } - }; - - /** - * startBlink - */ - Terminal.prototype.startBlink = function (){ - if (this.cursor && this.cursorBlink && !this._blink) { - var context = this; - - this._blinker = function (){ - if (context._cursor) { - context.cursorState ^= 1; - - context.refresh(context.y, context.y); - } - }; - - if (this._cursor) { - this._blink = setInterval(this._blinker, this.cursorBlinkSpeed); - } - } - }; - - /** - * stopBlink - */ - Terminal.prototype.stopBlink = function (){ - if (this._blink && this._blinker) { - clearInterval(this._blink); - - delete this._blink; - delete this._blinker; - - if (this.cursor && this._cursor) { - this.cursorState = 1; - } else { - this.cursorState = 0; - } - - this.refresh(this.y, this.y); - } - }; -}; diff --git a/static/js/terminal/lib/debug.js b/static/js/terminal/lib/debug.js deleted file mode 100644 index 1b03750..0000000 --- a/static/js/terminal/lib/debug.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * log - */ - Terminal.prototype.log = function (){ - if (!this.debug) return; - - if (!window.console || !window.console.log) return; - - var args = Array.prototype.slice.call(arguments); - - window.console.log.apply(window.console, args); - }; - - /** - * error - */ - Terminal.prototype.error = function (){ - if (!this.debug) return; - - if (!window.console || !window.console.error) return; - - var args = Array.prototype.slice.call(arguments); - - window.console.error.apply(window.console, args); - }; -}; diff --git a/static/js/terminal/lib/erase.js b/static/js/terminal/lib/erase.js deleted file mode 100644 index 623f37c..0000000 --- a/static/js/terminal/lib/erase.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * eraseAttr - * @returns {number} - */ - Terminal.prototype.eraseAttr = function (){ - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); - }; - - /** - * eraseRight - * @param x - * @param y - */ - Terminal.prototype.eraseRight = function (x, y){ - var line = this.lines[this.ybase + y]; - var ch = [this.eraseAttr(), ' ']; - - for (; x < this.cols; x++) { - line[x] = ch; - } - - this.updateRange(y); - }; - - /** - * eraseLeft - * @param x - * @param y - */ - Terminal.prototype.eraseLeft = function (x, y){ - var line = this.lines[this.ybase + y]; - var ch = [this.eraseAttr(), ' ']; - - x++; - - while (x--) line[x] = ch; - - this.updateRange(y); - }; - - /** - * eraseLine - * @param y - */ - Terminal.prototype.eraseLine = function (y){ - this.eraseRight(0, y); - }; -}; diff --git a/static/js/terminal/lib/esc/index.js b/static/js/terminal/lib/esc/index.js deleted file mode 100644 index 9ab0414..0000000 --- a/static/js/terminal/lib/esc/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -var states = require('../states'); - -module.exports = function (Terminal){ - // ESC D Index (IND is 0x84). - Terminal.prototype.index = function (){ - this.y++; - - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - - this.state = states.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.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); - } - - this.state = states.normal; - }; -}; diff --git a/static/js/terminal/lib/esc/reset.js b/static/js/terminal/lib/esc/reset.js deleted file mode 100644 index 5105482..0000000 --- a/static/js/terminal/lib/esc/reset.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // ESC c Full Reset (RIS). - Terminal.prototype.reset = function (){ - var parent; - - if (this.screen) { - parent = this.screen.parentNode; - - if (parent) { - parent.removeChild(this.screen); - } - } - - Terminal.call(this, this.options); - - this.open(); - - if (parent) { - parent.appendChild(this.screen); - } - }; -}; diff --git a/static/js/terminal/lib/esc/tabSet.js b/static/js/terminal/lib/esc/tabSet.js deleted file mode 100644 index e57557e..0000000 --- a/static/js/terminal/lib/esc/tabSet.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; -var states = require('../states'); - -module.exports = function (Terminal){ - // ESC H Tab Set (HTS is 0x88). - Terminal.prototype.tabSet = function (){ - this.tabs[this.x] = true; - this.state = states.normal; - }; -}; diff --git a/static/js/terminal/lib/focused.js b/static/js/terminal/lib/focused.js deleted file mode 100644 index f8abcf4..0000000 --- a/static/js/terminal/lib/focused.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Created by nuintun on 2015/11/25. - */ - -'use strict'; - -module.exports = function (Terminal){ - Terminal.focus = null; - - /** - * isFocused - * @returns {boolean} - */ - Terminal.prototype.isFocused = function (){ - return Terminal.focus === this; - }; - - /** - * focus - */ - Terminal.prototype.focus = function (){ - if (Terminal.focus === this) return; - - if (Terminal.focus) { - Terminal.focus.blur(); - } - - Terminal.focus = this; - - if (this.cursor) { - this.showCursor(); - } - - if (this.cursorBlink) { - this.startBlink(); - } - }; - - /** - * blur - */ - Terminal.prototype.blur = function (){ - if (Terminal.focus !== this) return; - - this.hideCursor(); - - Terminal.focus = null; - }; -}; diff --git a/static/js/terminal/lib/open.js b/static/js/terminal/lib/open.js deleted file mode 100644 index 1e0e4c1..0000000 --- a/static/js/terminal/lib/open.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -// if bold is broken, we can't -// use it in the terminal. -function isBoldBroken(){ - var el = document.createElement('span'); - - el.innerHTML = 'hello world'; - - document.body.appendChild(el); - - var w1 = el.scrollWidth; - - el.style.fontWeight = 'bold'; - - var w2 = el.scrollWidth; - - document.body.removeChild(el); - - return w1 !== w2; -} - -module.exports = function (Terminal){ - /** - * open - */ - Terminal.prototype.open = function (){ - var div; - var i = 0; - - this.screen = document.createElement('div'); - this.screen.className = 'ui-terminal'; - this.screen.style.outline = 'none'; - - this.screen.setAttribute('tabindex', '0'); - this.screen.setAttribute('spellcheck', 'false'); - - // sync default bg/fg colors - this.screen.style.backgroundColor = this.background; - this.screen.style.color = this.foreground; - - // Create the lines for our terminal. - this.children = []; - - for (; i < this.rows; i++) { - div = document.createElement('div'); - div.className = 'ui-terminal-row'; - - this.children.push(div); - this.screen.appendChild(div); - } - - // XXX - hack, move this somewhere else. - if (Terminal.brokenBold === null) { - Terminal.brokenBold = isBoldBroken(); - } - - this.refresh(0, this.rows - 1); - }; -}; diff --git a/static/js/terminal/lib/options.js b/static/js/terminal/lib/options.js deleted file mode 100644 index 0db661c..0000000 --- a/static/js/terminal/lib/options.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - Terminal.termName = 'xterm'; - Terminal.debug = false; - Terminal.geometry = [100, 80]; - Terminal.cursor = true; - Terminal.cursorBlink = true; - Terminal.cursorBlinkSpeed = 500; - Terminal.visualBell = true; - Terminal.popOnBell = true; - Terminal.scrollback = 640; - Terminal.screenKeys = false; - Terminal.programFeatures = false; - - // Terminal defaults - Terminal.defaults = { - debug: Terminal.debug, - termName: Terminal.termName, - cursor: Terminal.cursor, - cursorBlink: Terminal.cursorBlink, - cursorBlinkSpeed: Terminal.cursorBlinkSpeed, - visualBell: Terminal.visualBell, - popOnBell: Terminal.popOnBell, - scrollback: Terminal.scrollback, - screenKeys: Terminal.screenKeys, - programFeatures: Terminal.programFeatures - }; -}; diff --git a/static/js/terminal/lib/range.js b/static/js/terminal/lib/range.js deleted file mode 100644 index 3e8191e..0000000 --- a/static/js/terminal/lib/range.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * updateRange - * @param y - */ - Terminal.prototype.updateRange = function (y){ - if (y < this.refreshStart) this.refreshStart = y; - - if (y > this.refreshEnd) this.refreshEnd = y; - }; - - /** - * maxRange - */ - Terminal.prototype.maxRange = function (){ - this.refreshStart = 0; - this.refreshEnd = this.rows - 1; - }; -}; diff --git a/static/js/terminal/lib/refresh.js b/static/js/terminal/lib/refresh.js deleted file mode 100644 index 726923a..0000000 --- a/static/js/terminal/lib/refresh.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - // 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 - - /** - * refresh - * @param start - * @param end - */ - Terminal.prototype.refresh = function (start, end){ - var parent = this.screen ? this.screen.parentNode : null; - var optimize = parent && end - start >= this.rows / 2; - var x, y, i, line, out, ch, width, data, attr, fgColor, bgColor, flags, row; - - if (optimize) { - parent.removeChild(this.screen); - } - - width = this.cols; - y = start; - - if (end >= this.lines.length) { - end = this.lines.length - 1; - } - - for (; y <= end; y++) { - i = 0; - out = ''; - attr = this.defAttr; - row = y + this.ydisp; - line = this.lines[row]; - - if (y === this.y && this.cursor && this.cursorState && this.ydisp === this.ybase) { - x = this.x; - } else { - x = -1; - } - - 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 += ''; - } - - if (data !== this.defAttr) { - if (data === -1) { - out += ''; - } else { - out += ''; - } - } - } - - switch (ch) { - case '&': - out += '&'; - break; - case '<': - out += '<'; - break; - case '>': - out += '>'; - break; - default: - if (ch <= ' ') { - out += ' '; - } else { - if (this.isWide(ch)) i++; - - out += ch; - } - break; - } - - attr = data; - } - - if (attr !== this.defAttr) { - out += ''; - } - - this.children[y].innerHTML = out; - } - - if (optimize) { - parent.appendChild(this.screen); - } - }; -}; diff --git a/static/js/terminal/lib/resize.js b/static/js/terminal/lib/resize.js deleted file mode 100644 index 684466c..0000000 --- a/static/js/terminal/lib/resize.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Created by nuintun on 2015/11/25. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * resize - * @param x - * @param y - */ - Terminal.prototype.resize = function (x, y){ - var line, screen, i, j, ch; - - if (x < 1) x = 1; - - if (y < 1) y = 1; - - // resize cols - j = this.cols; - - if (j < x) { - // does xterm use the default attr - ch = [this.defAttr, ' ']; - i = this.lines.length; - - while (i--) { - while (this.lines[i].length < x) { - this.lines[i].push(ch); - } - } - } else if (j > x) { - i = this.lines.length; - - while (i--) { - while (this.lines[i].length > x) { - this.lines[i].pop(); - } - } - } - - this.setupStops(j); - - this.cols = x; - - // resize rows - j = this.rows; - - if (j < y) { - screen = this.screen; - - 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'); - - screen.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) { - screen = this.children.pop(); - - if (!screen) continue; - - screen.parentNode.removeChild(screen); - } - } - } - - 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; - }; -}; diff --git a/static/js/terminal/lib/scrollDisp.js b/static/js/terminal/lib/scrollDisp.js deleted file mode 100644 index 05f71f5..0000000 --- a/static/js/terminal/lib/scrollDisp.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * scroll - */ - 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.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); - }; - - /** - * scrollDisp - * @param disp - */ - 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); - }; -}; diff --git a/static/js/terminal/lib/setgCharset.js b/static/js/terminal/lib/setgCharset.js deleted file mode 100644 index bcea532..0000000 --- a/static/js/terminal/lib/setgCharset.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * setgCharset - * @param g - * @param charset - */ - Terminal.prototype.setgCharset = function (g, charset){ - this.charsets[g] = charset; - - if (this.glevel === g) { - this.charset = charset; - } - }; -}; diff --git a/static/js/terminal/lib/setgLevel.js b/static/js/terminal/lib/setgLevel.js deleted file mode 100644 index ee23e75..0000000 --- a/static/js/terminal/lib/setgLevel.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * setgLevel - * @param g - */ - Terminal.prototype.setgLevel = function (g){ - this.glevel = g; - this.charset = this.charsets[g]; - }; -}; diff --git a/static/js/terminal/lib/states.js b/static/js/terminal/lib/states.js deleted file mode 100644 index 58bba59..0000000 --- a/static/js/terminal/lib/states.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = { - normal: 0, - escaped: 1, - csi: 2, - osc: 3, - charset: 4, - dcs: 5, - ignore: 6, - udk: { type: 'udk' } -}; diff --git a/static/js/terminal/lib/stops.js b/static/js/terminal/lib/stops.js deleted file mode 100644 index 84dd453..0000000 --- a/static/js/terminal/lib/stops.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -// ignore warnings regarging == and != (coersion makes things work here appearently) -module.exports = function (Terminal){ - /** - * setupStops - * @param i - */ - Terminal.prototype.setupStops = function (i){ - if (arguments.length) { - if (!this.tabs[i]) { - i = this.prevStop(i); - } - } else { - i = 0; - this.tabs = {}; - } - - for (; i < this.cols; i += 8) { - this.tabs[i] = true; - } - }; - - /** - * prevStop - * @param x - * @returns {number} - */ - Terminal.prototype.prevStop = function (x){ - if (!arguments.length) x = this.x; - - while (!this.tabs[--x] && x > 0) {} - - return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; - }; - - /** - * nextStop - * @param x - * @returns {number} - */ - Terminal.prototype.nextStop = function (x){ - if (!arguments.length) x = this.x; - - while (!this.tabs[++x] && x < this.cols) {} - - return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; - }; -}; diff --git a/static/js/terminal/lib/util.js b/static/js/terminal/lib/util.js deleted file mode 100644 index 9745d25..0000000 --- a/static/js/terminal/lib/util.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -module.exports = function (Terminal){ - /** - * isWide - * @param ch - * @returns {boolean} - */ - Terminal.prototype.isWide = 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'); - }; - - /** - * ch - * @param cur - * @returns {string[]} - */ - Terminal.prototype.ch = function (cur){ - return cur ? [this.eraseAttr(), ' '] : [this.defAttr, ' ']; - }; - - /** - * is - * @param term - * @returns {boolean} - */ - Terminal.prototype.is = function (term){ - var name = this.termName; - - return (name + '').indexOf(term) === 0; - }; -}; diff --git a/static/js/terminal/lib/write.js b/static/js/terminal/lib/write.js deleted file mode 100644 index 7b71132..0000000 --- a/static/js/terminal/lib/write.js +++ /dev/null @@ -1,842 +0,0 @@ -/** - * Created by nuintun on 2015/11/24. - */ - -'use strict'; - -var states = require('./states'); - -module.exports = function (Terminal){ - /** - * send - * @param data - */ - Terminal.prototype.send = function (data){ - var context = this; - - if (!this.queue) { - setTimeout(function (){ - context.ondata.call(context, context.queue); - - context.queue = ''; - }, 1); - } - - this.queue += data; - }; - - /** - * bell - */ - Terminal.prototype.bell = function (){ - // buffers automatically when created - var snd = new Audio('bell.wav'); - - snd.play(); - - if (!this.visualBell) return; - - var context = this; - - this.screen.style.borderColor = 'white'; - - setTimeout(function (){ - context.screen.style.borderColor = ''; - }, 10); - - if (this.popOnBell) this.focus(); - }; - - /** - * write - * @param data - */ - Terminal.prototype.write = function (data){ - var l = data.length; - var i = 0; - var ch = null; - var cs, lch; - - this.refreshStart = this.y; - this.refreshEnd = this.y; - - if (this.ybase !== this.ydisp) { - this.ydisp = this.ybase; - - this.maxRange(); - } - - for (; i < l; i++, lch = ch) { - ch = data[i]; - - switch (this.state) { - case states.normal: - switch (ch) { - // '\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. - 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 = states.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 (this.isWide(ch)) { - var 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 states.escaped: - switch (ch) { - // ESC [ Control Sequence Introducer ( CSI is 0x9b). - case '[': - this.params = []; - this.currentParam = 0; - this.state = states.csi; - break; - // ESC ] Operating System Command ( OSC is 0x9d). - case ']': - this.params = []; - this.currentParam = 0; - this.state = states.osc; - break; - // ESC P Device Control String ( DCS is 0x90). - case 'P': - this.params = []; - this.prefix = ''; - this.currentParam = ''; - this.state = states.dcs; - break; - // ESC _ Application Program Command ( APC is 0x9f). - case '_': - this.state = states.ignore; - break; - // ESC ^ Privacy Message ( PM is 0x9e). - case '^': - this.state = states.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; - break; - 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.setgLevel(0); - this.setgCharset(0, Terminal.charsets.US); - - this.state = states.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 = states.charset; - break; - // Designate G3 Character Set (VT300). - // A = ISO Latin-1 Supplemental. - // Not implemented. - case '/': - this.gcharset = 3; - this.state = states.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 = states.normal; - break; - // ESC 8 Restore Cursor (DECRC). - case '8': - this.restoreCursor(); - - this.state = states.normal; - break; - // ESC # 3 DEC line height/width - case '#': - this.state = states.normal; - - i++; - break; - // ESC H Tab Set (HTS is 0x88). - case 'H': - this.tabSet(); - break; - // ESC = Application Keypad (DECPAM). - case '=': - this.applicationKeypad = true; - this.state = states.normal; - - this.log('Serial port requested application keypad.'); - break; - // ESC > Normal Keypad (DECPNM). - case '>': - this.applicationKeypad = false; - this.state = states.normal; - - this.log('Switching back to normal keypad.'); - break; - default: - this.state = states.normal; - - this.error('Unknown ESC control: %s.', ch); - break; - } - break; - case states.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 = states.normal; - break; - case states.osc: - // OSC Ps ; Pt ST - // OSC Ps ; Pt BEL - // Set Text Parameters. - if ((lch === '\x1b' && ch === '\\') || ch === '\x07') { - if (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.ontitle.call(this, 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 = states.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 states.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 = states.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': - this.charAttributes(this.params); - break; - // CSI Ps n Device Status Report (DSR). - case 'n': - 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.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; - case 'p': - switch (this.prefix) { - case '!': - this.softReset(this.params); - break; - } - break; - default: - this.error('Unknown CSI code: %s.', ch); - break; - } - - this.prefix = ''; - this.postfix = ''; - break; - case states.dcs: - if ((lch === '\x1b' && ch === '\\') || ch === '\x07') { - // Workarounds: - if (this.prefix === 'tmux;\x1b') { - // Tmux only accepts ST, not BEL: - if (ch === '\x07') { - this.currentParam += ch; - continue; - } - } - - if (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). - case states.udk: - break; - // Request Status String (DECRQSS). - // test: echo -e '\eP$q"p\e\\' - case '$q': - var valid = 0; - - switch (pt) { - // DECSCA - case '"q': - valid = 1; - pt = '0"q'; - break; - // DECSCL - case '"p': - valid = 1; - pt = '61"p'; - break; - // DECSTBM - case 'r': - valid = 1; - pt = '' + (this.scrollTop + 1) + ';' + (this.scrollBottom + 1) + 'r'; - break; - // SGR - case 'm': - // TODO: Parse this.curAttr here. - // Not implemented. - valid = 0; - break; - default: - this.error('Unknown DCS Pt: %s.', pt); - - valid = 0; - pt = ''; - break; - } - - this.send('\x1bP' + valid + '$r' + pt + '\x1b\\'); - break; - // Set Termcap/Terminfo Data (xterm, experimental). - case '+p': - 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': - 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': - break; - default: - this.error('Unknown DCS prefix: %s.', this.prefix); - break; - } - - this.currentParam = 0; - this.prefix = ''; - this.state = states.normal; - } else { - this.currentParam += ch; - - if (!this.prefix) { - if (/^\d*;\d*\|/.test(this.currentParam)) { - this.prefix = states.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 states.ignore: - // For PM and APC. - if ((lch === '\x1b' && ch === '\\') || ch === '\x07') { - this.state = states.normal; - } - break; - } - } - - this.updateRange(this.y); - this.refresh(this.refreshStart, this.refreshEnd); - }; - - Terminal.prototype.writeln = function (data){ - // adding empty char before line break ensures that empty lines render properly - this.write(data + ' \r\n'); - }; -}; diff --git a/static/js/terminal/xterm.js b/static/js/terminal/xterm.js deleted file mode 100644 index 180e47c..0000000 --- a/static/js/terminal/xterm.js +++ /dev/null @@ -1,2714 +0,0 @@ -/** - * Created by nuintun on 2015/11/30. - */ - -'use strict'; - -/** - * range function for numbers [a, .., b-1] - * - * @param {number} a - * @param {number} b - * @return {Array} - */ -function r(a, b){ - var c = b - a; - var arr = new Array(c); - - while (c--) { - arr[c] = --b; - } - - return arr; -} - -/** - * Add a transition to the transition table. - * - * @param table - table to add transition - * @param {number} inp - input character code - * @param {number} state - current state - * @param {number=} action - action to be taken - * @param {number=} next - next state - */ -function add(table, inp, state, action, next){ - table[state << 8 | inp] = ((action | 0) << 4) | ((next === undefined) ? state : next); -} - -/** - * Add multiple transitions to the transition table. - * - * @param table - table to add transition - * @param {Array} inps - array of input character codes - * @param {number} state - current state - * @param {number=} action - action to be taken - * @param {number=} next - next state - */ -function add_list(table, inps, state, action, next){ - for (var i = 0; i < inps.length; i++) - add(table, inps[i], state, action, next); -} - -/** global definition of printables and executables */ -var PRINTABLES = r(0x20, 0x7f); -var EXECUTABLES = r(0x00, 0x18); -EXECUTABLES.push(0x19); -EXECUTABLES.concat(r(0x1c, 0x20)); - -/** - * create the standard transition table - used by all parser instances - * - * table[state << 8 | character code] = action << 4 | next state - * - * - states are numbers from 0 to 13 - * - control character codes defined from 0 to 159 (C0 and C1) - * - actions are numbers from 0 to 14 - * - any higher character than 159 is handled by the 'error' action - * - * state replacements (14 states): - * 'GROUND' -> 0 - * 'ESCAPE' -> 1 - * 'ESCAPE_INTERMEDIATE' -> 2 - * 'CSI_ENTRY' -> 3 - * 'CSI_PARAM' -> 4 - * 'CSI_INTERMEDIATE' -> 5 - * 'CSI_IGNORE' -> 6 - * 'SOS_PM_APC_STRING' -> 7 - * 'OSC_STRING' -> 8 - * 'DCS_ENTRY' -> 9 - * 'DCS_PARAM' -> 10 - * 'DCS_IGNORE' -> 11 - * 'DCS_INTERMEDIATE' -> 12 - * 'DCS_PASSTHROUGH' -> 13 - * - * action replacements (15 actions): - * 'no action' -> 0 - * 'error' -> 1 - * 'print' -> 2 - * 'execute' -> 3 - * 'osc_start' -> 4 - * 'osc_put' -> 5 - * 'osc_end' -> 6 - * 'csi_dispatch' -> 7 - * 'param' -> 8 - * 'collect' -> 9 - * 'esc_dispatch' -> 10 - * 'clear' -> 11 - * 'dcs_hook' -> 12 - * 'dcs_put' -> 13 - * 'dcs_unhook' -> 14 - */ -var TRANSITION_TABLE = (function (){ - var table = new Uint8Array(4095); - - // table with default transition [any] --> [error, GROUND] - for (var state = 0; state < 14; ++state) { - for (var code = 0; code < 160; ++code) { - table[state << 8 | code] = 16; - } - } - - // apply transitions - // printables - add_list(table, PRINTABLES, 0, 2); - - // global anywhere rules - for (state = 0; state < 14; ++state) { - add_list(table, [0x18, 0x1a, 0x99, 0x9a], state, 3, 0); - add_list(table, r(0x80, 0x90), state, 3, 0); - add_list(table, r(0x90, 0x98), state, 3, 0); - add(table, 0x9c, state, 0, 0); // ST as terminator - add(table, 0x1b, state, 11, 1); // ESC - add(table, 0x9d, state, 4, 8); // OSC - add_list(table, [0x98, 0x9e, 0x9f], state, 0, 7); - add(table, 0x9b, state, 11, 3); // CSI - add(table, 0x90, state, 11, 9); // DCS - } - - // rules for executables and 7f - add_list(table, EXECUTABLES, 0, 3); - add_list(table, EXECUTABLES, 1, 3); - add(table, 0x7f, 1); - add_list(table, EXECUTABLES, 8); - add_list(table, EXECUTABLES, 3, 3); - add(table, 0x7f, 3); - add_list(table, EXECUTABLES, 4, 3); - add(table, 0x7f, 4); - add_list(table, EXECUTABLES, 6, 3); - add_list(table, EXECUTABLES, 5, 3); - add(table, 0x7f, 5); - add_list(table, EXECUTABLES, 2, 3); - add(table, 0x7f, 2); - // osc - add(table, 0x5d, 1, 4, 8); - add_list(table, PRINTABLES, 8, 5); - add(table, 0x7f, 8, 5); - add_list(table, [0x9c, 0x1b, 0x18, 0x1a, 0x07], 8, 6, 0); - add_list(table, r(0x1c, 0x20), 8, 0); - // sos/pm/apc does nothing - add_list(table, [0x58, 0x5e, 0x5f], 1, 0, 7); - add_list(table, PRINTABLES, 7); - add_list(table, EXECUTABLES, 7); - add(table, 0x9c, 7, 0, 0); - // csi entries - add(table, 0x5b, 1, 11, 3); - add_list(table, r(0x40, 0x7f), 3, 7, 0); - add_list(table, r(0x30, 0x3a), 3, 8, 4); - add(table, 0x3b, 3, 8, 4); - add_list(table, [0x3c, 0x3d, 0x3e, 0x3f], 3, 9, 4); - add_list(table, r(0x30, 0x3a), 4, 8); - add(table, 0x3b, 4, 8); - add_list(table, r(0x40, 0x7f), 4, 7, 0); - add_list(table, [0x3a, 0x3c, 0x3d, 0x3e, 0x3f], 4, 0, 6); - add_list(table, r(0x20, 0x40), 6); - add(table, 0x7f, 6); - add_list(table, r(0x40, 0x7f), 6, 0, 0); - add(table, 0x3a, 3, 0, 6); - add_list(table, r(0x20, 0x30), 3, 9, 5); - add_list(table, r(0x20, 0x30), 5, 9); - add_list(table, r(0x30, 0x40), 5, 0, 6); - add_list(table, r(0x40, 0x7f), 5, 7, 0); - add_list(table, r(0x20, 0x30), 4, 9, 5); - // esc_intermediate - add_list(table, r(0x20, 0x30), 1, 9, 2); - add_list(table, r(0x20, 0x30), 2, 9); - add_list(table, r(0x30, 0x7f), 2, 10, 0); - add_list(table, r(0x30, 0x50), 1, 10, 0); - add_list(table, [0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5a, 0x5c], 1, 10, 0); - add_list(table, r(0x60, 0x7f), 1, 10, 0); - // dcs entry - add(table, 0x50, 1, 11, 9); - add_list(table, EXECUTABLES, 9); - add(table, 0x7f, 9); - add_list(table, r(0x1c, 0x20), 9); - add_list(table, r(0x20, 0x30), 9, 9, 12); - add(table, 0x3a, 9, 0, 11); - add_list(table, r(0x30, 0x3a), 9, 8, 10); - add(table, 0x3b, 9, 8, 10); - add_list(table, [0x3c, 0x3d, 0x3e, 0x3f], 9, 9, 10); - add_list(table, EXECUTABLES, 11); - add_list(table, r(0x20, 0x80), 11); - add_list(table, r(0x1c, 0x20), 11); - add_list(table, EXECUTABLES, 10); - add(table, 0x7f, 10); - add_list(table, r(0x1c, 0x20), 10); - add_list(table, r(0x30, 0x3a), 10, 8); - add(table, 0x3b, 10, 8); - add_list(table, [0x3a, 0x3c, 0x3d, 0x3e, 0x3f], 10, 0, 11); - add_list(table, r(0x20, 0x30), 10, 9, 12); - add_list(table, EXECUTABLES, 12); - add(table, 0x7f, 12); - add_list(table, r(0x1c, 0x20), 12); - add_list(table, r(0x20, 0x30), 12, 9); - add_list(table, r(0x30, 0x40), 12, 0, 11); - add_list(table, r(0x40, 0x7f), 12, 12, 13); - add_list(table, r(0x40, 0x7f), 10, 12, 13); - add_list(table, r(0x40, 0x7f), 9, 12, 13); - add_list(table, EXECUTABLES, 13, 13); - add_list(table, PRINTABLES, 13, 13); - add(table, 0x7f, 13); - add_list(table, [0x1b, 0x9c], 13, 14, 0); - - return table; -})(); - -/** - * helper for param conversion - * @param {string} params - params string with ; - * @return {Array} - */ -function parse_params(params){ - // params are separated by ';' - // empty defaults to 0 - var p = params.split(';'); - - for (var i = 0; i < p.length; ++i) { - p[i] = Number(p[i]); - } - - return p; -} - -/** - * AnsiParser - Parser for ANSI terminal escape sequences. - * - * @param {Object=} terminal - * @constructor - */ -function AnsiParser(terminal){ - this.initial_state = 0; // 'GROUND' is default - this.current_state = this.initial_state; - - // global non pushable buffers for multiple parse invocations - this.osc = ''; - this.params = ''; - this.collected = ''; - - // back reference to terminal - this.term = terminal || {}; - var instructions = [ - 'inst_p', 'inst_o', 'inst_x', 'inst_c', - 'inst_e', 'inst_H', 'inst_P', 'inst_U', 'inst_E' - ]; - - for (var i = 0; i < instructions.length; ++i) { - if (!(instructions[i] in this.term)) { - this.term[instructions[i]] = function (){}; - } - } -} - -/** - * Reset the parser. - */ -AnsiParser.prototype.reset = function (){ - this.current_state = this.initial_state; - this.osc = ''; - this.params = ''; - this.collected = ''; -}; - -/** - * Parse string s. - * @param {string} s - */ -AnsiParser.prototype.parse = function (s){ - var error = false; - var c, code, transition; - var current_state = this.current_state; - - // local buffers - var printed = ''; - var dcs = ''; - var osc = this.osc; - var collected = this.collected; - var params = this.params; - - // process input string - for (var i = 0; i < s.length; ++i) { - c = s.charAt(i); - code = c.charCodeAt(0); - - if (code < 0xa0) { - transition = TRANSITION_TABLE[current_state << 8 | code]; - } else { - transition = 16; - } - - switch (transition >> 4) { - case 0: - // no action - break; - case 1: - // error - // NOTE: real error recovery is not implemented - // handle unicode chars in write buffers w'o state change - if (code > 0x9f) { - switch (current_state) { - case 0: - // GROUND -> add char to print string - printed += c; - break; - case 8: - // OSC_STRING -> add char to osc string - osc += c; - transition |= 8; - break; - case 6: - // CSI_IGNORE -> ignore char - transition |= 6; - break; - case 11: - // DCS_IGNORE -> ignore char - transition |= 11; - break; - case 13: - // DCS_PASSTHROUGH -> add char to dcs - dcs += c; - transition |= 13; - break; - default: - error = true; - } - } else { - error = true; - } - - if (error) { - if ( - this.term.inst_E({ - pos: i, // position in parse string - character: c, // wrong character - state: current_state, // in state - print: printed, // print buffer - dcs: dcs, // dcs buffer - osc: osc, // osc buffer - collect: collected, // collect buffer - params: params // params buffer - }) - ) { - return; - } - error = false; - } - break; - case 2: - // print - printed += c; - break; - case 3: - // execute - if (printed) { - this.term.inst_p(printed); - } - - printed = ''; - - this.term.inst_x(c); - break; - case 7: - // csi_dispatch - this.term.inst_c(collected, parse_params(params), c); - break; - case 8: - // param - params += c; - break; - case 9: - // collect - collected += c; - break; - case 10: - // esc_dispatch - this.term.inst_e(collected, c); - break; - case 11: - // clear - if (printed) { - this.term.inst_p(printed); - } - - printed = ''; - osc = ''; - params = ''; - collected = ''; - dcs = ''; - break; - case 4: - // osc_start - if (printed) { - this.term.inst_p(printed); - } - - printed = ''; - osc = ''; - break; - case 5: - // osc_put - osc += c; - break; - case 6: - // osc_end - if (osc && code !== 0x18 && code !== 0x1a) { - this.term.inst_o(osc); - } - - if (code === 0x1b) { - transition |= 1; - } - - osc = ''; - params = ''; - collected = ''; - dcs = ''; - break; - // dcs_hook - case 12: - this.term.inst_H(collected, parse_params(params), c); - break; - // dcs_put - case 13: - dcs += c; - break; - // dcs_unhook - case 14: - if (dcs) { - this.term.inst_P(dcs); - } - - this.term.inst_U(); - - if (code === 0x1b) { - transition |= 1; - } - - osc = ''; - params = ''; - collected = ''; - dcs = ''; - break; - } - - current_state = transition & 15; - } - - // push leftover pushable buffers to terminal - if (!current_state && printed) { - this.term.inst_p(printed); - } else if (current_state === 13 && dcs) { - this.term.inst_P(dcs); - } - - // save non pushable buffers - this.osc = osc; - this.collected = collected; - this.params = params; - - // save state - this.current_state = current_state; -}; - -/** - * AnsiTerminal - an offscreen xterm like terminal. - * - * TODO: - * - unicode tests - * - move box printing chars to frontend - * - create output methods for TChar and AnsiTerminal - * - bracketed paste mode - * - tabs, tab stops, tab width, tab output - * - tons of DCS codes - * - advanced tests, vttest - * - mouse handling goes here (via registering handler callbacks) - * - test cases - */ - -/** - * wcswidth - * - * taken from: - * - http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - * - wcwidth node module - */ -var _WIDTH_COMBINING = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] -]; - -function _width_bisearch(ucs){ - var mid; - var min = 0; - var max = _WIDTH_COMBINING.length - 1; - - if (ucs < _WIDTH_COMBINING[0][0] || ucs > _WIDTH_COMBINING[max][1]) { - return false; - } - - while (max >= min) { - mid = Math.floor((min + max) / 2); - - if (ucs > _WIDTH_COMBINING[mid][1]) { - min = mid + 1; - } else if (ucs < _WIDTH_COMBINING[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - - return false; -} - -function _width_wcwidth(ucs, opts){ - // test for 8-bit control characters - if (ucs === 0) { - return opts.nul; - } - - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { - return opts.control; - } - - // binary search in table of non-spacing characters - if (_width_bisearch(ucs)) { - return 0; - } - - // if we arrive here, ucs is not a combining or C0/C1 control character - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || // Hangul Jamo init. consonants - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || // CJK ... Yi - (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables - (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compatibility Ideographs - (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms - (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compatibility Forms - (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))); -} - -var _WIDTH_DEFAULTS = { nul: 0, control: 0 }; - -// wcswidth(string[, opts]) - number of taken terminal cells by a string (printed space) -function wcswidth(str, opts){ - // FIXME - opts = _WIDTH_DEFAULTS; - - if (typeof str !== 'string') { - return _width_wcwidth(str, opts); - } - - var s = 0; - - for (var i = 0; i < str.length; i++) { - var n = _width_wcwidth(str.charCodeAt(i), opts); - - if (n < 0) { - return -1; - } - - s += n; - } - - return s; -} - -/** - * TChar - terminal character with attributes. - * - * Bits of text attr: - * 1-8 BG / BG red - * 9-16 FG / FG red - * 17 bold - * 18 italic - * 19 underline - * 20 blink - * 21 inverse - * 22 conceal - * 23 cursor - * 24 - * 25 BG set - * 26 BG RGB mode - * 27 FG set - * 28 FG RGB mode - * 29-32 - * - * Bits of gb: - * 1-8 BG blue - * 9-16 FG blue - * 17-24 BG green - * 25-32 FG green - * - * @param {string} value - An unicode character (multiple if surrogate or combining). - * @param {number} [attr] - Cell attributes as integer. - * @param {number} [gb] - Green and blue part of RGB as integer. - * @param {number} [width] - Terminal cells taken by this character. - * @constructor - */ -function TChar(value, attr, gb, width){ - this.value = value; - this.attr = attr | 0; - this.gb = gb | 0; - this.width = (width === undefined) ? 1 : width; -} - -TChar.prototype.clone = function (){ - return new TChar(this.value, this.attr, this.gb, this.width); -}; - -/** - * getAttributes - * @return {object} Object with attributes in a readable manner. - */ -TChar.prototype.getAttributes = function (){ - var colorbits = this.attr >>> 24; - var r = this.attr & 65535; - var g = this.gb >>> 16; - var b = this.gb & 65535; - var bits = this.attr >>> 16 & 255; - - return { - bold: !!(bits & 1), - italic: !!(bits & 2), - underline: !!(bits & 4), - blink: !!(bits & 8), - inverse: !!(bits & 16), - conceal: !!(bits & 32), - // TODO cursor - // cursor: !!(bits & 64), - foreground: { - set: !!(colorbits & 4), - RGB: !!(colorbits & 8), - color: [r >>> 8, g >>> 8, b >>> 8] - }, - background: { - set: !!(colorbits & 1), - RGB: !!(colorbits & 2), - color: [r & 255, g & 255, b & 255] - } - } -}; - -/** - * setAttributes - * @param attributes - */ -TChar.prototype.setAttributes = function (attributes){ - var attr = this.attr; - - ['bold', 'italic', 'underline', 'blink', 'inverse', 'conceal'].map(function (key, i){ - attr = attributes[key] ? attr | (2 << (15 + i)) : attr & ~(2 << (15 + i)); - }); - - if (attributes['foreground']) { - var foreground = attributes['foreground']; - - attr = foreground['set'] ? attr | (2 << 25) : attr & ~(2 << 25); - attr = foreground['RGB'] ? attr | (2 << 26) : attr & ~(2 << 26); - - if (Array.isArray(foreground['color'])) { - attr = (attr & ~65280) | (foreground['color'][0] << 8); - this.gb = (this.gb & ~4278190080) | (foreground['color'][1] << 24); - this.gb = (this.gb & ~65280) | (foreground['color'][2] << 8); - } - } - - if (attributes['background']) { - var background = attributes['background']; - - attr = background['set'] ? attr | (2 << 23) : attr & ~(2 << 23); - attr = background['RGB'] ? attr | (2 << 24) : attr & ~(2 << 24); - - if (Array.isArray(background['color'])) { - attr = (attr & ~255) | (background['color'][0]); - this.gb = (this.gb & ~16711680) | (background['color'][1] << 16); - this.gb = (this.gb & ~255) | background['color'][2]; - } - } - - this.attr = attr; -}; - -TChar.prototype.toString = function (){ - return this.value; -}; - -var _uniqueId = 0; - -/** - * Row - * @constructor - */ -function Row(){ - this.uniqueId = _uniqueId++ | 0; - this.version = 0; - this.cells = []; -} - -/** - * ScreenBuffer - represents a terminal screen with cols and rows. - * - * .buffer is an array of length rows and contains the row objects. - * - * @param cols - * @param rows - * @param scrollLength - * @constructor - */ -function ScreenBuffer(cols, rows, scrollLength){ - this.rows = rows; - this.cols = cols; - this.scrollLength = scrollLength | 0; - - this.buffer = []; - this.scrollbuffer = []; - - this.reset(); -} - -ScreenBuffer.prototype.reset = function (){ - this.buffer = []; - this.scrollbuffer = []; - - var row; - - for (var i = 0; i < this.rows; ++i) { - row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('')); - } - - this.buffer.push(row); - } -}; - -ScreenBuffer.prototype.appendToScrollBuffer = function (row){ - this.scrollbuffer.push(row); - - while (this.scrollbuffer.length > this.scrollLength) { - this.scrollbuffer.shift(); - } -}; - -ScreenBuffer.prototype.fetchFromScrollBuffer = function (){ - return this.scrollbuffer.pop(); -}; - -ScreenBuffer.prototype.resize = function (cols, rows, cursor){ - // xterm behavior - shrink: - // delete higher rows til cursor then lowest to scrollbuffer - // xterm behavior - enlarge: - // fill lowest from scrollbuffer then append new at end - - // assume xterm handles alternate buffer the same way - // in respect of the cursor position but w/o scrolling - - // shrink height - if (rows < this.rows) { - while (this.buffer.length > rows) { - if (this.buffer.length > cursor.row + 1) { - this.buffer.pop(); - } else { - this.appendToScrollBuffer(this.buffer.shift()); - cursor.row -= 1; - } - } - } - - // enlarge height - if (rows > this.rows) { - while (this.buffer.length < rows) { - var row = this.fetchFromScrollBuffer(); - - if (row) { - this.buffer.unshift(row); - cursor.row += 1; - } else { - row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('')); - } - - this.buffer.push(row); - } - } - } - - if (cursor.row >= rows) { - cursor.row = rows - 1; - } - - var i; - - // shrink width - if (cols < this.cols) { - for (i = 0; i < this.buffer.length; ++i) { - var remove = this.cols - cols; - - do { - this.buffer[i].cells.pop(); - } while (--remove); - - this.buffer[i].version++; - } - - for (i = 0; i < this.scrollbuffer.length; ++i) { - remove = this.cols - cols; - - do { - this.scrollbuffer[i].cells.pop(); - } while (--remove); - - this.scrollbuffer[i].version++; - } - } - - // enlarge width - if (cols > this.cols) { - for (i = 0; i < this.buffer.length; ++i) { - var append = cols - this.cols; - - do { - this.buffer[i].cells.push(new TChar('')); - } while (--append); - - this.buffer[i].version++; - } - - for (i = 0; i < this.scrollbuffer.length; ++i) { - append = cols - this.cols; - - do { - this.scrollbuffer[i].cells.push(new TChar('')); - } while (--append); - - this.scrollbuffer[i].version++; - } - } - - if (cursor.col >= cols) - cursor.col = cols - 1; - - this.rows = rows; - this.cols = cols; -}; - -/** minimal support for switching charsets (only basic drawing symbols supported) */ -var CHARSET_0 = { - '`': '◆', 'a': '▒', 'b': '␉', 'c': '␌', 'd': '␍', - 'e': '␊', 'f': '°', 'g': '±', 'h': '␤', 'i': '␋', - 'j': '┘', 'k': '┐', 'l': '┌', 'm': '└', 'n': '┼', - 'o': '⎺', 'p': '⎻', 'q': '─', 'r': '⎼', 's': '⎽', - 't': '├', 'u': '┤', 'v': '┴', 'w': '┬', 'x': '│', - 'y': '≤', 'z': '≥', '{': 'π', '|': '≠', '}': '£', '~': '°' -}; - -/** fix: box drawing bold */ -// FIXME: should this go into frontend? -// since most monospace fonts can't handle bold on these right we have to -// switch to to corrensponding unicode character -var BOXSYMBOLS_BOLD = { - '─': '━', '│': '┃', '┄': '┅', '┆': '┇', '┈': '┉', '┊': '┋', - '┌': '┏', '┐': '┓', '└': '┗', '┘': '┛', '├': '┣', '┤': '┫', - '┬': '┳', '┴': '┻', '┼': '╋', '╌': '╍', '╎': '╏' -}; - -var TERM_STRING = { - CSI: '\u001b[', SS3: '\u001bO', DCS: '\u001bP', ST: '\u001b\\', - OSC: '\u001b]', PM: '\u001b^', APC: '\u001b_' -}; - -/** - * AnsiTerminal - an offscreen terminal. - * - * @param {number} cols - columns of the terminal. - * @param {number} rows - rows of the terminal. - * @param {number} scrollLength - lines of scrollbuffer. - * @constructor - */ -function AnsiTerminal(cols, rows, scrollLength){ - if (!(this instanceof AnsiTerminal)) { - return new AnsiTerminal(cols, rows, scrollLength); - } - - this.rows = rows; - this.cols = cols; - this.scrollLength = scrollLength | 0; - // callback for writing back to stream - this.send = function (data){}; - // callback for sending console beep - this.beep = function (tone, duration){}; - // announce changes in mouse handling - this.changedMouseHandling = function (mode, protocol){}; - - // init ansi parser - this.AnsiParser = new AnsiParser(this); - - this.reset(); -} - -/** - * write - * @param data - */ -AnsiTerminal.prototype.write = function (data){ - this.AnsiParser.parse(data); -}; - -/** Hard reset of the terminal. */ -AnsiTerminal.prototype.reset = function (){ - this.normal_screen = new ScreenBuffer(this.cols, this.rows, this.scrollLength); - this.alternate_screen = new ScreenBuffer(this.cols, this.rows, 0); - this.screen = this.normal_screen; - this.normal_cursor = { col: 0, row: 0 }; - this.alternate_cursor = { col: 0, row: 0 }; - this.cursor = this.normal_cursor; - this.charset = null; - this.textattributes = 0; - this.colors = 0; - this.charattributes = 0; - this.reverse_video = false; - - this.cursor_key_mode = false; - this.show_cursor = true; - // terminal title set by OSR - this.title = ''; - this.cursor_save = null; - // IRM (default replace) - this.insert_mode = false; - // CSI?12l - this.blinking_cursor = false; - // for DECSTBM - this.scrolling_top = 0; - // for DECSTBM - this.scrolling_bottom = this.rows; - // DECAWM - this.autowrap = true; - // LNM - this.newline_mode = false; - this.tab_width = 8; - // for REP - this.last_char = ''; - // tracking modes for mouse 0=off, (9, 1000, 1001, 1002, 1003) - this.mouse_mode = 0; - // 0 (normal), 1005 (utf-8), 1006 (sgr), 1015 (decimal) - this.mouse_protocol = 0; - - // mouse events - this.mouseDown = { 1: false, 2: false, 3: false, 4: false }; - // unicode and fullwidth support - // remainder of 0 width char (combining characters) - this._rem_g = ''; - // remainder of surrogates - this._rem_c = ''; - - // new wrapping behavior - this.wrap = false; - this.row_wrap = false; -}; - -/** - * toSting - * @param [type] - * @returns {string} representation of active buffer. - */ -AnsiTerminal.prototype.toString = function (type){ - var i, j; - var s = ''; - - if (type === 'html') { - var node; - - for (i = 0; i < this.screen.buffer.length; ++i) { - for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { - node = this.screen.buffer[i].cells[j]; - - if (node.value) { - console.log(node.value, ': ', styles(node)); - } - } - } - } else { - for (i = 0; i < this.screen.buffer.length; ++i) { - // FIXME: quick and dirty fill up from left - var last_nonspace = 0; - - for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { - if (this.screen.buffer[i].cells[j].value) { - last_nonspace = j; - } - } - - for (j = 0; j < this.screen.buffer[i].cells.length; ++j) { - s += (last_nonspace > j) - ? (this.screen.buffer[i].cells[j].value || ' ') - : this.screen.buffer[i].cells[j].value; - } - - s += '\n'; - } - } - - return s; -}; - -/** - * Resize terminal to cols x rows. - * - * @param cols - * @param rows - */ -AnsiTerminal.prototype.resize = function (cols, rows){ - // skip insane values - if ((cols < 2) || (rows < 2)) { - return false; - } - - // normal scroll buffer - //this._resize(cols, rows, this.normal_buffer, this.normal_cursor, true); - this.normal_screen.resize(cols, rows, this.normal_cursor); - - // alternative buffer - //this._resize(cols, rows, this.alternate_buffer, this.alternate_cursor, false); - this.alternate_screen.resize(cols, rows, this.alternate_cursor); - - // set new rows / cols to terminal - this.rows = rows; - this.cols = cols; - // FIXME: how to deal with scrolling area? - simply reset for now - this.scrolling_top = 0; - this.scrolling_bottom = this.rows; - - // if cursor got saved before we need to overwrite the saved values - if (this.cursor_save) { - this.DECSC(); - } -}; - -/** - * Propagate mouse action to terminal emulator. - * - * @param {string} type - type of action ('mousedown', 'mouseup', 'mousemove' or 'wheel') - * @param {number} button - button number - * @param {number} col - column the mouse action took place. - * @param {number} row - row the mouse action took place. - */ -AnsiTerminal.prototype.mouseAction = function (type, button, col, row){ - if (!this.mouse_mode) { - return; - } - - if (this.mouse_mode === 9 && (type !== 'mousedown' || type !== 'wheel')) { - return; - } - - if (this.mouse_mode === 1000 && type === 'mousemove') { - return; - } - - if (this.mouse_mode === 1002 && type === 'mousemove' && !this.mouseDown[button]) { - return; - } - - // if we made it this far we got a legal mouse action and process it further - // special state switch for mousemove after mousedown in 1002 - if (this.mouse_mode === 1002) { - if (type == 'mousedown') { - this.mouseDown[button] = true; - } else if (type == 'mouseup') { - this.mouseDown[button] = false; - } - } - - switch (this.mouse_protocol) { - case 0: - break; - case 1005: - break; - case 1006: - break; - case 1015: - break; - default: - console.log('mouse protocol' + this.mouse_protocol + 'not implemented'); - } -}; - -/** - * Implementation of the parser instructions - */ - -/** - * inst_p - handle printable character. - * - * @param {string} s - */ -AnsiTerminal.prototype.inst_p = function (s){ - if (this.debug) { - console.log('inst_p', s); - } - - var c = ''; - var code = 0; - var low = 0; - var width = 1; - - // add leftover surrogate high - if (this._rem_c) { - s += this._rem_c; - this._rem_c = ''; - } - - for (var i = 0; i < s.length; ++i) { - c = s.charAt(i); - code = s.charCodeAt(i); - - // surrogate high - if (0xD800 <= code && code <= 0xDBFF) { - low = s.charCodeAt(i + 1); - - if (low !== low) { - this._rem_c = c; - - return; - } - - code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; - c += s.charAt(i + 1); - } - - // surrogate low - already handled - if (0xDC00 <= code && code <= 0xDFFF) { - continue; - } - - width = wcswidth(code); - - if (width == 2 && (width + this.cursor.col) > this.cols) { - if (this.autowrap) { - this.wrap = true; - } else { - continue; - } - } - - if (this.wrap && width) { - this.cursor.col = 0; - this.cursor.row++; - this.wrap = false; - } - - if (this.cursor.row >= this.scrolling_bottom) { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom, 0, row); - - var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; - - if (!this.scrolling_top) { - this.screen.appendToScrollBuffer(scrolled_out); - } - - this.cursor.row--; - } - - this.screen.buffer[this.cursor.row].version++; - - // combining characters - if (!width && this.cursor.col) { - this.screen.buffer[this.cursor.row].cells[this.cursor.col - 1].value += c; - } else { - c = (this.charset) ? (this.charset[c] || c) : c; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = c; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = this.charattributes; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].gb = this.colors; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].width = width; - - // fix box drawing -- this is a really ugly problem - FIXME: goes into frontend - if (c >= '\u2500' && c <= '\u2547') { - if (this.textattributes && (this.textattributes & 65536)) { - this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = BOXSYMBOLS_BOLD[c] || c; - - // unset bold here, but set intense instead if applicable - var attr = this.charattributes & ~65536; - - if (attr & 67108864 && !(attr & 134217728) && (attr >>> 8 & 255) < 8) { - attr |= 2048; - } - - this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = attr; - } - } - - this.cursor.col += 1; - } - - if (width === 2) { - this.screen.buffer[this.cursor.row].cells[this.cursor.col].width = 0; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].value = ''; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].attr = this.charattributes; - this.screen.buffer[this.cursor.row].cells[this.cursor.col].gb = this.colors; - this.cursor.col += 1; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 2; - - if (this.autowrap) { - this.wrap = true; - } - } - } - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - - if (this.autowrap) { - this.wrap = true; - } - } - } -}; - -AnsiTerminal.prototype.inst_o = function (s){ - if (this.debug) { - console.log('inst_o', s); - } - - this.last_char = ''; - this.wrap = false; - - if (s.charAt(0) == '0') { - this.title = s.slice(2); - } else { - console.log('inst_o unhandled:', s); - } -}; - -AnsiTerminal.prototype.inst_x = function (flag){ - if (this.debug) { - console.log('inst_x', flag.charCodeAt(0), flag); - } - - this.last_char = ''; - this.wrap = false; - - switch (flag) { - case '\n': - this.cursor.row++; - - if (this.cursor.row >= this.scrolling_bottom) { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom, 0, row); - - var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; - - if (!this.scrolling_top) { - this.screen.appendToScrollBuffer(scrolled_out); - } - - this.cursor.row--; - } - - if (this.newline_mode) { - this.cursor.col = 0; - } - - if (this.cursor.col >= this.cols) { - this.cursor.col--; - } - break; - case '\r': - this.cursor.col = 0; - break; - case '\t': - this.CHT([0]); - break; - case '\x07': - this.beep(); - break; - case '\x08': - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } - - this.cursor.col -= 1; - - if (this.cursor.col < 0) { - this.cursor.col = 0; - } - break; - case '\x0b': - this.inst_x('\n'); - break; - case '\x0c': - this.inst_x('\n'); - break; - // activate G1 - case '\x0e': - this.charset = CHARSET_0; - break; - // activate G0 FIXME - case '\x0f': - this.charset = null; - break; - // TODO - case '\x11': - console.log('unhandled DC1 (XON)'); - break; - // DC2 - case '\x12': - break; - // TODO - case '\x13': - console.log('unhandled DC3 (XOFF)'); - break; - // DC4 - case '\x14': - break; - default: - console.log('inst_x unhandled:', flag.charCodeAt(0)); - } -}; - -/** - * missing (from xterm) - * - * CSI Ps g Tab Clear (TBC). - * (more to come...) - * - */ -AnsiTerminal.prototype.inst_c = function (collected, params, flag){ - if (this.debug) { - console.log('inst_c', collected, params, flag); - } - - // hack for getting REP working - if (flag != 'b') { - this.last_char = ''; - } - - this.wrap = false; - - switch (collected) { - case '': - switch (flag) { - case '@': - return this.ICH(params); - case 'E': - return this.CNL(params); - case 'F': - return this.CPL(params); - case 'G': - return this.CHA(params); - case 'D': - return this.CUB(params); - case 'B': - return this.CUD(params); - case 'C': - return this.CUF(params); - case 'A': - return this.CUU(params); - case 'I': - return this.CHT(params); - case 'Z': - return this.CBT(params); - case 'f': - case 'H': - return this.CUP(params); - case 'P': - return this.DCH(params); - case 'J': - return this.ED(params); - case 'K': - return this.EL(params); - case 'L': - return this.IL(params); - case 'M': - return this.DL(params); - case 'S': - return this.SU(params); - case 'T': - return this.SD(params); - case 'X': - return this.ECH(params); - case 'a': - return this.HPR(params); - case 'b': - return this.REP(params); - case 'e': - return this.VPR(params); - case 'd': - return this.VPA(params); - // DA1 - case 'c': - return this.send(TERM_STRING['CSI'] + '?64;1;2;6;9;15;18;21;22c'); - case 'h': - return this.high(collected, params); - case 'l': - return this.low(collected, params); - case 'm': - return this.SGR(params); - case 'n': - return this.DSR(collected, params); - case 'r': - return this.DECSTBM(params); - case '`': - return this.HPA(params); - default : - console.log('inst_c unhandled:', collected, params, flag); - } - break; - case '?': - switch (flag) { - // DECSED as normal ED - case 'J': - return this.ED(params); - // DECSEL as normal EL - case 'K': - return this.EL(params); - case 'h': - return this.high(collected, params); - case 'l': - return this.low(collected, params); - case 'n': - return this.DSR(collected, params); - default : - console.log('inst_c unhandled:', collected, params, flag); - } - break; - case '>': - switch (flag) { - // DA2 - case 'c': - return this.send(TERM_STRING['CSI'] + '>41;1;0c'); - default : - console.log('inst_c unhandled:', collected, params, flag); - } - break; - case '!': - switch (flag) { - case 'p': - return this.DECSTR(); - default : - console.log('inst_c unhandled:', collected, params, flag); - } - break; - default : - console.log('inst_c unhandled:', collected, params, flag); - } -}; - -AnsiTerminal.prototype.inst_e = function (collected, flag){ - if (this.debug) { - console.log('inst_e', collected, flag); - } - - this.last_char = ''; - this.wrap = false; - - switch (flag) { - // complete ESC codes from xterm: - // ESC H Tab Set ( HTS is 0x88). // TODO - // ESC N Single Shift Select of G2 Character Set ( SS2 is 0x8e). This affects next character only. - // ESC O Single Shift Select of G3 Character Set ( SS3 is 0x8f). This affects next character only. - // ESC P Device Control String ( DCS is 0x90). - // ESC V Start of Guarded Area ( SPA is 0x96). - // ESC W End of Guarded Area ( EPA is 0x97). - // ESC X Start of String ( SOS is 0x98). - // ESC Z Return Terminal ID (DECID is 0x9a). Obsolete form of CSI c (DA). - // case 'F': // (SP) 7-bit controls (S7C1T) - not supported - // case 'G': // (SP) 8-bit controls (S8C1T) - not supported - // case 'L': // (SP) Set ANSI conformance level 1 (dpANS X3.134.1) - not supported - // case 'M': // (SP) Set ANSI conformance level 2 (dpANS X3.134.1) - not supported - // case 'N': // (SP) Set ANSI conformance level 3 (dpANS X3.134.1) - not supported - // case '3': // (#) DEC double-height line, top half (DECDHL) - not supported - // case '4': // (#) DEC double-height line, bottom half (DECDHL) - not supported - // case '5': // (#) DEC single-width line (DECSWL) - not supported - // case '6': // (#) DEC double-width line (DECDWL) - not supported - // case '8': // (#) DEC Screen Alignment Test (DECALN) - not supported - // case '@': // (%) Select default character set. That is ISO 8859-1 (ISO 2022) - not supported - // case 'G': // (%) Select UTF-8 character set (ISO 2022) - not supported - // (() Designate G0 Character Set (ISO 2022, VT100) - // more flags: A B < %5 > 4 C 5 R f Q 9 K Y ` E 6 %6 Z H 7 = - // more collected: ) G1, * G2, + G3, - G1, . G2, / G3 - case '0': - if (collected == '(' || collected == ')') this.charset = CHARSET_0; - break; - // always reset charset - case 'B': - this.charset = null; - break; - // case '6': // Back Index (DECBI), VT420 and up - not supported - // Save Cursor (DECSC) - case '7': - return this.DECSC(); - // Restore Cursor (DECRC) - case '8': - return this.DECRC(); - // case '9': // Forward Index (DECFI), VT420 and up - not supported - // case '=': // Application Keypad (DECKPAM) // TODO - // case '>': // Normal Keypad (DECKPNM) // TODO - // case 'F': // Cursor to lower left corner of screen // TODO - // Full Reset (RIS) http://vt100.net/docs/vt220-rm/chapter4.html - case 'c': - return this.reset(); - // case 'l': // Memory Lock (per HP terminals). Locks memory above the cursor. - not supported - // case 'm': // Memory Unlock (per HP terminals). - not supported - // case 'n': // Invoke the G2 Character Set as GL (LS2). - not supported - // case 'o': // Invoke the G3 Character Set as GL (LS3). - not supported - // case '|': // Invoke the G3 Character Set as GR (LS3R). - not supported - // case '}': // Invoke the G2 Character Set as GR (LS2R). - not supported - // case '~': // Invoke the G1 Character Set as GR (LS1R). - not supported - case 'E': - return this.NEL(); - case 'D': - return this.IND(); - // ESC M Reverse Index ( RI is 0x8d). - case 'M': - return this.RI(); - default : - console.log('inst_e unhandled:', collected, flag); - } -}; - -AnsiTerminal.prototype.inst_H = function (collected, params, flag){ - console.log('inst_H unhandled:', collected, params, flag); - - this.last_char = ''; - this.wrap = false; -}; - -AnsiTerminal.prototype.inst_P = function (data){ - console.log('inst_P unhandled:', data); - - this.last_char = ''; - this.wrap = false; -}; - -AnsiTerminal.prototype.inst_U = function (){ - console.log('inst_U unhandled'); - - this.last_char = ''; - this.wrap = false; -}; - -/** - * functionality implementation - * * - * cheatsheets: - * - http://www.inwap.com/pdp10/ansicode.txt - * - overview http://www.vt100.net/docs/vt510-rm/chapter4 - * - http://paulbourke.net/dataformats/ascii/ - * - mouse support: http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html - * - sequences: http://docs2.attachmate.com/verastream/vhi/7.6sp1/en/index.jsp?topic=%2Fcom.attachmate.vhi.help%2Fhtml%2Freference%2Fcontrol_functions_sortbysequ.xhtml - */ - -/** - * unhandled sequences: (mc - mouse support) - * "inst_c unhandled:" "?" Array [ 2004 ] "h" bracketed paste mode https://cirw.in/blog/bracketed-paste - * - */ - - // scroll down - http://vt100.net/docs/vt510-rm/SD - // FIXME: apply new buffer logic -AnsiTerminal.prototype.SD = function (params){ - var lines = (params[0]) ? params[0] : 1; - - do { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_top, 0, row); - this.screen.buffer.splice(this.scrolling_bottom, 1); - } while (--lines); -}; - -// scroll up - http://vt100.net/docs/vt510-rm/SU -// FIXME: apply new buffer logic -AnsiTerminal.prototype.SU = function (params){ - var lines = (params[0]) ? params[0] : 1; - - do { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom, 0, row); - this.screen.buffer.splice(this.scrolling_top, 1); - } while (--lines); -}; - -// repeat - Repeat the preceding graphic character P s times (REP). -// FIXME: hacky solution with this.last_char -AnsiTerminal.prototype.REP = function (params){ - var s = ''; - var c = this.last_char; - var n = (params[0]) ? params[0] : 1; - - if (c) { - do { - s += c; - } while (--n); - - // for max col we need to set col to width to take - // advantage of the autowrapping in inst_p - // FIXME: not true anymore - if (this.cursor.col == this.cols - 1) { - this.cursor.col = this.cols; - } - - this.inst_p(s); - - this.last_char = ''; - } -}; - -// next line - http://vt100.net/docs/vt510-rm/NEL -AnsiTerminal.prototype.NEL = function (){ - this.cursor.row += 1; - - if (this.cursor.row >= this.scrolling_bottom) { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom, 0, row); - - var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; - - if (!this.scrolling_top) { - this.screen.appendToScrollBuffer(scrolled_out); - } - - this.cursor.row -= 1; - } - - this.cursor.col = 0; -}; - -// index - http://vt100.net/docs/vt510-rm/IND -AnsiTerminal.prototype.IND = function (){ - this.cursor.row += 1; - - if (this.cursor.row >= this.scrolling_bottom) { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom, 0, row); - - var scrolled_out = this.screen.buffer.splice(this.scrolling_top, 1)[0]; - - if (!this.scrolling_top) { - this.screen.appendToScrollBuffer(scrolled_out); - } - - this.cursor.row -= 1; - } -}; - -// vertical position relative - http://vt100.net/docs/vt510-rm/VPR -AnsiTerminal.prototype.VPR = function (params){ - this.cursor.row += ((params[0]) ? params[0] : 1); - - if (this.cursor.row >= this.rows) { - this.cursor.row = this.rows - 1; - } -}; - -// horizontal position relative - http://vt100.net/docs/vt510-rm/HPR -AnsiTerminal.prototype.HPR = function (params){ - this.cursor.col += ((params[0]) ? params[0] : 1); - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// horizontal position absolute - http://vt100.net/docs/vt510-rm/HPA -AnsiTerminal.prototype.HPA = function (params){ - this.cursor.col = ((params[0]) ? params[0] : 1) - 1; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// cursor backward tabulation - http://vt100.net/docs/vt510-rm/CBT -AnsiTerminal.prototype.CBT = function (params){ - this.cursor.col = (Math.floor((this.cursor.col - 1) / this.tab_width) - + 1 - ((params[0]) ? params[0] : 1)) * this.tab_width; - - if (this.cursor.col < 0) { - this.cursor.col = 0; - } -}; - -// cursor horizontal forward tabulation - http://vt100.net/docs/vt510-rm/CHT -AnsiTerminal.prototype.CHT = function (params){ - this.cursor.col = (Math.floor(this.cursor.col / this.tab_width) - + ((params[0]) ? params[0] : 1)) * this.tab_width; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// cursor previous line - http://vt100.net/docs/vt510-rm/CPL -AnsiTerminal.prototype.CPL = function (params){ - this.cursor.row -= (params[0]) ? params[0] : 1; - - if (this.cursor.row < 0) { - this.cursor.row = 0; - } - - this.cursor.col = 0; -}; - -// cursor next line - http://vt100.net/docs/vt510-rm/CNL -AnsiTerminal.prototype.CNL = function (params){ - this.cursor.row += (params[0]) ? params[0] : 1; - - if (this.cursor.row >= this.rows) { - this.cursor.row = this.rows - 1; - } - - this.cursor.col = 0; -}; - -// delete line - http://vt100.net/docs/vt510-rm/DL -AnsiTerminal.prototype.DL = function (params){ - var lines = params[0] || 1; - - do { - this.screen.buffer.splice(this.cursor.row, 1); - - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_bottom - 1, 0, row); - } while (--lines); - - // see http://vt100.net/docs/vt220-rm/chapter4.html - this.cursor.col = 0; -}; - -// insert character - http://vt100.net/docs/vt510-rm/ICH -AnsiTerminal.prototype.ICH = function (params){ - var chars = params[0] || 1; - - do { - // FIXME ugly code - do splicing only once - this.screen.buffer[this.cursor.row].cells - .splice(this.cursor.col, 0, new TChar('', this.textattributes, this.colors)); - this.screen.buffer[this.cursor.row].cells.pop(); - } while (--chars); -}; - -// Vertical Line Position Absolute - http://vt100.net/docs/vt510-rm/VPA -AnsiTerminal.prototype.VPA = function (params){ - this.cursor.row = ((params[0]) ? params[0] : 1) - 1; - - if (this.cursor.row >= this.rows) { - this.cursor.row = this.rows - 1; - } -}; - -// erase character - http://vt100.net/docs/vt510-rm/ECH -AnsiTerminal.prototype.ECH = function (params){ - var erase = ((params[0]) ? params[0] : 1) + this.cursor.col; - - erase = (this.cols < erase) ? this.cols : erase; - - for (var i = this.cursor.col; i < erase; ++i) { - this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); - } - - this.screen.buffer[this.cursor.row].version++; -}; - -// Insert Line - http://vt100.net/docs/vt510-rm/IL -AnsiTerminal.prototype.IL = function (params){ - var lines = (params[0]) ? params[0] : 1; - - // FIXME ugly code - less splice possible? - do { - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.cursor.row, 0, row); - this.screen.buffer.splice(this.scrolling_bottom, 1); - } while (--lines); - - // see http://vt100.net/docs/vt220-rm/chapter4.html - this.cursor.col = 0; -}; - -// Set Top and Bottom Margins - http://vt100.net/docs/vt510-rm/DECSTBM -AnsiTerminal.prototype.DECSTBM = function (params){ - var top = params[0] - 1 || 0; - var bottom = params[1] || this.rows; - - top = (top < 0) ? 0 : ((top >= this.rows) ? (this.rows - 1) : top); - bottom = (bottom > this.rows) ? (this.rows) : bottom; - - if (bottom > top) { - this.scrolling_top = top; - this.scrolling_bottom = bottom; - } - - // always set cursor to top (seems xterm always does this - bug?) - this.cursor.row = 0; -}; - -// soft terminal reset - http://vt100.net/docs/vt510-rm/DECSTR -AnsiTerminal.prototype.DECSTR = function (){ - // DECTCEM Text cursor enable --> Cursor enabled. - this.show_cursor = true; - // IRM Insert/replace --> Replace mode. - this.insert_mode = false; - // DECOM Origin --> Absolute (cursor origin at upper-left of screen.) TODO do we need this? - this.CUP(); // at least move cursor home - // DECAWM Autowrap --> No autowrap. TODO: really to false? - //this.autowrap = false; - // DECNRCM National replacement character set --> Multinational set. - unsupported - // KAM Keyboard action --> Unlocked. TODO - // DECNKM Numeric keypad --> Numeric characters. TODO - // DECCKM Cursor keys --> Normal (arrow keys). - this.cursor_key_mode = false; - // DECSTBM Set top and bottom margins --> Top margin = 1; bottom margin = page length. - this.DECSTBM([]); - // G0, G1, G2, G3, GL, GR --> Default settings. - unsupported - this.charset = null; // reset at least to unicode - // SGR Select graphic rendition --> Normal rendition. - this.SGR([0]); - // DECSCA Select character attribute --> Normal (erasable by DECSEL and DECSED). TODO do we need this? - // DECSC Save cursor state --> Home position. - this.DECSC(); - // DECAUPSS Assign user preference supplemental set --> Set selected in Set-Up. - unsupported - // DECSASD Select active status display --> Main display. TODO do we need this? - // DECKPM Keyboard position mode --> Character codes. TODO do we need this? - // DECRLM Cursor direction --> Reset (Left-to-right), regardless of NVR setting. TODO - // DECPCTERM PC Term mode --> Always reset. TODO do we need this? - // TODO: do we need to reset LNM? -}; - -// reverse index -AnsiTerminal.prototype.RI = function (){ - this.cursor.row -= 1; - - if (this.cursor.row < this.scrolling_top) { - this.cursor.row = this.scrolling_top; - - var row = new Row(); - - for (var j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer.splice(this.scrolling_top, 0, row); - this.screen.buffer.splice(this.scrolling_bottom, 1); - } -}; - -// save curor - http://vt100.net/docs/vt510-rm/DECSC -AnsiTerminal.prototype.DECSC = function (){ - var save = {}; - - save['cursor'] = { row: this.cursor.row, col: this.cursor.col }; - save['textattributes'] = this.textattributes; - save['charattributes'] = this.charattributes; - this.cursor_save = save; - // FIXME: this.colors -}; - -// restore cursor - http://vt100.net/docs/vt510-rm/DECRC -AnsiTerminal.prototype.DECRC = function (){ - // FIXME: this.colors - if (this.cursor_save) { - // load data back - this.cursor.col = this.cursor_save['cursor'].col; - this.cursor.row = this.cursor_save['cursor'].row; - this.textattributes = this.cursor_save['textattributes']; - this.charattributes = this.cursor_save['charattributes']; - } else { - // see http://vt100.net/docs/vt510-rm/DECRC - this.CUP(); - this.ED([2]); - this.textattributes = 0; - this.charattributes = 0; - } -}; - -AnsiTerminal.prototype.high = function (collected, params){ - // TODO: separate DEC and ANSI - for (var i = 0; i < params.length; ++i) { - switch (params[i]) { - // DECCKM - case 1: - this.cursor_key_mode = true; - break; - case 4: - if (!collected) { - // IRM - this.insert_mode = true; - } else { - // DECSCLM?? - console.log('unhandled high', collected, params[i]); - } - break; - case 7: - if (collected == '?') { - // DECAWM (should be default?) - this.autowrap = true; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 12: - if (collected == '?') { - this.blinking_cursor = true; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 20: - if (!collected) { - // LNM - this.newline_mode = true; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 25: - this.show_cursor = true; - // DECTCEM (default) - break; - // printer stuff not supported - case 43: - case 44: - case 45: - case 46: - case 47: - // end printer stuff - break; - /* mouse handling - * - 5 exclusive mouse modes: - * 9 X10 (press only) - * 1000 press and release events - * 1001 hilite mouse tracking (??) - * 1002 cell motion tracking (press, move on pressed, release) - * 1003 all (press, move, release) - * - exclusive formatting: - * 1005 utf-8 mouse mode - * 1006 sgr mouse mode - * 1015 urxvt mouse mode (decimal) - * - special focus event: 1004 (CSI I / CSI O) - * */ - case 9: - case 1000: - case 1001: - case 1002: - case 1003: - this.mouse_mode = params[i]; - this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); - break; - //case 1004: // focusIn/Out events - case 1005: - case 1006: - case 1015: - this.mouse_protocol = params[i]; - this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); - break; - // alt buffer - case 1049: - this.screen = this.alternate_screen; - this.cursor = this.alternate_cursor; - break; - default: - console.log('unhandled high', collected, params[i]); - } - } -}; - -AnsiTerminal.prototype.low = function (collected, params){ - // TODO: separate DEC and ANSI - for (var i = 0; i < params.length; ++i) { - switch (params[i]) { - case 1: - this.cursor_key_mode = false; - // DECCKM (default) - break; - case 4: - // IRM (default) - if (!collected) { - this.insert_mode = false; - } else { - console.log('unhandled low', collected, params[i]); - } - break; - case 7: - if (collected == '?') { - // DECAWM (default) - this.autowrap = false; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 12: - if (collected == '?') { - this.blinking_cursor = false; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 20: - if (!collected) { - // LNM (default) - this.newline_mode = false; - } else { - console.log('unhandled high', collected, params[i]); - } - break; - case 25: - this.show_cursor = false; - // DECTCEM - break; - // printer stuff not supported - case 43: - case 44: - case 45: - case 46: - case 47: - // end printer stuff - break; - case 9: - case 1000: - case 1001: - case 1002: - case 1003: - this.mouse_mode = 0; - this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); - break; - //case 1004: // focusIn/Out events - case 1005: - case 1006: - case 1015: - this.mouse_protocol = 0; - this.changedMouseHandling(this.mouse_mode, this.mouse_protocol); - break; - case 1049: - this.screen = this.normal_screen; - this.cursor = this.normal_cursor; - break; - default: - console.log('unhandled low', collected, params[i]); - } - } -}; - -// device status reports - http://vt100.net/docs/vt510-rm/DSR -// cursor position report - http://vt100.net/docs/vt510-rm/CPR -// FIXME: split and rename to DSR and CPR -AnsiTerminal.prototype.DSR = function (collected, params){ - switch (params[0]) { - // DSR - just send 'OK' - case 5: - this.send(TERM_STRING['CSI'] + '0n'); - break; - // cursor position report - case 6: - this.send(TERM_STRING['CSI'] + collected + (this.cursor.row + 1) + ';' + (this.cursor.col + 1) + 'R'); - break; - // DSR-DIR data integrity report - just send 'ready, no errors' - case 75: - this.send(TERM_STRING['CSI'] + '?70n'); - break; - default: - console.log('unhandled DSR', collected, params); - } -}; - -// cursor horizontal absolute - http://vt100.net/docs/vt510-rm/CHA -AnsiTerminal.prototype.CHA = function (params){ - this.cursor.col = ((params) ? (params[0] || 1) : 1) - 1; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// cursor backward - http://vt100.net/docs/vt510-rm/CUB -AnsiTerminal.prototype.CUB = function (params){ - this.cursor.col -= (params) ? (params[0] || 1) : 1; - - if (this.cursor.col < 0) { - this.cursor.col = 0; - } -}; - -// cursor down - http://vt100.net/docs/vt510-rm/CUD -AnsiTerminal.prototype.CUD = function (params){ - this.cursor.row += (params) ? (params[0] || 1) : 1; - - if (this.cursor.row >= this.rows) { - this.cursor.row = this.rows - 1; - } -}; - -// cursor forward - http://vt100.net/docs/vt510-rm/CUF -AnsiTerminal.prototype.CUF = function (params){ - this.cursor.col += (params) ? (params[0] || 1) : 1; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// cursor up - http://vt100.net/docs/vt510-rm/CUU -AnsiTerminal.prototype.CUU = function (params){ - this.cursor.row -= (params) ? (params[0] || 1) : 1; - - if (this.cursor.row < 0) { - this.cursor.row = 0; - } -}; - -// cursor position - http://vt100.net/docs/vt510-rm/CUP -AnsiTerminal.prototype.CUP = function (params){ - this.cursor.row = ((params) ? (params[0] || 1) : 1) - 1; - - if (this.cursor.row >= this.rows) { - this.cursor.row = this.rows - 1; - } - - this.cursor.col = ((params) ? (params[1] || 1) : 1) - 1; - - if (this.cursor.col >= this.cols) { - this.cursor.col = this.cols - 1; - } -}; - -// delete character - http://vt100.net/docs/vt510-rm/DCH -AnsiTerminal.prototype.DCH = function (params){ - var removed = this.screen.buffer[this.cursor.row].cells - .splice(this.cursor.col, (params) ? (params[0] || 1) : 1); - - for (var i = 0; i < removed.length; ++i) { - this.screen.buffer[this.cursor.row].cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer[this.cursor.row].version++; -}; - -// erase in display - http://vt100.net/docs/vt510-rm/ED -AnsiTerminal.prototype.ED = function (params){ - var i, j, row; - - switch ((params) ? params[0] : 0) { - case 0: - // from cursor to end of display - // remove to line end - this.EL([0]); - // clear lower lines - for (i = this.cursor.row + 1; i < this.rows; ++i) { - row = new Row(); - - for (j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer[i] = row; - } - break; - case 1: - // from top of display to cursor - // clear upper lines - for (i = 0; i < this.cursor.row; ++i) { - row = new Row(); - - for (j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer[i] = row; - } - - // clear line up to cursor - this.EL([1]); - break; - case 2: - // complete display - for (i = 0; i < this.rows; ++i) { - row = new Row(); - - for (j = 0; j < this.cols; ++j) { - row.cells.push(new TChar('', this.textattributes, this.colors)); - } - - this.screen.buffer[i] = row; - } - break; - } -}; - -// erase in line - http://vt100.net/docs/vt510-rm/EL -AnsiTerminal.prototype.EL = function (params){ - var i; - - switch ((params) ? params[0] : 0) { - case 0: - // cursor to end of line - for (i = this.cursor.col; i < this.cols; ++i) { - this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); - } - - this.screen.buffer[this.cursor.row].version++; - break; - case 1: - // beginning of line to cursor - for (i = 0; i <= this.cursor.col; ++i) { - this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); - } - - this.screen.buffer[this.cursor.row].version++; - break; - case 2: - // complete line - for (i = 0; i < this.cols; ++i) { - this.screen.buffer[this.cursor.row].cells[i] = new TChar('', this.textattributes, this.colors); - } - - this.screen.buffer[this.cursor.row].version++; - break; - } -}; - -// select graphic rendition - http://vt100.net/docs/vt510-rm/SGR -AnsiTerminal.prototype.SGR = function (params){ - // load global attributes and colors - var attr = this.textattributes; - var colors = this.colors; - - var ext_colors = 0; - var RGB_mode = false; - var counter = 0; - - // put reverse video mode in attributes - // used in charattributes but not in global textattributes - // to mimick xterm behavior - if (this.reverse_video) { - attr |= 1048576; - } - - for (var i = 0; i < params.length; ++i) { - // special treatment for extended colors - if (ext_colors) { - // first run in ext_colors gives color mode - // sets counter to determine max consumed params - if (!counter) { - switch (params[i]) { - case 2: - RGB_mode = true; - // eval up to 3 params - counter = 3; - // fg set SET+RGB: |(1<<26)|(1<<27) - // bg set SET+RGB: |(1<<24)|(1<<25) - attr |= (ext_colors == 38) ? 201326592 : 50331648; - break; - case 5: - RGB_mode = false; - // eval only 1 param - counter = 1; - // fg clear RGB, set SET: &~(1<<27)|(1<<26) - // bg clear RGB, set SET: &~(1<<25)|(1<<24) - attr = (ext_colors == 38) - ? (attr & ~134217728) | 67108864 - : (attr & ~33554432) | 16777216; - break; - default: - // unkown mode identifier, breaks ext_color mode - console.log('sgr unknown extended color mode:', ext_colors[1]); - ext_colors = 0; - } - continue; - } - if (RGB_mode) { - switch (counter) { - case 3: - // red - attr = (ext_colors == 38) - ? (attr & ~65280) | (params[i] << 8) - : (attr & ~255) | params[i]; - break; - case 2: - // green - colors = (ext_colors == 38) - ? (colors & ~4278190080) | (params[i] << 24) - : (colors & ~16711680) | (params[i] << 16); - break; - case 1: - // blue - colors = (ext_colors == 38) - ? (colors & ~65280) | (params[i] << 8) - : (colors & ~255) | params[i]; - } - } else { - // 256 color mode - // uses only lower bytes of attribute - attr = (ext_colors == 38) - ? (attr & ~65280) | (params[i] << 8) - : (attr & ~255) | params[i]; - } - counter -= 1; - - if (!counter) { - ext_colors = 0; - } - continue; - } - - switch (params[i]) { - case 0: - attr = 0; - break; - // bold on - case 1: - attr |= 65536; - break; - // not supported (faint) - case 2: - break; - // italic on - case 3: - attr |= 131072; - break; - // underline on - case 4: - attr |= 262144; - break; - // blink on - case 5: - attr |= 524288; - break; - // only one blinking speed - case 6: - attr |= 524288; - break; - // inverted on - case 7: - attr |= 1048576; - break; - // conceal on - case 8: - attr |= 2097152; - break; - // not supported (crossed out) - case 9: - break; - // not supported (font selection) - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - break; - // not supported (fraktur) - case 20: - break; - case 21: - // not supported (bold: off or underline: double) - break; - // bold off - case 22: - attr &= ~65536; - break; - // italic off - case 23: - attr &= ~131072; - break; - // underline off - case 24: - attr &= ~262144; - break; - // blink off - case 25: - attr &= ~524288; - break; - // reserved - case 26: - break; - // inverted off - case 27: - attr &= ~1048576; - break; - // conceal off - case 28: - attr &= ~2097152; - break; - // not supported (not crossed out) - case 29: - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - // clear fg RGB, nullify fg, set fg SET, color - // -134283009 = ~(1<<27) & ~(255<<8) - attr = (attr & -134283009) | 67108864 | (params[i] % 10 << 8); - break; - case 38: - ext_colors = 38; - break; - // default foreground color - case 39: - // fg set to false (1<<26) - attr &= ~67108864; - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - // clear bg RGB, nullify bg, set bg SET, color - // -33554688 = ~(1<<25) & ~255 - attr = (attr & -33554688) | 16777216 | params[i] % 10; - break; - case 48: - ext_colors = 48; - break; - // default background color - case 49: - // bg set to false - attr &= ~16777216; - break; - case 90: - case 91: - case 92: - case 93: - case 94: - case 95: - case 96: - case 97: - // same as 37 but with |8 in color - attr = (attr & -134283009) | 67108864 | (params[i] % 10 | 8 << 8); - break; - case 100: - case 101: - case 102: - case 103: - case 104: - case 105: - case 106: - case 107: - // same as 47 but with |8 in color - attr = (attr & -33554688) | 16777216 | params[i] % 10 | 8; - break; - default: - console.log('sgr unknown:', params[i]); - } - } - - // apply new attributes - // charattributes differs only in reverse mode - // for now from textattributes - this.charattributes = attr; - - // set reverse video and delete it from attributes - this.reverse_video = !!(attr & 1048576); - attr &= ~1048576; - - // set new global attributes - this.textattributes = attr; - this.colors = colors; -}; - -/** - * hex - * @param c - * @returns {*} - */ -function hex(c){ - c = c.toString(16); - - return c.length < 2 ? '0' + c : c; -} - -/** - * 256 colors - */ -var COLORS = (function colors256(){ - var i; - var r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - - // copy colors - var colors = [ - // 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 - ]; - - // 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)); - } - - return colors; -}()); - -/** - * char styles - * @param node - * @returns {{wide: boolean}} - */ -function styles(node){ - var color; - var gb = node.gb; - var attr = node.attr; - var attributes = node.getAttributes(); - var foreground = attributes.foreground; - var background = attributes.background; - var styles = { wide: node.width === 2 }; - - [ - 'bold', 'italic', 'underline', - 'blink', 'inverse', 'conceal' - ].forEach(function (key){ - styles[key] = attributes[key]; - }); - - styles.frgb = foreground.RGB; - styles.grgb = background.RGB; - - if (foreground.set && !foreground.RGB) { - if (attributes.inverse) { - if (attributes.bold) { - styles.background = COLORS[(attr >>> 8 & 255) | 8]; - } else { - styles.background = COLORS[attr >>> 8 & 255]; - } - } else { - if (attributes.bold) { - styles.foreground = COLORS[(attr >>> 8 & 255) | 8]; - } else { - styles.foreground = COLORS[attr >>> 8 & 255]; - } - } - } - - if (background.set && !background.RGB) { - if (attributes.inverse) { - styles.foreground = COLORS[attr & 255]; - } else { - styles.background = COLORS[attr & 255]; - } - } - - if (foreground.set && foreground.RGB) { - color = '#' + hex(attr >>> 8 & 255) + hex(gb >>> 24) + hex(gb >>> 8 & 255); - - if (attributes.inverse) { - styles.background = color; - } else { - styles.foreground = color; - } - } - - if (background.set && background.RGB) { - color = '#' + hex(attr & 255) + hex(gb >>> 16 & 255) + hex(gb & 255); - - if (attributes.inverse) { - styles.foreground = color; - } else { - styles.background = color; - } - } - - return styles; -} - -var colors = ''; - -COLORS.forEach(function (color, index){ - colors += '\r\n' + index + ': ' + color; -}); - -console.log(colors);