123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- var tty = require('tty');
- var encode = require('./lib/encode');
- var Stream = require('stream').Stream;
- var exports = module.exports = function () {
- var input = null;
- function setInput (s) {
- if (input) throw new Error('multiple inputs specified')
- else input = s
- }
-
- var output = null;
- function setOutput (s) {
- if (output) throw new Error('multiple outputs specified')
- else output = s
- }
-
- for (var i = 0; i < arguments.length; i++) {
- var arg = arguments[i];
- if (!arg) continue;
- if (arg.readable) setInput(arg)
- else if (arg.stdin || arg.input) setInput(arg.stdin || arg.input)
-
- if (arg.writable) setOutput(arg)
- else if (arg.stdout || arg.output) setOutput(arg.stdout || arg.output)
-
- }
-
- if (input && typeof input.fd === 'number' && tty.isatty(input.fd)) {
- if (process.stdin.setRawMode) {
- process.stdin.setRawMode(true);
- }
- else tty.setRawMode(true);
- }
-
- var charm = new Charm;
- if (input) {
- input.pipe(charm);
- }
-
- if (output) {
- charm.pipe(output);
- }
-
- charm.once('^C', process.exit);
- charm.once('end', function () {
- if (input) {
- if (typeof input.fd === 'number' && tty.isatty(input.fd)) {
- if (process.stdin.setRawMode) {
- process.stdin.setRawMode(false);
- }
- else tty.setRawMode(false);
- }
- input.destroy();
- }
- });
- return charm;
- };
- var Charm = exports.Charm = function Charm () {
- this.writable = true;
- this.readable = true;
- this.pending = [];
- }
- Charm.prototype = new Stream;
- Charm.prototype.write = function (buf) {
- var self = this;
-
- if (self.pending.length) {
- var codes = extractCodes(buf);
- var matched = false;
-
- for (var i = 0; i < codes.length; i++) {
- for (var j = 0; j < self.pending.length; j++) {
- var cb = self.pending[j];
- if (cb(codes[i])) {
- matched = true;
- self.pending.splice(j, 1);
- break;
- }
- }
- }
-
- if (matched) return;
- }
-
- if (buf.length === 1) {
- if (buf[0] === 3) self.emit('^C');
- if (buf[0] === 4) self.emit('^D');
- }
-
- self.emit('data', buf);
-
- return self;
- };
- Charm.prototype.destroy = function () {
- this.end();
- };
- Charm.prototype.end = function (buf) {
- if (buf) this.write(buf);
- this.emit('end');
- };
- Charm.prototype.reset = function (cb) {
- this.write(encode('c'));
- return this;
- };
- Charm.prototype.position = function (x, y) {
- // get/set absolute coordinates
- if (typeof x === 'function') {
- var cb = x;
- this.pending.push(function (buf) {
- if (buf[0] === 27 && buf[1] === encode.ord('[')
- && buf[buf.length-1] === encode.ord('R')) {
- var pos = buf.toString()
- .slice(2,-1)
- .split(';')
- .map(Number)
- ;
- cb(pos[1], pos[0]);
- return true;
- }
- });
- this.write(encode('[6n'));
- }
- else {
- this.write(encode(
- '[' + Math.floor(y) + ';' + Math.floor(x) + 'f'
- ));
- }
- return this;
- };
- Charm.prototype.move = function (x, y) {
- // set relative coordinates
- var bufs = [];
-
- if (y < 0) this.up(-y)
- else if (y > 0) this.down(y)
-
- if (x > 0) this.right(x)
- else if (x < 0) this.left(-x)
-
- return this;
- };
- Charm.prototype.up = function (y) {
- if (y === undefined) y = 1;
- this.write(encode('[' + Math.floor(y) + 'A'));
- return this;
- };
- Charm.prototype.down = function (y) {
- if (y === undefined) y = 1;
- this.write(encode('[' + Math.floor(y) + 'B'));
- return this;
- };
- Charm.prototype.right = function (x) {
- if (x === undefined) x = 1;
- this.write(encode('[' + Math.floor(x) + 'C'));
- return this;
- };
- Charm.prototype.left = function (x) {
- if (x === undefined) x = 1;
- this.write(encode('[' + Math.floor(x) + 'D'));
- return this;
- };
- Charm.prototype.column = function (x) {
- this.write(encode('[' + Math.floor(x) + 'G'));
- return this;
- };
- Charm.prototype.push = function (withAttributes) {
- this.write(encode(withAttributes ? '7' : '[s'));
- return this;
- };
- Charm.prototype.pop = function (withAttributes) {
- this.write(encode(withAttributes ? '8' : '[u'));
- return this;
- };
- Charm.prototype.erase = function (s) {
- if (s === 'end' || s === '$') {
- this.write(encode('[K'));
- }
- else if (s === 'start' || s === '^') {
- this.write(encode('[1K'));
- }
- else if (s === 'line') {
- this.write(encode('[2K'));
- }
- else if (s === 'down') {
- this.write(encode('[J'));
- }
- else if (s === 'up') {
- this.write(encode('[1J'));
- }
- else if (s === 'screen') {
- this.write(encode('[1J'));
- }
- else {
- this.emit('error', new Error('Unknown erase type: ' + s));
- }
- return this;
- };
- Charm.prototype.display = function (attr) {
- var c = {
- reset : 0,
- bright : 1,
- dim : 2,
- underscore : 4,
- blink : 5,
- reverse : 7,
- hidden : 8
- }[attr];
- if (c === undefined) {
- this.emit('error', new Error('Unknown attribute: ' + attr));
- }
- this.write(encode('[' + c + 'm'));
- return this;
- };
- Charm.prototype.foreground = function (color) {
- if (typeof color === 'number') {
- if (color < 0 || color >= 256) {
- this.emit('error', new Error('Color out of range: ' + color));
- }
- this.write(encode('[38;5;' + color + 'm'));
- }
- else {
- var c = {
- black : 30,
- red : 31,
- green : 32,
- yellow : 33,
- blue : 34,
- magenta : 35,
- cyan : 36,
- white : 37
- }[color.toLowerCase()];
-
- if (!c) this.emit('error', new Error('Unknown color: ' + color));
- this.write(encode('[' + c + 'm'));
- }
- return this;
- };
- Charm.prototype.background = function (color) {
- if (typeof color === 'number') {
- if (color < 0 || color >= 256) {
- this.emit('error', new Error('Color out of range: ' + color));
- }
- this.write(encode('[48;5;' + color + 'm'));
- }
- else {
- var c = {
- black : 40,
- red : 41,
- green : 42,
- yellow : 43,
- blue : 44,
- magenta : 45,
- cyan : 46,
- white : 47
- }[color.toLowerCase()];
-
- if (!c) this.emit('error', new Error('Unknown color: ' + color));
- this.write(encode('[' + c + 'm'));
- }
- return this;
- };
- Charm.prototype.cursor = function (visible) {
- this.write(encode(visible ? '[?25h' : '[?25l'));
- return this;
- };
- var extractCodes = exports.extractCodes = function (buf) {
- var codes = [];
- var start = -1;
-
- for (var i = 0; i < buf.length; i++) {
- if (buf[i] === 27) {
- if (start >= 0) codes.push(buf.slice(start, i));
- start = i;
- }
- else if (start >= 0 && i === buf.length - 1) {
- codes.push(buf.slice(start));
- }
- }
-
- return codes;
- }
|