terminal-kit
Advanced tools
Comparing version 0.0.8 to 0.1.1
@@ -1,17 +0,6 @@ | ||
[34mToto | ||
Tata | ||
[31mTiti[0m Tete | ||
# TOC | ||
- [colors](#colors) | ||
- [Find](#find) | ||
<a name=""></a> | ||
<a name="colors"></a> | ||
# colors | ||
should. | ||
```js | ||
console.log( term.BLUE + 'Toto' ) ; | ||
console.log( 'Tata' ) ; | ||
console.log( term.red( 'Titi' ) + ' Tete' ) ; | ||
``` | ||
<a name="find"></a> | ||
# Find |
// Mini formater | ||
/* | ||
%s string | ||
%f float | ||
%d integer | ||
%i integer, same as %d | ||
%u unsigned integer | ||
%U unsigned positive integer (>0) | ||
%x hexadecimal | ||
%h hexadecimal, same as %x | ||
%o octal | ||
%b binary | ||
%D drop | ||
%[ filter function existing in the 'this' context, e.g. %[filter:%a%a] | ||
%a argument for a function | ||
Candidate format: | ||
%c for char? (can receive a string or an integer translated into an UTF8 chars) | ||
%C for currency formating? | ||
%O for object? (using JSON.stringify() ?) | ||
%B for Buffer objects? | ||
%e for scientific notation? | ||
*/ | ||
function format( str ) | ||
@@ -8,41 +31,113 @@ { | ||
{ | ||
if ( str && typeof str === 'object' && typeof str.toString === 'function' ) { str = str.toString() ; } | ||
if ( str === null || str === undefined ) { return '' ; } | ||
else if ( /*str && typeof str === 'object' && */ typeof str.toString === 'function' ) { str = str.toString() ; } | ||
else { return '' ; } | ||
} | ||
var arg , autoIndex = 1 , args = arguments , length = arguments.length ; | ||
var arg , autoIndex = 1 , args = arguments , length = arguments.length , self = this ; | ||
//console.log( 'format args:' , arguments ) ; | ||
//str = str.replace( /%[siuUd%]/g , function( match ) { | ||
str = str.replace( /%([0-9]*)([siuUd%])/g , function( match , index , mode ) { | ||
// /!\ each changes here should be reported on format.count() too /!\ | ||
str = str.replace( /%([+-]?)([0-9]*)(([a-zA-Z%])|\[([a-zA-Z0-9_]+)(:([^\]]*))?\])/g , | ||
function( match , relative , index , trash , mode , fn , trash2 , argString ) { | ||
//console.log( 'replaceArgs:' , arguments , 'MATCH:' , match ) ; | ||
if ( mode === '%' ) { return '%'; } | ||
if ( ! index ) { index = autoIndex ; } | ||
if ( index >= length ) { return '' ; } | ||
arg = args[ index ] ; | ||
autoIndex ++ ; | ||
switch ( mode ) | ||
{ | ||
case 's': | ||
if ( typeof arg === 'string' ) { return arg ; } | ||
if ( typeof arg === 'number' ) { return '' + arg ; } | ||
if ( typeof arg.toString === 'function' ) { return arg.toString() ; } | ||
return '' ; | ||
case 'd': | ||
if ( typeof arg === 'number' ) { return '' + arg ; } | ||
return 0 ; | ||
case 'i': | ||
if ( typeof arg === 'number' ) { return '' + Math.floor( arg ) ; } | ||
return 0 ; | ||
case 'u': | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ) ; } | ||
return 0 ; | ||
case 'U': | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 1 ) ; } | ||
return 1 ; | ||
} | ||
//console.log( 'replaceArgs:' , arguments , 'MATCH:' , match ) ; | ||
if ( mode === '%' ) { return '%'; } | ||
if ( fn ) | ||
{ | ||
var i , argMatches , argList = [] ; | ||
if ( argString && ( argMatches = argString.match( /%([+-]?)([0-9]*)[a-zA-Z]/g ) ) ) | ||
{ | ||
//console.log( argMatches ) ; | ||
//console.log( argString ) ; | ||
for ( i = 0 ; i < argMatches.length ; i ++ ) | ||
{ | ||
relative = argMatches[ i ][ 1 ] ; | ||
index = argMatches[ i ][ 2 ] ; | ||
if ( index ) | ||
{ | ||
index = parseInt( index ) ; | ||
if ( relative ) | ||
{ | ||
if ( relative === '+' ) { index = autoIndex + index ; } | ||
else if ( relative === '-' ) { index = autoIndex - index ; } | ||
} | ||
} | ||
else | ||
{ | ||
index = autoIndex ; | ||
} | ||
autoIndex ++ ; | ||
if ( index >= length || index < 1 ) { argList[ i ] = undefined ; } | ||
else { argList[ i ] = args[ index ] ; } | ||
} | ||
} | ||
//console.log( '~F~' ) ; | ||
if ( typeof self[ fn ] !== 'function' ) { return '' ; } | ||
return self[ fn ].apply( self , argList ) ; | ||
} | ||
if ( index ) | ||
{ | ||
index = parseInt( index ) ; | ||
if ( relative ) | ||
{ | ||
if ( relative === '+' ) { index = autoIndex + index ; } | ||
else if ( relative === '-' ) { index = autoIndex - index ; } | ||
} | ||
} | ||
else | ||
{ | ||
index = autoIndex ; | ||
} | ||
autoIndex ++ ; | ||
if ( index >= length || index < 1 ) { return '' ; } | ||
arg = args[ index ] ; | ||
switch ( mode ) | ||
{ | ||
case 's' : // string | ||
if ( arg === null || arg === undefined ) { return '' ; } | ||
if ( typeof arg === 'string' ) { return arg ; } | ||
if ( typeof arg === 'number' ) { return '' + arg ; } | ||
if ( typeof arg.toString === 'function' ) { return arg.toString() ; } | ||
return '' ; | ||
case 'f' : // float | ||
if ( typeof arg === 'number' ) { return '' + arg ; } | ||
return 0 ; | ||
case 'd' : | ||
case 'i' : // integer decimal | ||
if ( typeof arg === 'number' ) { return '' + Math.floor( arg ) ; } | ||
return 0 ; | ||
case 'u' : // unsigned decimal | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ) ; } | ||
return 0 ; | ||
case 'U' : // unsigned positive decimal | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 1 ) ; } | ||
return 1 ; | ||
case 'x' : | ||
case 'h' : // unsigned hexadecimal | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 16 ) ; } | ||
return 0 ; | ||
case 'o' : // unsigned hexadecimal | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 8 ) ; } | ||
return 0 ; | ||
case 'b' : // unsigned binary | ||
if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 2 ) ; } | ||
return 0 ; | ||
default : | ||
return '' ; | ||
} | ||
} ) ; | ||
@@ -53,8 +148,9 @@ | ||
arg = args[ autoIndex ] ; | ||
if ( typeof arg === 'string' ) { str += arg ; } | ||
if ( typeof arg === 'number' ) { str += arg ; } | ||
if ( typeof arg.toString === 'function' ) { str += arg.toString() ; } | ||
if ( arg === null || arg === undefined ) { continue ; } | ||
else if ( typeof arg === 'string' ) { str += arg ; } | ||
else if ( typeof arg === 'number' ) { str += arg ; } | ||
else if ( typeof arg.toString === 'function' ) { str += arg.toString() ; } | ||
} | ||
return str; | ||
return str ; | ||
} | ||
@@ -69,4 +165,4 @@ | ||
// This regex differs slightly from the main regex: we do not count '%%' | ||
var matches = str.match( /%[0-9]*[siuUd]/g ) ; | ||
// This regex differs slightly from the main regex: we do not count '%%' and '%[' | ||
var matches = str.match( /%[+-]?[0-9]*[a-zA-Z]/g ) ; | ||
if ( ! matches ) { return 0 ; } | ||
@@ -78,4 +174,13 @@ else { return matches.length ; } | ||
// From Mozilla Developper Network | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions | ||
format.escapeRegExp = function escapeRegExp( str ) | ||
{ | ||
return str.replace( /([.*+?^${}()|\[\]\/\\])/g , '\\$1' ) ; | ||
} ; | ||
module.exports = format ; | ||
@@ -30,161 +30,191 @@ /* | ||
// Load modules | ||
var format = require( './format' ) ; | ||
var tree = require( 'tree-kit' ) ; | ||
var async = require( 'async-kit' ) ; | ||
var tty = require( 'tty' ) ; | ||
var format = require( './format' ) ; | ||
var punycode = require( 'punycode' ) ; | ||
// This module allow to get TTY even if the program is piped. | ||
// However, it performs some actions (opening files, etc...) so it's better to keep it commented until used appropriately. | ||
// Also, since the code is really short, it could be wise to just copy/paste it somewhere. | ||
// var ttys = require( 'ttys' ) ; | ||
var chainable = {} ; | ||
var term = Object.create( chainable ) ; | ||
module.exports = term ; | ||
/* STDIN / STDOUT / STDERR */ | ||
term.stdin = process.stdin ; | ||
term.stdout = process.stdout ; | ||
term.stderr = process.stderr ; | ||
term.couldTTY = true ; | ||
/* Escape sequences */ | ||
term.esc = {} ; | ||
// Mini-doc: | ||
// ESC = \x1b | ||
// CSI = ESC + [ | ||
// OSC = ESC + ] | ||
// DSC = ESC + P | ||
// ST = ESC + \ (end some sequences) | ||
// CSI: ESC + [ + <command> + <type> | ||
// It is possible to separate many command with a ';' before the final 'type'. | ||
// See: http://en.wikipedia.org/wiki/ANSI_escape_code | ||
// and: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html | ||
// man tput | ||
// man 5 terminfo | ||
/* Common sequences */ | ||
var commonEsc = {} ; | ||
// Remove colors | ||
commonEsc.noColor = "\x1b[39m" ; // back to the default color, most of time it is the same than .white | ||
commonEsc.noBgColor = "\x1b[49m" ; // back to the default color, most of time it is the same than .bgBlack | ||
term.esc = { | ||
function createTerminal( createOptions ) | ||
{ | ||
// Manage createOptions | ||
if ( ! createOptions ) | ||
{ | ||
createOptions = { | ||
stdin: process.stdin , | ||
stdout: process.stdout , | ||
stderr: process.stderr , | ||
type: 'xterm' | ||
// couldTTY: true | ||
} ; | ||
} | ||
/* Control sequences */ | ||
// Reset the terminal | ||
reset: { on: "\x1bc" } , | ||
if ( typeof createOptions.type !== 'string' ) { createOptions.type = 'xterm' ; } | ||
// Cursors | ||
moveToLowerLeft: { on: "\x1bF" } , | ||
saveCursor: { on: "\x1b7" } , | ||
restoreCursor: { on: "\x1b8" } , | ||
var termconfig ; | ||
var chainable = Object.create( notChainable ) ; | ||
var options = { on: '', off: '', params: 0, err: false, out: createOptions.stdout } ; | ||
up: { on: '\x1b[%uA' } , | ||
down: { on: '\x1b[%uB' } , | ||
right: { on: '\x1b[%uC' } , | ||
left: { on: '\x1b[%uD' } , | ||
moveTo: { on: '\x1b[%2U;%1UH' } , | ||
var term = applyEscape.bind( undefined , options ) ; | ||
// Emit a beep | ||
beep: { on: "\x07" } , | ||
/* Style sequences */ | ||
styleReset: { on: "\x1b[0m" } , | ||
// Yay, this is a nasty hack... | ||
term.__proto__ = chainable ; // jshint ignore:line | ||
term.apply = Function.prototype.apply ; | ||
bold: { on: "\x1b[1m" , off: "\x1b[21m" } , | ||
dim: { on: "\x1b[2m" , off: "\x1b[22m" } , // dim: darker, 'off' remove removes also bold/bright | ||
italic: { on: "\x1b[3m" , off: "\x1b[23m" } , | ||
underline: { on: "\x1b[4m" , off: "\x1b[24m" } , | ||
blink: { on: "\x1b[5m" , off: "\x1b[25m" } , | ||
inverse: { on: "\x1b[7m" , off: "\x1b[27m" } , | ||
hidden: { on: "\x1b[8m" , off: "\x1b[28m" } , // invisible, but can be copy/paste'd | ||
strike: { on: "\x1b[9m" , off: "\x1b[29m" } , | ||
// Fix the root | ||
options.root = term ; | ||
term.root = term ; | ||
// Foreground color | ||
black: { on: "\x1b[30m" , off: commonEsc.noColor } , | ||
red: { on: "\x1b[31m" , off: commonEsc.noColor } , | ||
green: { on: "\x1b[32m" , off: commonEsc.noColor } , | ||
yellow: { on: "\x1b[33m" , off: commonEsc.noColor } , | ||
blue: { on: "\x1b[34m" , off: commonEsc.noColor } , | ||
magenta: { on: "\x1b[35m" , off: commonEsc.noColor } , | ||
cyan: { on: "\x1b[36m" , off: commonEsc.noColor } , | ||
white: { on: "\x1b[37m" , off: commonEsc.noColor } , | ||
brightBlack: { on: "\x1b[90m" , off: commonEsc.noColor } , | ||
brightRed: { on: "\x1b[91m" , off: commonEsc.noColor } , | ||
brightGreen: { on: "\x1b[92m" , off: commonEsc.noColor } , | ||
brightYellow: { on: "\x1b[93m" , off: commonEsc.noColor } , | ||
brightBlue: { on: "\x1b[94m" , off: commonEsc.noColor } , | ||
brightMagenta: { on: "\x1b[95m" , off: commonEsc.noColor } , | ||
brightCyan: { on: "\x1b[96m" , off: commonEsc.noColor } , | ||
brightWhite: { on: "\x1b[97m" , off: commonEsc.noColor } , | ||
// Background color | ||
bgBlack: { on: "\x1b[40m" , off: commonEsc.noBgColor } , | ||
bgRed: { on: "\x1b[41m" , off: commonEsc.noBgColor } , | ||
bgGreen: { on: "\x1b[42m" , off: commonEsc.noBgColor } , | ||
bgYellow: { on: "\x1b[43m" , off: commonEsc.noBgColor } , | ||
bgBlue: { on: "\x1b[44m" , off: commonEsc.noBgColor } , | ||
bgMagenta: { on: "\x1b[45m" , off: commonEsc.noBgColor } , | ||
bgCyan: { on: "\x1b[46m" , off: commonEsc.noBgColor } , | ||
bgWhite: { on: "\x1b[47m" , off: commonEsc.noBgColor } , | ||
bgBrightBlack: { on: "\x1b[100m" , off: commonEsc.noBgColor } , | ||
bgBrightRed: { on: "\x1b[101m" , off: commonEsc.noBgColor } , | ||
bgBrightGreen: { on: "\x1b[102m" , off: commonEsc.noBgColor } , | ||
bgBrightYellow: { on: "\x1b[103m" , off: commonEsc.noBgColor } , | ||
bgBrightBlue: { on: "\x1b[104m" , off: commonEsc.noBgColor } , | ||
bgBrightMagenta: { on: "\x1b[105m" , off: commonEsc.noBgColor } , | ||
bgBrightCyan: { on: "\x1b[106m" , off: commonEsc.noBgColor } , | ||
bgBrightWhite: { on: "\x1b[107m" , off: commonEsc.noBgColor } , | ||
term.createTerminal = createTerminal ; | ||
term.options = options ; | ||
term.stdin = createOptions.stdin ; | ||
term.stdout = createOptions.stdout ; | ||
term.stderr = createOptions.stderr ; | ||
term.grabbing = false ; | ||
/* Input / Output sequences */ | ||
// Screen size | ||
term.width = undefined ; | ||
term.height = undefined ; | ||
term.updateScreenSize() ; | ||
if ( term.stdout.isTTY ) { term.stdout.on( 'resize' , term.updateScreenSize.bind( term ) ) ; } | ||
else if ( createOptions.processSigwinch ) { process.on( 'SIGWINCH' , term.updateScreenSize.bind( term ) ) ; } | ||
// Terminal will send the cursor coordinate | ||
cursor: { on: "\x1b[?6n" , off: '' } , | ||
//term.couldTTY = true ; | ||
// Terminal will send button event and mouse position | ||
mouseClick: { on: "\x1b[?1000h" , off: "\x1b[?1000l" } , | ||
try { | ||
termconfig = require( './termconfig/' + createOptions.type + '.js' ) ; | ||
} | ||
catch ( error ) { | ||
termconfig = require( './termconfig/xterm.js' ) ; | ||
} | ||
// Terminal will send position of the column hilighted | ||
mouseHilight: { on: "\x1b[?1001h" , off: "\x1b[?1001l" } , | ||
// if needed, this should be replaced by some tput commands? | ||
term.esc = tree.extend( null , {} , termconfig.esc , pseudoEsc ) ; | ||
term.escHandler = { root: term } ; | ||
term.keymap = tree.extend( null , {} , termconfig.keymap ) ; | ||
// ? | ||
mouseCell: { on: "\x1b[?1002h" , off: "\x1b[?1002l" } , | ||
// reverse keymap | ||
term.rKeymap = [] ; | ||
term.rKeymapMaxSize = -1 ; | ||
term.rKeymapStarter = [] ; | ||
term.rKeymapStarterMaxSize = -1 ; | ||
// Terminal will send all motion | ||
mouseMotion: { on: "\x1b[?1003h" , off: "\x1b[?1003l" } , | ||
Object.keys( term.keymap ).forEach( function( key ) { | ||
var i , j , keymapObject , code , codeList = term.keymap[ key ] ; | ||
if ( ! Array.isArray( codeList ) ) { codeList = [ codeList ] ; } | ||
for ( j = 0 ; j < codeList.length ; j ++ ) | ||
{ | ||
code = codeList[ j ] ; | ||
if ( typeof code === 'object' ) | ||
{ | ||
keymapObject = code ; | ||
keymapObject.name = key ; | ||
code = keymapObject.code ; | ||
} | ||
else | ||
{ | ||
keymapObject = { | ||
code: code , | ||
name: key , | ||
matches: [ key ] | ||
} ; | ||
} | ||
if ( code ) | ||
{ | ||
if ( code.length > term.rKeymapMaxSize ) | ||
{ | ||
for ( i = term.rKeymapMaxSize + 1 ; i <= code.length ; i ++ ) { term.rKeymap[ i ] = {} ; } | ||
term.rKeymapMaxSize = code.length ; | ||
} | ||
if ( term.rKeymap[ code.length ][ code ] ) | ||
{ | ||
term.rKeymap[ code.length ][ code ].matches.push( key ) ; | ||
} | ||
else | ||
{ | ||
term.rKeymap[ code.length ][ code ] = keymapObject ; | ||
term.rKeymap[ code.length ][ code ].matches = [ key ] ; | ||
} | ||
} | ||
else | ||
{ | ||
if ( ! keymapObject.starter || ! keymapObject.ender || ! keymapObject.handler ) { continue ; } | ||
if ( keymapObject.starter.length > term.rKeymapStarterMaxSize ) | ||
{ | ||
for ( i = term.rKeymapStarterMaxSize + 1 ; i <= keymapObject.starter.length ; i ++ ) { term.rKeymapStarter[ i ] = {} ; } | ||
term.rKeymapStarterMaxSize = keymapObject.starter.length ; | ||
} | ||
if ( term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ] ) | ||
{ | ||
term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ].push( key ) ; | ||
} | ||
else | ||
{ | ||
term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ] = [ keymapObject ] ; | ||
} | ||
} | ||
} | ||
} ) ; | ||
// Do not work... | ||
noecho: { on: "\x1b[12h" } , | ||
/* OSC - OS Control sequences: may be unavailable on some context */ | ||
// Create methods for the 'chainable' prototype | ||
// Set the title of an xterm-compatible window | ||
windowTitle: { on: '\x1b]0;%s\x1b\\' } , | ||
Object.keys( term.esc ).forEach( function( key ) { | ||
// build-time resolution | ||
if ( typeof term.esc[ key ].on === 'function' ) { term.esc[ key ].on = term.esc[ key ].on.call( term ) ; } | ||
if ( typeof term.esc[ key ].off === 'function' ) { term.esc[ key ].off = term.esc[ key ].off.call( term ) ; } | ||
// dynamic handler | ||
if ( term.esc[ key ].handler ) { term.escHandler[ key ] = term.esc[ key ].handler ; } | ||
Object.defineProperty( chainable , key , { | ||
configurable: true , | ||
get: function () { | ||
var fn , options = {} ; | ||
options = tree.extend( null , {} , this.options ) ; | ||
options.on += this.root.esc[ key ].on ; | ||
options.off = ( this.root.esc[ key ].off || '' ) + options.off ; | ||
options.params += format.count( this.root.esc[ key ].on ) ; | ||
if ( this.root.esc[ key ].err ) { options.out = this.root.stderr ; } | ||
fn = applyEscape.bind( undefined , options ) ; | ||
// Yay, this is a nasty hack... | ||
fn.__proto__ = chainable ; // jshint ignore:line | ||
fn.apply = Function.prototype.apply ; | ||
fn.root = this.root || this ; | ||
fn.options = options ; | ||
// Replace the getter by the newly created function, to speed up further call | ||
Object.defineProperty( this , key , { value: fn } ) ; | ||
//console.log( 'Create function:' , key ) ; | ||
return fn ; | ||
} | ||
} ) ; | ||
} ) ; | ||
// Dev tests for new escape sequences discoveries | ||
test: { on: "\x1b[12h" } | ||
} ; | ||
return term ; | ||
} | ||
@@ -195,3 +225,2 @@ | ||
/* Apply */ | ||
@@ -201,18 +230,20 @@ | ||
function applyEscape( chainOn , chainOff , chainParams ) | ||
// CAUTION: 'options' MUST NOT BE OVERWRITTEN! | ||
// It is binded at the function creation and contains function specificities! | ||
function applyEscape( options ) | ||
{ | ||
var formatParams ; | ||
var formatParams , on = options.on ; | ||
// If not enough arguments, return right now | ||
if ( arguments.length < 3 + chainParams ) { return this ; } | ||
if ( arguments.length < 1 + options.params ) { return options.root ; } | ||
var action = arguments[ 3 + chainParams ] ; | ||
var action = arguments[ 1 + options.params ] ; | ||
if ( chainParams ) | ||
if ( options.params ) | ||
{ | ||
formatParams = Array.prototype.slice.call( arguments , 3 , 3 + chainParams ) ; | ||
formatParams.unshift( chainOn ) ; | ||
formatParams = Array.prototype.slice.call( arguments , 1 , 1 + options.params ) ; | ||
formatParams.unshift( options.on ) ; | ||
//console.log( '\napplyEscape arguments' , arguments ) ; | ||
//console.log( '\napplyEscape formatParams:' , formatParams ) ; | ||
chainOn = format.apply( undefined , formatParams ) ; | ||
on = format.apply( options.root.escHandler , formatParams ) ; | ||
} | ||
@@ -223,4 +254,4 @@ | ||
{ | ||
this.stdout.write( chainOn ) ; | ||
return this ; | ||
options.out.write( on ) ; | ||
return options.root ; | ||
} | ||
@@ -230,4 +261,4 @@ | ||
{ | ||
this.stdout.write( chainOff ) ; | ||
return this ; | ||
options.out.write( options.off ) ; | ||
return options.root ; | ||
} | ||
@@ -237,11 +268,4 @@ | ||
{ | ||
if ( typeof action === 'object' && typeof action.toString === 'function' ) | ||
{ | ||
action = action.toString() ; | ||
} | ||
else | ||
{ | ||
// Cannot be stringified? so return now! | ||
return this ; | ||
} | ||
if ( typeof action.toString === 'function' ) { action = action.toString() ; } | ||
else { action = '' ; } | ||
} | ||
@@ -251,13 +275,13 @@ | ||
if ( arguments.length > 4 ) | ||
if ( arguments.length > 2 ) | ||
{ | ||
formatParams = Array.prototype.slice.call( arguments , 3 + chainParams ) ; | ||
this.stdout.write( chainOn + format.apply( undefined , formatParams ) + chainOff ) ; | ||
formatParams = Array.prototype.slice.call( arguments , 1 + options.params ) ; | ||
options.out.write( on + format.apply( options.root.escHandler , formatParams ) + options.off ) ; | ||
} | ||
else | ||
{ | ||
this.stdout.write( chainOn + action + chainOff ) ; | ||
options.out.write( on + action + options.off ) ; | ||
} | ||
return this ; | ||
return options.root ; | ||
} | ||
@@ -267,34 +291,288 @@ | ||
// Create methods for the 'chainable' prototype | ||
Object.keys( term.esc ).forEach( function( key ) { | ||
/* Pseudo esc */ | ||
var pseudoEsc = { | ||
move: { on: '%[move:%a%a]' , | ||
handler: function move( x , y ) { | ||
var sequence = '' ; | ||
if ( x ) | ||
{ | ||
if ( x > 0 ) { sequence += format( this.root.esc.right.on , x ) ; } | ||
else { sequence += format( this.root.esc.left.on , -x ) ; } | ||
} | ||
if ( y ) | ||
{ | ||
if ( y > 0 ) { sequence += format( this.root.esc.down.on , y ) ; } | ||
else { sequence += format( this.root.esc.up.on , -y ) ; } | ||
} | ||
return sequence ; | ||
} | ||
} , | ||
Object.defineProperty( chainable , key , { | ||
configurable: true , | ||
get: function () { | ||
var fn , chainOn , chainOff , chainParams ; | ||
color: { on: '%[color:%a]' , | ||
off: function() { return this.root.esc.defaultColor.on ; } , | ||
handler: function color( c ) | ||
{ | ||
if ( typeof c !== 'number' ) { return '' ; } | ||
if ( this.chainOn ) { chainOn = this.chainOn + term.esc[ key ].on ; } | ||
else { chainOn = term.esc[ key ].on ; } | ||
c = Math.floor( c ) ; | ||
if ( this.chainOff ) { chainOff = ( term.esc[ key ].off || '' ) + this.chainOff ; } | ||
else { chainOff = term.esc[ key ].off || '' ; } | ||
if ( c < 0 || c > 15 ) { return '' ; } | ||
if ( this.chainParams ) { chainParams = this.chainParams + format.count( term.esc[ key ].on ) ; } | ||
else { chainParams = format.count( term.esc[ key ].on ) ; } | ||
if ( c <= 7 ) { return format( this.root.esc.darkColor.on , c ) ; } | ||
else { return format( this.root.esc.brightColor.on , c - 8 ) ; } | ||
} | ||
} , | ||
bgColor: { on: '%[bgColor:%a]' , | ||
off: function() { return this.root.esc.bgDefaultColor.on ; } , | ||
handler: function bgColor( c ) | ||
{ | ||
if ( typeof c !== 'number' ) { return '' ; } | ||
fn = applyEscape.bind( this.root || this , chainOn , chainOff , chainParams ) ; | ||
fn.__proto__ = chainable ; | ||
fn.chainOn = chainOn ; | ||
fn.chainOff = chainOff ; | ||
fn.chainParams = chainParams ; | ||
fn.root = this.root || this ; | ||
c = Math.floor( c ) ; | ||
// Replace the getter by the newly created function, to speed up further call | ||
Object.defineProperty( this , key , { value: fn } ) ; | ||
//console.log( ' Getter called:' , key ) ; | ||
if ( c < 0 || c > 15 ) { return '' ; } | ||
return fn ; | ||
if ( c <= 7 ) { return format( this.root.esc.bgDarkColor.on , c ) ; } | ||
else { return format( this.root.esc.bgBrightColor.on , c - 8 ) ; } | ||
} | ||
} ) ; | ||
} | ||
} ; | ||
/* Advanced methods */ | ||
// Complexes functions that cannot be chained. | ||
// It is the ancestors of the terminal object, so it should inherit from async.EventEmitter. | ||
var notChainable = Object.create( async.EventEmitter.prototype ) ; | ||
notChainable.fullscreen = function fullscreen( options ) | ||
{ | ||
if ( options === false ) | ||
{ | ||
// Disable fullscrenn mode | ||
this.alternateScreenBuffer( false ) ; | ||
return this ; | ||
} | ||
if ( ! options ) { options = {} ; } | ||
if ( ! options.noAlternate ) { this.alternateScreenBuffer( true ) ; } | ||
this.clear() ; | ||
} ; | ||
notChainable.updateScreenSize = function updateScreenSize() | ||
{ | ||
if ( this.stdout.getWindowSize ) | ||
{ | ||
var windowSize = this.stdout.getWindowSize() ; | ||
this.width = windowSize[ 0 ] ; | ||
this.height = windowSize[ 1 ] ; | ||
} | ||
this.emit( 'terminal' , 'SCREEN_RESIZE' , { resized: true , width: this.width , height: this.height } ) ; | ||
} ; | ||
/* Input management */ | ||
function onStdin( chunk ) | ||
{ | ||
var i , j , buffer , startBuffer , char , codepoint , | ||
keymapCode , keymapStartCode , keymap , keymapList , | ||
regexp , matches , bytes , found , handlerResult , | ||
index = 0 , length = chunk.length ; | ||
while ( index < length ) | ||
{ | ||
found = false ; | ||
bytes = 1 ; | ||
if ( chunk[ index ] <= 0x1f || chunk[ index ] === 0x7f ) | ||
{ | ||
// Those are ASCII control character and DEL key | ||
for ( i = Math.min( length , Math.max( this.rKeymapMaxSize , this.rKeymapStarterMaxSize ) ) ; i > 0 ; i -- ) | ||
{ | ||
buffer = chunk.slice( index ) ; | ||
keymapCode = buffer.toString() ; | ||
startBuffer = chunk.slice( index , index + i ) ; | ||
keymapStartCode = startBuffer.toString() ; | ||
if ( this.rKeymap[ i ] && this.rKeymap[ i ][ keymapStartCode ] ) | ||
{ | ||
// First test fixed sequences | ||
keymap = this.rKeymap[ i ][ keymapStartCode ] ; | ||
found = true ; | ||
if ( keymap.handler ) | ||
{ | ||
handlerResult = keymap.handler.call( this , keymap.name , chunk.slice( index + i ) ) ; | ||
bytes = i + handlerResult.eaten ; | ||
this.emit( keymap.event , handlerResult.name , handlerResult.data ) ; | ||
} | ||
else if ( keymap.event ) | ||
{ | ||
bytes = i ; | ||
this.emit( keymap.event , keymap.name , keymap.data , { code: startBuffer } ) ; | ||
} | ||
else | ||
{ | ||
bytes = i ; | ||
this.emit( 'key' , keymap.name , keymap.matches , { code: startBuffer } ) ; | ||
} | ||
break ; | ||
} | ||
else if ( this.rKeymapStarter[ i ] && this.rKeymapStarter[ i ][ keymapStartCode ] ) | ||
{ | ||
// Then test pattern sequences | ||
keymapList = this.rKeymapStarter[ i ][ keymapStartCode ] ; | ||
//console.log( 'for i:' , keymapList ) ; | ||
for ( j = 0 ; j < keymapList.length ; j ++ ) | ||
{ | ||
keymap = keymapList[ j ] ; | ||
regexp = '^' + | ||
format.escapeRegExp( keymap.starter ) + | ||
'([0-9;]*)' + | ||
format.escapeRegExp( keymap.ender ) ; | ||
matches = keymapCode.match( new RegExp( regexp ) , 'g' ) ; | ||
//console.log( 'for j:' , keymap , regexp , matches ) ; | ||
if ( matches ) | ||
{ | ||
found = true ; | ||
handlerResult = keymap.handler.call( this , keymap.name , matches[ 1 ] ) ; | ||
bytes = matches[ 0 ].length ; | ||
this.emit( keymap.event , handlerResult.name , handlerResult.data ) ; | ||
break ; | ||
} | ||
} | ||
if ( found ) { break ; } | ||
} | ||
} | ||
// Nothing was found, so to not emit trash, we just abort the current buffer processing | ||
if ( ! found ) { this.emit( 'unknown' , chunk ) ; return ; } | ||
} | ||
else if ( chunk[ index ] >= 0x80 ) | ||
{ | ||
// Unicode bytes per char guessing | ||
if ( chunk[ index ] < 0xc0 ) { continue ; } // We are in a middle of an unicode multibyte sequence... Something fails somewhere, we will just continue for now... | ||
else if ( chunk[ index ] < 0xe0 ) { bytes = 2 ; } | ||
else if ( chunk[ index ] < 0xf0 ) { bytes = 3 ; } | ||
else if ( chunk[ index ] < 0xf8 ) { bytes = 4 ; } | ||
else if ( chunk[ index ] < 0xfc ) { bytes = 5 ; } | ||
else { bytes = 6 ; } | ||
buffer = chunk.slice( index , index.bytes ) ; | ||
char = buffer.toString( 'utf8' ) ; | ||
if ( bytes > 2 ) { codepoint = punycode.ucs2.decode( char )[ 0 ] ; } | ||
else { codepoint = char.charCodeAt( 0 ) ; } | ||
this.emit( 'key' , char , [ char ] , { codepoint: codepoint , code: buffer } ) ; | ||
} | ||
else | ||
{ | ||
// Standard ASCII | ||
char = String.fromCharCode( chunk[ index ] ) ; | ||
this.emit( 'key' , char , [ char ] , { codepoint: chunk[ index ] , code: chunk[ index ] } ) ; | ||
} | ||
index += bytes ; | ||
} | ||
} | ||
notChainable.grabInput = function grabInput( options ) | ||
{ | ||
if ( ! this.onStdin ) { this.onStdin = onStdin.bind( this ) ; } | ||
// RESET | ||
this.stdout.write( | ||
this.esc.mouseButton.off + | ||
this.esc.mouseDrag.off + | ||
this.esc.mouseMotion.off + | ||
this.esc.mouseSGR.off + | ||
this.esc.focusEvent.off | ||
) ; | ||
if ( options === false ) | ||
{ | ||
// Disable grabInput mode | ||
this.stdin.removeListener( 'data' , this.onStdin ) ; | ||
this.stdin.setRawMode( false ) ; | ||
return this ; | ||
} | ||
if ( ! options ) { options = {} ; } | ||
// SET | ||
this.stdin.setRawMode( true ) ; | ||
this.stdin.on( 'data' , this.onStdin ) ; | ||
if ( options.mouse ) | ||
{ | ||
switch ( options.mouse ) | ||
{ | ||
case 'button' : this.stdout.write( this.esc.mouseButton.on + this.esc.mouseSGR.on ) ; break ; | ||
case 'drag' : this.stdout.write( this.esc.mouseDrag.on + this.esc.mouseSGR.on ) ; break ; | ||
case 'motion' : this.stdout.write( this.esc.mouseMotion.on + this.esc.mouseSGR.on ) ; break ; | ||
} | ||
} | ||
if ( options.focus ) { this.stdout.write( this.esc.focusEvent.on ) ; } | ||
return this ; | ||
} ; | ||
module.exports = createTerminal( { | ||
stdin: process.stdin , | ||
stdout: process.stdout , | ||
stderr: process.stderr , | ||
type: process.env.TERM , | ||
processSigwinch: true | ||
// couldTTY: true | ||
} ) ; | ||
@@ -304,2 +582,8 @@ | ||
/* | ||
// Code from 'cli-color' | ||
@@ -319,33 +603,7 @@ Object.defineProperties( term , { | ||
function positiveOrZero( n ) { return isNaN( n ) ? 0 : Math.max( Math.floor( n ) , 0 ) ; } | ||
function positive( n ) { return isNaN( n ) ? 1 : Math.max( Math.floor( n ) , 1 ) ; } | ||
function floor( n ) { return isNaN( n ) ? 0 : Math.floor( n ) ; } | ||
/* | ||
term.up = function up( n ) { return '\x1b[' + positiveOrZero( n ) + 'A' ; } ; | ||
term.down = function down( n ) { return '\x1b[' + positiveOrZero( n ) + 'B' ; } ; | ||
term.right = function right( n ) { return '\x1b[' + positiveOrZero( n ) + 'C' ; } ; | ||
term.left = function left( n ) { return '\x1b[' + positiveOrZero( n ) + 'D' ; } ; | ||
*/ | ||
term.move = function move( x , y ) | ||
{ | ||
x >= 0 ? term.right( x ) : term.left( -x ) ; | ||
y >= 0 ? term.down( y ) : term.up( -y ) ; | ||
} ; | ||
/* | ||
term.moveTo = function moveTo( x , y ) | ||
{ | ||
x = positive( x ) ; | ||
y = positive( y ) ; | ||
return '\x1b[' + y + ';' + x + 'H'; | ||
} ; | ||
*/ | ||
// Set the title of the window | ||
//term.windowTitle = function windowTitle( title ) { this.stdout.write( '\x1b]0;' + title + '\x1b\\' ) ; } ; | ||
// The following is PHP code from the CSK PHP lib. | ||
@@ -352,0 +610,0 @@ // This is not backported ATM. |
{ | ||
"name": "terminal-kit", | ||
"version": "0.0.8", | ||
"version": "0.1.1", | ||
"description": "Terminal utilities.", | ||
@@ -10,3 +10,4 @@ "main": "lib/terminal.js", | ||
"dependencies": { | ||
"tree-kit": "0.0.4", | ||
"async-kit": "^0.4.3", | ||
"tree-kit": "^0.0.5", | ||
"ttys": "0.0.3" | ||
@@ -13,0 +14,0 @@ }, |
@@ -18,18 +18,1 @@ | ||
[34mToto | ||
Tata | ||
[31mTiti[0m Tete | ||
# TOC | ||
- [colors](#colors) | ||
<a name=""></a> | ||
<a name="colors"></a> | ||
# colors | ||
should. | ||
```js | ||
console.log( term.BLUE + 'Toto' ) ; | ||
console.log( 'Tata' ) ; | ||
console.log( term.red( 'Titi' ) + ' Tete' ) ; | ||
``` | ||
@@ -1,100 +0,7 @@ | ||
/* | ||
The Cedric's Swiss Knife (CSK) - CSK terminal toolbox test suite | ||
Copyright (c) 2009 - 2014 Cédric Ronvel | ||
The MIT License (MIT) | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
*/ | ||
/* jshint unused:false */ | ||
/* global describe, it, before, after */ | ||
var term = require( '../lib/terminal.js' ) ; | ||
var expect = require( 'expect.js' ) ; | ||
var print = process.stdout.write.bind( process.stdout ) ; | ||
/* Helper functions */ | ||
// | ||
/* Tests */ | ||
describe( "colors" , function() { | ||
it( "should" , function( done ) { | ||
/* | ||
var format = require( '../lib/format' ) ; | ||
//console.log( format.count( '%i' ) ) ; | ||
console.log( format( 'format %2s %1s' , 'one' , 2.1 ) ) ; | ||
done() ; | ||
return ; | ||
//*/ | ||
print( term.esc.blue.on + 'Blue' ) ; | ||
print( 'normal' ) ; | ||
term.red( 'Red' ) ; | ||
print( ' normal' ) ; | ||
term.red( 'Red' ) ; | ||
print( ' normal' ) ; | ||
term.bold.underline.red( 'Bold-underline-red' ) ; | ||
term.green.strike( 'Green-strike' ) ; | ||
term.magenta.italic( 'Magenta-italic' ) ; | ||
term.blink( 'Blink' ) ; | ||
print( term.esc.blue.on + 'Blue' ) ; | ||
term.styleReset() ; print( 'normal' ) ; | ||
term.windowTitle( 'wonderful title' ) ; | ||
term.up( 4 ) ; | ||
term.red( 'up 4' ) ; | ||
term.moveTo( 1 , 1 ) ; | ||
term.blue( 'origin' ) ; | ||
//term.moveToLowerLeft() ; print( 'lowerleft!' ) ; | ||
process.stdin.setRawMode( true ) ; | ||
print( term.esc.mouseMotion.on ) ; | ||
process.stdin.on( 'data' , function( chunk ) { | ||
console.log( chunk ) ; | ||
} ) ; | ||
setTimeout( function() { | ||
print( term.esc.mouseMotion.off ) ; | ||
done() ; | ||
} , 1500 ) ; | ||
} ) ; | ||
describe( "Find" , function() { | ||
it( "how to test a terminal lib with Mocha..." ) ; | ||
} ) ; | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
310810
35
1586
3
18
2
1
+ Addedasync-kit@^0.4.3
+ Addedasync-kit@0.4.13(transitive)
+ Addedtree-kit@0.0.50.2.4(transitive)
- Removedtree-kit@0.0.4(transitive)
Updatedtree-kit@^0.0.5