/lib/program.js
JavaScript | 3581 lines | 2447 code | 375 blank | 759 comment | 459 complexity | 9cf7620da05e61e38f7a2e292cb14e27 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /**
- * program.js - basic curses-like functionality for blessed.
- * Copyright (c) 2013, Christopher Jeffrey (MIT License).
- * https://github.com/chjj/blessed
- */
- /**
- * Modules
- */
- var EventEmitter = require('events').EventEmitter
- , StringDecoder = require('string_decoder').StringDecoder
- , util = require('util')
- , fs = require('fs');
- var Tput = require('./tput')
- , colors = require('./colors')
- , slice = Array.prototype.slice;
- /**
- * Program
- */
- function Program(options) {
- var self = this;
- if (!(this instanceof Program)) {
- return new Program(options);
- }
- EventEmitter.call(this);
- if (!options || options.__proto__ !== Object.prototype) {
- options = {
- input: arguments[0],
- output: arguments[1]
- };
- }
- this.options = options;
- this.input = options.input || process.stdin;
- this.output = options.output || process.stdout;
- options.log = options.log || options.dump;
- if (options.log) {
- this._logger = fs.createWriteStream(options.log);
- if (options.dump) this.setupDump();
- }
- this.zero = options.zero !== false;
- this.useBuffer = options.buffer;
- this.x = 0;
- this.y = 0;
- this.savedX = 0;
- this.savedY = 0;
- this.cols = this.output.columns || 1;
- this.rows = this.output.rows || 1;
- this.scrollTop = 0;
- this.scrollBottom = this.rows - 1;
- this.terminal = options.term
- || options.terminal
- || process.env.TERM
- || (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
- this._buf = '';
- this._flush = this.flush.bind(this);
- unshiftEvent(process, 'exit', function() {
- // Ensure the buffer is flushed (it should
- // always be at this point, but who knows).
- self.flush();
- // Ensure _exiting is set (could technically
- // use process._exiting).
- self._exiting = true;
- });
- if (!Program.global) {
- Program.global = this;
- }
- if (options.tput !== false) {
- this.setupTput();
- }
- this.listen();
- }
- Program.prototype.__proto__ = EventEmitter.prototype;
- Program.prototype.log = function() {
- return this._log('LOG', util.format.apply(util, arguments));
- };
- Program.prototype.debug = function() {
- if (!this.options.debug) return;
- return this._log('DEBUG', util.format.apply(util, arguments));
- };
- Program.prototype._log = function(pre, msg) {
- if (!this._logger) return;
- return this._logger.write(pre + ': ' + msg + '\n-\n');
- };
- Program.prototype.setupDump = function() {
- var self = this
- , write = this.output.write
- , decoder = new StringDecoder('utf8');
- function stringify(data) {
- return caret(data
- .replace(/\r/g, '\\r')
- .replace(/\n/g, '\\n')
- .replace(/\t/g, '\\t'))
- .replace(/[^ -~]/g, function(ch) {
- if (ch.charCodeAt(0) > 0xff) return ch;
- ch = ch.charCodeAt(0).toString(16);
- if (ch.length > 2) {
- if (ch.length < 4) ch = '0' + ch;
- return '\\u' + ch;
- }
- if (ch.length < 2) ch = '0' + ch;
- return '\\x' + ch;
- });
- }
- function caret(data) {
- return data.replace(/[\0\x80\x1b-\x1f\x7f\x01-\x1a]/g, function(ch) {
- switch (ch) {
- case '\0':
- case '\200':
- ch = '@';
- break;
- case '\x1b':
- ch = '[';
- break;
- case '\x1c':
- ch = '\\';
- break;
- case '\x1d':
- ch = ']';
- break;
- case '\x1e':
- ch = '^';
- break;
- case '\x1f':
- ch = '_';
- break;
- case '\x7f':
- ch = '?';
- break;
- default:
- ch = ch.charCodeAt(0);
- // From ('A' - 64) to ('Z' - 64).
- if (ch >= 1 && ch <= 26) {
- ch = String.fromCharCode(ch + 64);
- } else {
- return String.fromCharCode(ch);
- }
- break;
- }
- return '^' + ch;
- });
- }
- this.input.on('data', function(data) {
- self._log('IN', stringify(decoder.write(data)));
- });
- this.output.write = function(data) {
- self._log('OUT', stringify(data));
- return write.apply(this, arguments);
- };
- };
- Program.prototype.setupTput = function() {
- if (this._tputSetup) return;
- this._tputSetup = true;
- var self = this
- , options = this.options
- , write = this.write.bind(this);
- var tput = this.tput = new Tput({
- term: this.terminal,
- padding: options.padding,
- extended: options.extended,
- printf: options.printf,
- termcap: options.termcap
- });
- this.put = function() {
- var args = slice.call(arguments)
- , cap = args.shift();
- if (tput[cap]) {
- return this._write(tput[cap].apply(tput, args));
- }
- };
- Object.keys(tput).forEach(function(key) {
- if (self[key] == null) {
- self[key] = tput[key];
- }
- if (typeof tput[key] !== 'function') {
- self.put[key] = tput[key];
- return;
- }
- if (options.padding) {
- self.put[key] = function() {
- return tput._print(tput[key].apply(tput, arguments), write);
- };
- } else {
- self.put[key] = function() {
- return self._write(tput[key].apply(tput, arguments));
- };
- }
- });
- };
- Program.prototype.has = function(name) {
- return this.tput
- ? this.tput.has(name)
- : false;
- };
- Program.prototype.term = function(is) {
- return this.terminal.indexOf(is) === 0;
- };
- Program.prototype.listen = function() {
- var readline = require('readline')
- , self = this;
- if (!this.input.isTTY || !this.output.isTTY) {
- throw new Error('Not a terminal.');
- }
- // Input
- this.input.on('keypress', function(ch, key) {
- key = key || { ch: ch };
- if (key.name === 'undefined'
- && (key.code === '[M' || key.code === '[I' || key.code === '[O')) {
- // A mouse sequence. The readline module doesn't understand these.
- return;
- }
- if (key.name === 'undefined') {
- // Not sure what this is, but we should probably ignore it.
- return;
- }
- if (key.name === 'enter' && key.sequence === '\n') {
- key.name = 'linefeed';
- }
- if (key.name === 'return' && key.sequence === '\r') {
- self.input.emit('keypress', ch, merge({}, key, { name: 'enter' }));
- }
- var name = (key.ctrl ? 'C-' : '')
- + (key.meta ? 'M-' : '')
- + (key.shift && key.name ? 'S-' : '')
- + (key.name || ch);
- key.full = name;
- self.emit('keypress', ch, key);
- self.emit('key ' + name, ch, key);
- });
- this.input.on('data', function(data) {
- self.emit('data', data);
- });
- readline.emitKeypressEvents(this.input);
- this.on('newListener', function fn(type) {
- if (type === 'keypress' || type === 'mouse') {
- self.removeListener('newListener', fn);
- if (!self.input.isRaw) {
- self.input.setRawMode(true);
- self.input.resume();
- }
- }
- });
- this.on('newListener', function fn(type) {
- if (type === 'mouse') {
- self.removeListener('newListener', fn);
- self.bindMouse();
- }
- });
- // Output
- this.output.on('resize', function() {
- self.cols = self.output.columns;
- self.rows = self.output.rows;
- self.emit('resize');
- });
- };
- Program.prototype.key = function(key, listener) {
- if (typeof key === 'string') key = key.split(/\s*,\s*/);
- key.forEach(function(key) {
- return this.on('key ' + key, listener);
- }, this);
- };
- Program.prototype.onceKey = function(key, listener) {
- if (typeof key === 'string') key = key.split(/\s*,\s*/);
- key.forEach(function(key) {
- return this.once('key ' + key, listener);
- }, this);
- };
- Program.prototype.unkey =
- Program.prototype.removeKey = function(key, listener) {
- if (typeof key === 'string') key = key.split(/\s*,\s*/);
- key.forEach(function(key) {
- return this.removeListener('key ' + key, listener);
- }, this);
- };
- // XTerm mouse events
- // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
- // To better understand these
- // the xterm code is very helpful:
- // Relevant files:
- // button.c, charproc.c, misc.c
- // Relevant functions in xterm/button.c:
- // BtnCode, EmitButtonCode, EditorButton, SendMousePosition
- // send a mouse event:
- // regular/utf8: ^[[M Cb Cx Cy
- // urxvt: ^[[ Cb ; Cx ; Cy M
- // sgr: ^[[ Cb ; Cx ; Cy M/m
- // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
- // locator: CSI P e ; P b ; P r ; P c ; P p & w
- // motion example of a left click:
- // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
- // mouseup, mousedown, mousewheel
- // left click: ^[[M 3<^[[M#3<
- // mousewheel up: ^[[M`3>
- Program.prototype.bindMouse = function() {
- if (this._boundMouse) return;
- this._boundMouse = true;
- var decoder = new StringDecoder('utf8')
- , self = this;
- this.on('data', function(data) {
- data = decoder.write(data);
- if (!data) return;
- self._bindMouse(data);
- });
- };
- Program.prototype._bindMouse = function(s) {
- var self = this
- , key
- , parts;
- key = {
- name: undefined,
- ctrl: false,
- meta: false,
- shift: false
- };
- if (Buffer.isBuffer(s)) {
- if (s[0] > 127 && s[1] === undefined) {
- s[0] -= 128;
- s = '\x1b' + s.toString('utf-8');
- } else {
- s = s.toString('utf-8');
- }
- }
- // XTerm / X10
- if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) {
- var b = parts[1].charCodeAt(0)
- , x = parts[1].charCodeAt(1)
- , y = parts[1].charCodeAt(2)
- , mod;
- key.name = 'mouse';
- key.type = 'X10';
- key.raw = [b, x, y, parts[0]];
- key.x = x - 32;
- key.y = y - 32;
- if (key.x === -32) key.x = 255;
- if (key.y === -32) key.y = 255;
- if (this.zero) key.x--, key.y--;
- mod = b >> 2;
- key.shift = !!(mod & 1);
- key.meta = !!((mod >> 1) & 1);
- key.ctrl = !!((mod >> 2) & 1);
- b -= 32;
- if ((b >> 6) & 1) {
- key.action = b & 1 ? 'wheeldown' : 'wheelup';
- key.button = 'middle';
- } else if (b === 3) {
- // Could also be a movement.
- key.action = 'mouseup';
- key.button = 'unknown';
- } else {
- key.action = 'mousedown';
- key.button =
- b === 0 ? 'left'
- : b === 1 ? 'middle'
- : b === 2 ? 'right'
- : 'unknown';
- }
- // It's a movement
- // Wrong
- //if (b > 32 && b < 64) {
- // delete key.button;
- // key.action = 'mousemove';
- //}
- // Probably a movement.
- if (key.action === 'mousedown' && key.button === 'unknown') {
- delete key.button;
- key.action = 'mousemove';
- }
- self.emit('keypress', null, key);
- self.emit('mouse', key);
- return;
- }
- // URxvt
- if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) {
- var parts = parts[1].split(';')
- , b = +parts[0]
- , x = +parts[1]
- , y = +parts[2];
- key.name = 'mouse';
- key.type = 'urxvt';
- key.x = x;
- key.y = y;
- if (this.zero) key.x--, key.y--;
- // NOTE: Duplicate of the above.
- mod = b >> 3;
- key.shift = mod & 4;
- key.meta = mod & 8;
- key.ctrl = mod & 16;
- b -= 32;
- if (b === 64) {
- key.action = 'wheelup';
- key.button = 'middle';
- } else if (b === 65) {
- key.action = 'wheeldown';
- key.button = 'middle';
- } else if (b === 3) {
- // Could also be a movement.
- key.action = 'mouseup';
- key.button = 'unknown';
- } else {
- key.action = 'mousedown';
- key.button =
- b === 0 ? 'left'
- : b === 1 ? 'middle'
- : b === 2 ? 'right'
- : 'unknown';
- }
- // It's a movement
- // Wrong
- //if (b > 32 && b < 64) {
- // delete key.button;
- // key.action = 'movement';
- //}
- // Probably a movement.
- if (key.action === 'mousedown' && key.button === 'unknown') {
- delete key.button;
- key.action = 'mousemove';
- }
- self.emit('keypress', null, key);
- self.emit('mouse', key);
- return;
- }
- // SGR
- if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) {
- var down = parts[2] === 'm'
- , parts = parts[1].split(';')
- , b = +parts[0]
- , x = +parts[1]
- , y = +parts[2];
- key.name = 'mouse';
- key.type = 'sgr';
- key.x = x;
- key.y = y;
- if (this.zero) key.x--, key.y--;
- b &= 3;
- // NOTE: Get mod. And wheel.
- key.action = down
- ? 'mousedown'
- : 'mouseup';
- key.button =
- b === 0 ? 'left'
- : b === 1 ? 'middle'
- : b === 2 ? 'right'
- : 'unknown';
- self.emit('keypress', null, key);
- self.emit('mouse', key);
- return;
- }
- // DEC
- // The xterm mouse documentation says there is a
- // `<` prefix, the DECRQLP says there is no prefix.
- if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) {
- var parts = parts[1].split(';')
- , b = +parts[0]
- , x = +parts[1]
- , y = +parts[2]
- , page = +parts[3];
- key.name = 'mouse';
- key.type = 'dec';
- key.button = b;
- key.x = x;
- key.y = y;
- if (this.zero) key.x--, key.y--;
- key.action = b === 3
- ? 'mouseup'
- : 'mousedown';
- key.button =
- b === 2 ? 'left'
- : b === 4 ? 'middle'
- : b === 6 ? 'right'
- : 'unknown';
- self.emit('keypress', null, key);
- self.emit('mouse', key);
- return;
- }
- // vt300
- if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) {
- var b = +parts[1]
- , x = +parts[2]
- , y = +parts[3];
- key.name = 'mouse';
- key.type = 'vt300';
- key.x = x;
- key.y = y;
- if (this.zero) key.x--, key.y--;
- key.action = 'mousedown';
- key.button =
- b === 1 ? 'left'
- : b === 2 ? 'middle'
- : b === 5 ? 'right'
- : 'unknown';
- self.emit('keypress', null, key);
- self.emit('mouse', key);
- return;
- }
- if (parts = /^\x1b\[(O|I)/.exec(s)) {
- key.action = parts[1] === 'I'
- ? 'focus'
- : 'blur';
- self.emit('mouse', key);
- self.emit(key.action);
- return;
- }
- };
- // All possible responses from the terminal
- Program.prototype.bindResponse = function() {
- if (this._boundResponse) return;
- this._boundResponse = true;
- var decoder = new StringDecoder('utf8')
- , self = this;
- this.on('data', function(data) {
- data = decoder.write(data);
- if (!data) return;
- self._bindResponse(data);
- });
- };
- Program.prototype._bindResponse = function(s) {
- var self = this
- , out = {}
- , parts;
- if (Buffer.isBuffer(s)) {
- if (s[0] > 127 && s[1] === undefined) {
- s[0] -= 128;
- s = '\x1b' + s.toString('utf-8');
- } else {
- s = s.toString('utf-8');
- }
- }
- // CSI P s c
- // Send Device Attributes (Primary DA).
- // CSI > P s c
- // Send Device Attributes (Secondary DA).
- if (parts = /^\x1b\[(\?|>)(\d*(?:;\d*)*)c/.exec(s)) {
- parts = parts[2].split(';').map(function(ch) {
- return +ch || 0;
- });
- out.event = 'device-attributes';
- out.code = 'DA';
- if (parts[1] === '?') {
- out.type = 'primary-attribute';
- // VT100-style params:
- if (parts[0] === 1 && parts[2] === 2) {
- out.term = 'vt100';
- out.advancedVideo = true;
- } else if (parts[0] === 1 && parts[2] === 0) {
- out.term = 'vt101';
- } else if (parts[0] === 6) {
- out.term = 'vt102';
- } else if (parts[0] === 60
- && parts[1] === 1 && parts[2] === 2
- && parts[3] === 6 && parts[4] === 8
- && parts[5] === 9 && parts[6] === 15) {
- out.term = 'vt220';
- } else {
- // VT200-style params:
- parts.forEach(function(attr) {
- switch (attr) {
- case 1:
- out.cols132 = true;
- break;
- case 2:
- out.printer = true;
- break;
- case 6:
- out.selectiveErase = true;
- break;
- case 8:
- out.userDefinedKeys = true;
- break;
- case 9:
- out.nationalReplacementCharsets = true;
- break;
- case 15:
- out.technicalCharacters = true;
- break;
- case 18:
- out.userWindows = true;
- break;
- case 21:
- out.horizontalScrolling = true;
- break;
- case 22:
- out.ansiColor = true;
- break;
- case 29:
- out.ansiTextLocator = true;
- break;
- }
- });
- }
- } else {
- out.type = 'secondary-attribute';
- switch (parts[0]) {
- case 0:
- out.term = 'vt100';
- break;
- case 1:
- out.term = 'vt220';
- break;
- case 2:
- out.term = 'vt240';
- break;
- case 18:
- out.term = 'vt330';
- break;
- case 19:
- out.term = 'vt340';
- break;
- case 24:
- out.term = 'vt320';
- break;
- case 41:
- out.term = 'vt420';
- break;
- case 61:
- out.term = 'vt510';
- break;
- case 64:
- out.term = 'vt520';
- break;
- case 65:
- out.term = 'vt525';
- break;
- }
- out.firmwareVersion = parts[1];
- out.romCartridgeRegistrationNumber = parts[2];
- }
- // LEGACY
- out.deviceAttributes = out;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- // CSI Ps n Device Status Report (DSR).
- // Ps = 5 -> Status Report. Result (``OK'') is
- // CSI 0 n
- // CSI ? Ps n
- // Device Status Report (DSR, DEC-specific).
- // 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.
- if (parts = /^\x1b\[(\?)?(\d+)(?:;(\d+);(\d+);(\d+))?n/.exec(s)) {
- out.event = 'device-status';
- out.code = 'DSR';
- if (!parts[1] && parts[2] === '0' && !parts[3]) {
- out.type = 'device-status';
- out.status = 'OK';
- // LEGACY
- out.deviceStatus = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] && (parts[2] === '10' || parts[2] === '11') && !parts[3]) {
- out.type = 'printer-status';
- out.status = parts[2] === '10'
- ? 'ready'
- : 'not ready';
- // LEGACY
- out.printerStatus = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] && (parts[2] === '20' || parts[2] === '21') && !parts[3]) {
- out.type = 'udk-status';
- out.status = parts[2] === '20'
- ? 'unlocked'
- : 'locked';
- // LEGACY
- out.UDKStatus = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1]
- && parts[2] === '27'
- && parts[3] === '1'
- && parts[4] === '0'
- && parts[5] === '0') {
- out.type = 'keyboard-status';
- out.status = 'OK';
- // LEGACY
- out.keyboardStatus = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] && (parts[2] === '53' || parts[2] === '50') && !parts[3]) {
- out.type = 'locator-status';
- out.status = parts[2] === '53'
- ? 'available'
- : 'unavailable';
- // LEGACY
- out.locator = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- out.type = 'error';
- out.text = 'Unhandled: ' + JSON.stringify(parts);
- // LEGACY
- out.error = out.text;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- // CSI Ps n Device Status Report (DSR).
- // 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).
- if (parts = /^\x1b\[(\?)?(\d+);(\d+)R/.exec(s)) {
- out.event = 'device-status-report';
- out.code = 'DSR';
- out.type = 'cursor-status';
- out.status = {
- x: +parts[3],
- y: +parts[2],
- page: !parts[1] ? undefined : 0
- };
- out.x = out.status.x;
- out.y = out.status.y;
- out.page = out.status.page;
- // LEGACY
- out.cursor = out.status;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- // CSI Ps ; Ps ; Ps t
- // Window manipulation (from dtterm, as well as extensions).
- // These controls may be disabled using the allowWindowOps
- // resource. Valid values for the first (and any additional
- // parameters) are:
- // Ps = 1 1 -> Report xterm window state. If the xterm window
- // is open (non-iconified), it returns CSI 1 t . If the xterm
- // window is iconified, it returns CSI 2 t .
- // Ps = 1 3 -> Report xterm window position. Result is CSI 3
- // ; x ; y t
- // Ps = 1 4 -> Report xterm window in pixels. Result is CSI
- // 4 ; height ; width t
- // Ps = 1 8 -> Report the size of the text area in characters.
- // Result is CSI 8 ; height ; width t
- // Ps = 1 9 -> Report the size of the screen in characters.
- // Result is CSI 9 ; height ; width t
- if (parts = /^\x1b\[(\d+)(?:;(\d+);(\d+))?t/.exec(s)) {
- out.event = 'window-manipulation';
- out.code = '';
- if ((parts[1] === '1' || parts[1] === '2') && !parts[2]) {
- out.type = 'window-state';
- out.state = parts[1] === '1'
- ? 'non-iconified'
- : 'iconified'
- // LEGACY
- out.windowState = out.state;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] === '3' && parts[2]) {
- out.type = 'window-position';
- out.position = {
- x: +parts[2],
- y: +parts[3]
- };
- out.x = out.position.x;
- out.y = out.position.y;
- // LEGACY
- out.windowPosition = out.position;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] === '4' && parts[2]) {
- out.type = 'window-size-pixels';
- out.size = {
- height: +parts[2],
- width: +parts[3]
- };
- out.height = out.size.height;
- out.width = out.size.width;
- // LEGACY
- out.windowSizePixels = out.size;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] === '8' && parts[2]) {
- out.type = 'textarea-size';
- out.size = {
- height: +parts[2],
- width: +parts[3]
- };
- out.height = out.size.height;
- out.width = out.size.width;
- // LEGACY
- out.textAreaSizeCharacters = out.size;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] === '9' && parts[2]) {
- out.type = 'screen-size';
- out.size = {
- height: +parts[2],
- width: +parts[3]
- };
- out.height = out.size.height;
- out.width = out.size.width;
- // LEGACY
- out.screenSizeCharacters = out.size;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- out.type = 'error';
- out.text = 'Unhandled: ' + JSON.stringify(parts);
- // LEGACY
- out.error = out.text;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- // CSI Ps ; Ps ; Ps t
- // Window manipulation (from dtterm, as well as extensions).
- // These controls may be disabled using the allowWindowOps
- // resource. Valid values for the first (and any additional
- // parameters) are:
- // Ps = 2 0 -> Report xterm window's icon label. Result is
- // OSC L label ST
- // Ps = 2 1 -> Report xterm window's title. Result is OSC l
- // label ST
- if (parts = /^\x1b\](l|L)([^\x07\x1b]*)(?:\x07|\x1b\\)/.exec(s)) {
- out.type = 'window-manipulation';
- out.code = '';
- if (parts[1] === 'L') {
- out.type = 'window-icon-label';
- out.text = parts[2];
- // LEGACY
- out.windowIconLabel = out.text;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- if (parts[1] === 'l') {
- out.type = 'window-title';
- out.text = parts[2];
- // LEGACY
- out.windowTitle = out.text;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- out.type = 'error';
- out.text = 'Unhandled: ' + JSON.stringify(parts);
- // LEGACY
- out.error = out.text;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- // CSI Ps ' |
- // Request Locator Position (DECRQLP).
- // -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
- // Parameters are [event;button;row;column;page].
- // Valid values for the event:
- // Pe = 0 -> locator unavailable - no other parameters sent.
- // Pe = 1 -> request - xterm received a DECRQLP.
- // Pe = 2 -> left button down.
- // Pe = 3 -> left button up.
- // Pe = 4 -> middle button down.
- // Pe = 5 -> middle button up.
- // Pe = 6 -> right button down.
- // Pe = 7 -> right button up.
- // Pe = 8 -> M4 button down.
- // Pe = 9 -> M4 button up.
- // Pe = 1 0 -> locator outside filter rectangle.
- // ``button'' parameter is a bitmask indicating which buttons are
- // pressed:
- // Pb = 0 <- no buttons down.
- // Pb & 1 <- right button down.
- // Pb & 2 <- middle button down.
- // Pb & 4 <- left button down.
- // Pb & 8 <- M4 button down.
- // ``row'' and ``column'' parameters are the coordinates of the
- // locator position in the xterm window, encoded as ASCII deci-
- // mal.
- // The ``page'' parameter is not used by xterm, and will be omit-
- // ted.
- // NOTE:
- // This is already implemented in the _bindMouse
- // method, but it might make more sense here.
- // The xterm mouse documentation says there is a
- // `<` prefix, the DECRQLP says there is no prefix.
- if (parts = /^\x1b\[(\d+(?:;\d+){4})&w/.exec(s)) {
- parts = parts[1].split(';').map(function(ch) {
- return +ch;
- });
- out.event = 'locator-position';
- out.code = 'DECRQLP';
- switch (parts[0]) {
- case 0:
- out.status = 'locator-unavailable';
- break;
- case 1:
- out.status = 'request';
- break;
- case 2:
- out.status = 'left-button-down';
- break;
- case 3:
- out.status = 'left-button-up';
- break;
- case 4:
- out.status = 'middle-button-down';
- break;
- case 5:
- out.status = 'middle-button-up';
- break;
- case 6:
- out.status = 'right-button-down';
- break;
- case 7:
- out.status = 'right-button-up';
- break;
- case 8:
- out.status = 'm4-button-down';
- break;
- case 9:
- out.status = 'm4-button-up';
- break;
- case 10:
- out.status = 'locator-outside';
- break;
- }
- out.mask = parts[1];
- out.row = parts[2];
- out.col = parts[3];
- out.page = parts[4];
- // LEGACY
- out.locatorPosition = out;
- this.emit('response', out);
- this.emit('response ' + out.event, out);
- return;
- }
- };
- Program.prototype.receive = function(text, callback) {
- var listeners = (this._events && this._events['keypress']) || []
- , bak = listeners.slice()
- , self = this;
- if (!this.input.isRaw) {
- throw new Error('Input must be raw.');
- }
- listeners.length = 0;
- if (!callback) {
- callback = text;
- text = null;
- }
- this.input.once('data', function(data) {
- listeners.push.apply(listeners, bak);
- if (typeof data !== 'string') {
- data = data.toString('utf8');
- }
- return callback(null, data);
- });
- if (text != null) {
- return this._write(text);
- }
- };
- Program.prototype.response = function(name, text, callback) {
- if (arguments.length === 2) {
- callback = text;
- text = name;
- name = null;
- }
- if (!callback) {
- callback = function() {};
- }
- this.bindResponse();
- name = name
- ? 'response ' + name
- : 'response';
- this.once(name, function(event) {
- if (event.type === 'error') {
- return callback(new Error(event.event + ': ' + event.text));
- }
- return callback(null, event);
- });
- return this._write(text);
- };
- Program.prototype._wrapCursor = function(nl) {
- if (this.tput) {
- //if (this.tput.bools.eat_newline_glitch) {
- // return;
- //}
- if (this.tput.bools.auto_right_margin) {
- this.x = 0;
- this.y++;
- return;
- }
- this.x--;
- return;
- }
- this.x = 0;
- this.y++;
- };
- // TODO: Make a private write for all of the methods here
- // which does not call _parseChar. Make public write call
- // _parseChar.
- Program.prototype._parseChar = function(text, attr) {
- for (var i = 0; i < text.length; i++) {
- var cs = false;
- while (text[i] === '\x1b') {
- i++;
- if (text[i] === '[' || text === ']') {
- cs = true;
- continue;
- }
- if (cs && (text[i] === ';' || (text[i] >= '0' && text[i] <= '9'))) {
- continue;
- }
- if (text[i] === '\\' && text[i + 1] === '\x1b') i++;
- i++;
- cs = false;
- break;
- }
- if (text[i] === '\n') {
- if (this.tput
- && this.tput.bools.eat_newline_glitch
- && this.x >= this.cols) {
- ;
- } else {
- this.x = 0;
- this.y++;
- }
- } else if (text[i] === '\r') {
- this.x = 0;
- } else {
- this.x++;
- if (this.x >= this.cols) {
- this._wrapCursor();
- }
- }
- }
- };
- Program.prototype._buffer = function(text) {
- if (this._exiting) {
- this.flush();
- this.output.write(text);
- return;
- }
- if (this._buf) {
- this._buf += text;
- return;
- }
- this._buf = text;
- process.nextTick(this._flush);
- };
- Program.prototype.flush = function(text) {
- if (!this._buf) return;
- this.output.write(this._buf);
- this._buf = '';
- };
- Program.prototype._write = function(text) {
- if (this.ret) return text;
- if (this.useBuffer) {
- return this._buffer(text);
- }
- return this.output.write(text);
- };
- Program.prototype.echo =
- Program.prototype.write = function(text, attr) {
- return attr
- ? this._write(this.text(text, attr))
- : this._write(text);
- };
- Program.prototype._ncoords = function() {
- if (this.x < 0) this.x = 0;
- else if (this.x >= this.cols) this.x = this.cols - 1;
- if (this.y < 0) this.y = 0;
- else if (this.y >= this.rows) this.y = this.rows - 1;
- };
- Program.prototype.setx = function(x) {
- return this.cursorCharAbsolute(x);
- // return this.charPosAbsolute(x);
- };
- Program.prototype.sety = function(y) {
- return this.linePosAbsolute(y);
- };
- Program.prototype.move = function(x, y) {
- return this.cursorPos(y, x);
- };
- // TODO: Fix cud and cuu calls.
- Program.prototype.omove = function(x, y) {
- if (!this.zero) {
- x = (x || 1) - 1;
- y = (y || 1) - 1;
- } else {
- x = x || 0;
- y = y || 0;
- }
- if (y === this.y && x === this.x) {
- return;
- }
- if (y === this.y) {
- if (x > this.x) {
- this.cuf(x - this.x);
- } else if (x < this.x) {
- this.cub(this.x - x);
- }
- } else if (x === this.x) {
- if (y > this.y) {
- this.cud(y - this.y);
- } else if (y < this.y) {
- this.cuu(this.y - y);
- }
- } else {
- if (!this.zero) x++, y++;
- this.cup(y, x);
- }
- };
- Program.prototype.rsetx = function(x) {
- // return this.HPositionRelative(x);
- if (!x) return;
- return x > 0
- ? this.forward(x)
- : this.back(-x);
- };
- Program.prototype.rsety = function(y) {
- // return this.VPositionRelative(y);
- if (!y) return;
- return y > 0
- ? this.up(y)
- : this.down(-y);
- };
- Program.prototype.rmove = function(x, y) {
- this.rsetx(x);
- this.rsety(y);
- };
- Program.prototype.simpleInsert = function(ch, i, attr) {
- return this._write(this.repeat(ch, i), attr);
- };
- Program.prototype.repeat = function(ch, i) {
- if (!(i >= 0)) i = 0;
- return Array(i + 1).join(ch);
- };
- /**
- * Normal
- */
- //Program.prototype.pad =
- Program.prototype.nul = function() {
- //if (this.has('pad')) return this.put.pad();
- return this._write('\200');
- };
- Program.prototype.bel =
- Program.prototype.bell = function() {
- if (this.has('bel')) return this.put.bel();
- return this._write('\x07');
- };
- Program.prototype.vtab = function() {
- this.y++;
- this._ncoords();
- return this._write('\x0b');
- };
- Program.prototype.ff =
- Program.prototype.form = function() {
- if (this.has('ff')) return this.put.ff();
- return this._write('\x0c');
- };
- Program.prototype.kbs =
- Program.prototype.backspace = function() {
- this.x--;
- this._ncoords();
- if (this.has('kbs')) return this.put.kbs();
- return this._write('\x08');
- };
- Program.prototype.ht =
- Program.prototype.tab = function() {
- this.x += 8;
- this._ncoords();
- if (this.has('ht')) return this.put.ht();
- return this._write('\t');
- };
- Program.prototype.shiftOut = function() {
- // if (this.has('S2')) return this.put.S2();
- return this._write('\x0e');
- };
- Program.prototype.shiftIn = function() {
- // if (this.has('S3')) return this.put.S3();
- return this._write('\x0f');
- };
- Program.prototype.cr =
- Program.prototype.return = function() {
- this.x = 0;
- if (this.has('cr')) return this.put.cr();
- return this._write('\r');
- };
- Program.prototype.nel =
- Program.prototype.newline =
- Program.prototype.feed = function() {
- if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.cols) {
- return;
- }
- this.x = 0;
- this.y++;
- this._ncoords();
- if (this.has('nel')) return this.put.nel();
- return this._write('\n');
- };
- /**
- * Esc
- */
- // ESC D Index (IND is 0x84).
- Program.prototype.ind =
- Program.prototype.index = function() {
- this.y++;
- this._ncoords();
- if (this.tput) return this.put.ind();
- return this._write('\x1bD');
- };
- // ESC M Reverse Index (RI is 0x8d).
- Program.prototype.ri =
- Program.prototype.reverse =
- Program.prototype.reverseIndex = function() {
- this.y--;
- this._ncoords();
- if (this.tput) return this.put.ri();
- return this._write('\x1bM');
- };
- // ESC E Next Line (NEL is 0x85).
- Program.prototype.nextLine = function() {
- this.y++;
- this.x = 0;
- this._ncoords();
- if (this.has('nel')) return this.put.nel();
- return this._write('\x1bE');
- };
- // ESC c Full Reset (RIS).
- Program.prototype.reset = function() {
- this.x = this.y = 0;
- if (this.has('rs1') || this.has('ris')) {
- return this.has('rs1')
- ? this.put.rs1()
- : this.put.ris();
- }
- return this._write('\x1bc');
- };
- // ESC H Tab Set (HTS is 0x88).
- Program.prototype.tabSet = function() {
- if (this.tput) return this.put.hts();
- return this._write('\x1bH');
- };
- // ESC 7 Save Cursor (DECSC).
- Program.prototype.sc =
- Program.prototype.saveCursor = function(key) {
- if (key) return this.lsaveCursor(key);
- this.savedX = this.x || 0;
- this.savedY = this.y || 0;
- if (this.tput) return this.put.sc();
- return this._write('\x1b7');
- };
- // ESC 8 Restore Cursor (DECRC).
- Program.prototype.rc =
- Program.prototype.restoreCursor = function(key, hide) {
- if (key) return this.lrestoreCursor(key, hide);
- this.x = this.savedX || 0;
- this.y = this.savedY || 0;
- if (this.tput) return this.put.rc();
- return this._write('\x1b8');
- };
- // Save Cursor Locally
- Program.prototype.lsaveCursor = function(key) {
- var key = key || 'local';
- this._saved = this._saved || {};
- this._saved[key] = this._saved[key] || {};
- this._saved[key].x = this.x;
- this._saved[key].y = this.y;
- this._saved[key].hidden = this.cursorHidden;
- };
- // Restore Cursor Locally
- Program.prototype.lrestoreCursor = function(key, hide) {
- var key = key || 'local', pos;
- if (!this._saved || !this._saved[key]) return;
- pos = this._saved[key];
- //delete this._saved[key];
- this.cup(pos.y, pos.x);
- if (hide && pos.hidden !== this.cursorHidden) {
- if (pos.hidden) {
- this.hideCursor();
- } else {
- this.showCursor();
- }
- }
- };
- // ESC # 3 DEC line height/width
- Program.prototype.lineHeight = function() {
- return this._write('\x1b#');
- };
- // ESC (,),*,+,-,. Designate G0-G2 Character Set.
- Program.prototype.charset = function(val, level) {
- level = level || 0;
- // See also:
- // acs_chars / acsc / ac
- // enter_alt_charset_mode / smacs / as
- // exit_alt_charset_mode / rmacs / ae
- // enter_pc_charset_mode / smpch / S2
- // exit_pc_charset_mode / rmpch / S3
- switch (level) {
- case 0:
- level = '(';
- break;
- case 1:
- level = ')';
- break;
- case 2:
- level = '*';
- break;
- case 3:
- level = '+';
- break;
- }
- var name = typeof val === 'string'
- ? val.toLowerCase()
- : val;
- switch (name) {
- case 'acs':
- case 'scld': // DEC Special Character and Line Drawing Set.
- if (this.tput) return this.put.smacs();
- val = '0';
- break;
- case 'uk': // UK
- val = 'A';
- break;
- case 'us': // United States (USASCII).
- case 'usascii':
- case 'ascii':
- if (this.tput) return this.put.rmacs();
- val = 'B';
- break;
- case 'dutch': // Dutch
- val = '4';
- break;
- case 'finnish': // Finnish
- val = 'C';
- val = '5';
- break;
- case 'french': // French
- val = 'R';
- break;
- case 'frenchcanadian': // FrenchCanadian
- val = 'Q';
- break;
- case 'german': // German
- val = 'K';
- break;
- case 'italian': // Italian
- val = 'Y';
- break;
- case 'norwegiandanish': // NorwegianDanish
- val = 'E';
- val = '6';
- break;
- case 'spanish': // Spanish
- val = 'Z';
- break;
- case 'swedish': // Swedish
- val = 'H';
- val = '7';
- break;
- case 'swiss': // Swiss
- val = '=';
- break;
- case 'isolatin': // ISOLatin (actually /A)
- val = '/A';
- break;
- default: // Default
- if (this.tput) return this.put.rmacs();
- val = 'B';
- break;
- }
- return this._write('\x1b(' + val);
- };
- Program.prototype.enter_alt_charset_mode =
- Program.prototype.as =
- Program.prototype.smacs = function() {
- return this.charset('acs');
- };
- Program.prototype.exit_alt_charset_mode =
- Program.prototype.ae =
- Program.prototype.rmacs = function() {
- return this.charset('ascii');
- };
- // 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 n
- // Invoke the G2 Character Set as GL (LS2).
- // ESC o
- // Invoke the G3 Character Set as GL (LS3).
- // ESC |
- // Invoke the G3 Character Set as GR (LS3R).
- // ESC }
- // Invoke the G2 Character Set as GR (LS2R).
- // ESC ~
- // Invoke the G1 Character Set as GR (LS1R).
- Program.prototype.setG = function(val) {
- // if (this.tput) return this.put.S2();
- // if (this.tput) return this.put.S3();
- switch (val) {
- case 1:
- val = '~'; // GR
- break;
- case 2:
- val = 'n'; // GL
- val = '}'; // GR
- val = 'N'; // Next Char Only
- break;
- case 3:
- val = 'o'; // GL
- val = '|'; // GR
- val = 'O'; // Next Char Only
- break;
- }
- return this._write('\x1b' + val);
- };
- /**
- * OSC
- */
- // OSC Ps ; Pt ST
- // OSC Ps ; Pt BEL
- // Set Text Parameters.
- Program.prototype.setTitle = function(title) {
- if (this.term('screen')) {
- // Tmux pane
- // if (process.env.TMUX) {
- // return this._write('\x1b]2;' + title + '\x1b\\');
- // }
- return this._write('\x1bk' + title + '\x1b\\');
- }
- return this._write('\x1b]0;' + title + '\x07');
- };
- // OSC Ps ; Pt ST
- // OSC Ps ; Pt BEL
- // Reset colors
- Program.prototype.resetColors = function(param) {
- if (this.has('Cr')) {
- return this.put.Cr(param);
- }
- return this._write('\x1b]112\x07');
- //return this._write('\x1b]112;' + param + '\x07');
- };
- // OSC Ps ; Pt ST
- // OSC Ps ; Pt BEL
- // Change dynamic colors
- Program.prototype.dynamicColors = function(param) {
- if (this.has('Cs')) {
- return this.put.Cs(param);
- }
- return this._write('\x1b]12;' + param + '\x07');
- };
- // OSC Ps ; Pt ST
- // OSC Ps ; Pt BEL
- // Sel data
- Program.prototype.selData = function(a, b) {
- if (this.has('Ms')) {
- return this.put.Ms(a, b);
- }
- return this._write('\x1b]52;' + a + ';' + b + '\x07');
- };
- /**
- * CSI
- */
- // CSI Ps A
- // Cursor Up Ps Times (default = 1) (CUU).
- Program.prototype.cuu =
- Program.prototype.up =
- Program.prototype.cursorUp = function(param) {
- this.y -= param || 1;
- this._ncoords();
- if (this.tput) return this.put.cuu(param);
- return this._write('\x1b[' + (param || '') + 'A');
- };
- // CSI Ps B
- // Cursor Down Ps Times (default = 1) (CUD).
- Program.prototype.cud =
- Program.prototype.down =
- Program.prototype.cursorDown = function(param) {
- this.y += param || 1;
- this._ncoords();
- if (this.tput) return this.put.cud(param);
- return this._write('\x1b[' + (param || '') + 'B');
- };
- // CSI Ps C
- // Cursor Forward Ps Times (default = 1) (CUF).
- Program.prototype.cuf =
- Program.prototype.right =
- Program.prototype.forward =
- Program.prototype.cursorForward = function(param) {
- this.x += param || 1;
- this._ncoords();
- if (this.tput) return this.put.cuf(param);
- return this._write('\x1b[' + (param || '') + 'C');
- };
- // CSI Ps D
- // Cursor Backward Ps Times (default = 1) (CUB).
- Program.prototype.cub =
- Program.prototype.left =
- Program.prototype.back =
- Program.prototype.cursorBackward = function(param) {
- this.x -= param || 1;
- this._ncoords();
- if (this.tput) return this.put.cub(param);
- return this._write('\x1b[' + (param || '') + 'D');
- };
- // CSI Ps ; Ps H
- // Cursor Position [row;column] (default = [1,1]) (CUP).
- Program.prototype.cup =
- Program.prototype.pos =
- Program.prototype.cursorPos = function(row, col) {
- if (!this.zero) {
- row = (row || 1) - 1;
- col = (col || 1) - 1;
- } else {
- row = row || 0;
- col = col || 0;
- }
- this.x = col;
- this.y = row;
- this._ncoords();
- if (this.tput) return this.put.cup(row, col);
- return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'H');
- };
- // 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.
- Program.prototype.ed =
- Program.prototype.eraseInDisplay = function(param) {
- if (this.tput) {
- switch (param) {
- case 'above':
- param = 1;
- break;
- case 'all':
- param = 2;
- break;
- case 'saved':
- param = 3;
- break;
- case 'below':
- default:
- param = 0;
- break;
- }
- // extended tput.E3 = ^[[3;J
- return this.put.ed(param);
- }
- switch (param) {
- case 'above':
- return this._write('\X1b[1J');
- case 'all':
- return this._write('\x1b[2J');
- case 'saved':
- return this._write('\x1b[3J');
- case 'below':
- default:
- return this._write('\x1b[J');
- }
- };
- Program.prototype.clear = function() {
- this.x = 0;
- this.y = 0;
- if (this.tput) return this.put.clear();
- return this._write('\x1b[H\x1b[J');
- };
- // 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.
- Program.prototype.el =
- Program.prototype.eraseInLine = function(param) {
- if (this.tput) {
- //if (this.tput.back_color_erase) ...
- switch (param) {
- case 'left':
- param = 1;
- break;
- case 'all':
- param = 2;
- break;
- case 'right':
- default:
- param = 0;
- break;
- }
- return this.put.el(param);
- }
- switch (param) {
- case 'left':
- return this._write('\x1b[1K');
- case 'all':
- return this._write('\x1b[2K');
- case 'right':
- default:
- return this._write('\x1b[K');
- }
- };
- // 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.
- Program.prototype.sgr =
- Program.prototype.attr =
- Program.prototype.charAttributes = function(param, val) {
- return this._write(this._attr(param, val));
- };
- Program.prototype.text = function(text, attr) {
- return this._attr(attr, true) + text + this._attr(attr, false);
- };
- // NOTE: sun-color may not allow multiple params for SGR.
- Program.prototype._attr = function(param, val) {
- var self = this
- , param
- , parts
- , color
- , m;
- if (Array.isArray(param)) {
- parts = param;
- param = parts[0] || 'normal';
- } else {
- param = param || 'normal';
- parts = param.split(/\s*[,;]\s*/);
- }
- if (parts.length > 1) {
- var used = {}
- , out = [];
- parts.forEach(function(part) {
- part = self._attr(part, val).slice(2, -1);
- if (part === '') return;
- if (used[part]) return;
- used[part] = true;
- out.push(part);
- });
- return '\x1b[' + out.join(';') + 'm';
- }
- if (param.indexOf('no ') === 0) {
- param = param.substring(3);
- val = false;
- } else if (param.indexOf('!') === 0) {
- param = param.substring(1);
- val = false;
- }
- switch (param) {
- // attributes
- case 'normal':
- case 'default':
- if (val === false) return '';
- return '\x1b[m';
- case 'bold':
- return val === false
- ? '\x1b[22m'
- : '\x1b[1m';
- case 'ul':
- case 'underline':
- case 'underlined':
- return val === false
- ? '\x1b[24m'
- : '\x1b[4m';
- case 'blink':
- return val === false
- ? '\x1b[25m'
- : '\x1b[5m';
- case 'inverse':
- return val === false
- ? '\x1b[27m'
- : '\x1b[7m';
- break;
- case 'invisible':
- return val === false
- ? '\x1b[28m'
- : '\x1b[8m';
- // 8-color foreground
- case 'black fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[30m';
- case 'red fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[31m';
- case 'green fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[32m';
- case 'yellow fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[33m';
- case 'blue fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[34m';
- case 'magenta fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[35m';
- case 'cyan fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[36m';
- case 'white fg':
- return val === false
- ? '\x1b[39m'
- : '\x1b[37m';
- case 'default fg':
- if (val === false) return '';
- return '\x1b[39m';
- // 8-color background
- case 'black bg':
- return val === false
- ? '\x1b[49m'
- : '\x1b[40m';
- case 'red bg':
- return val === false
- ? '\x1b[49m'
- : '…
Large files files are truncated, but you can click here to view the full file