function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function BigText(options) {
if (!(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}n/a
function Box(options) {
if (!(this instanceof Node)) {
return new Box(options);
}
options = options || {};
Element.call(this, options);
}n/a
function Button(options) {
var self = this;
if (!(this instanceof Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}n/a
function Checkbox(options) {
var self = this;
if (!(this instanceof Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
function FileManager(options) {
var self = this;
if (!(this instanceof Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', file));
}
self.refresh();
} else {
self.emit('file', file);
}
});
});
}n/a
function Form(options) {
var self = this;
if (!(this instanceof Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}n/a
function Image(options) {
if (!(this instanceof Node)) {
return new Image(options);
}
options = options || {};
options.type = options.itype || options.type || 'ansi';
Box.call(this, options);
if (options.type === 'ansi' && this.type !== 'ansiimage') {
var ANSIImage = require('./ansiimage');
Object.getOwnPropertyNames(ANSIImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ANSIImage.prototype, key));
}, this);
ANSIImage.call(this, options);
return this;
}
if (options.type === 'overlay' && this.type !== 'overlayimage') {
var OverlayImage = require('./overlayimage');
Object.getOwnPropertyNames(OverlayImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(OverlayImage.prototype, key));
}, this);
OverlayImage.call(this, options);
return this;
}
throw new Error('`type` must either be `ansi` or `overlay`.');
}n/a
function Input(options) {
if (!(this instanceof Node)) {
return new Input(options);
}
options = options || {};
Box.call(this, options);
}n/a
function Layout(options) {
if (!(this instanceof Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}n/a
function Line(options) {
if (!(this instanceof Node)) {
return new Line(options);
}
options = options || {};
var orientation = options.orientation || 'vertical';
delete options.orientation;
if (orientation === 'vertical') {
options.width = 1;
} else {
options.height = 1;
}
Box.call(this, options);
this.ch = !options.type || options.type === 'line'
? orientation === 'horizontal' ? '─' : '│'
: options.ch || ' ';
this.border = {
type: 'bg',
__proto__: this
};
this.style.border = this.style;
}n/a
function List(options) {
var self = this;
if (!(this instanceof Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(sel ...n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function ListTable(options) {
var self = this;
if (!(this instanceof Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function Loading(options) {
if (!(this instanceof Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}n/a
function Log(options) {
var self = this;
if (!(this instanceof Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this.on('set content', function() {
if (!self._userScrolled || self.scrollOnInput) {
nextTick(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self.screen.render();
});
}
});
}n/a
function Message(options) {
if (!(this instanceof Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}n/a
function Node(options) {
var self = this;
var Screen = require('./screen');
if (!(this instanceof Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}n/a
function OverlayImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function Program(options) {
var self = this;
if (!(this instanceof Program)) {
return new Program(options);
}
Program.bind(this);
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.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this._terminal = this._terminal.toLowerCase();
// OSX
this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
|| !!process.env.ITERM_SESSION_ID;
// VTE
// NOTE: lxterminal does not provide an env variable to check for.
// NOTE: gnome-terminal and sakura use a later version of VTE
// which provides VTE_VERSION as well as supports SGR events.
this.isXFCE = /xfce/i.test(process.env.COLORTERM);
this.isTerminator = !!process.env.TERMINATOR_UUID;
this.isLXDE = false;
this.isVTE = !!process.env.VTE_VERSION
|| this.isXFCE
|| this.isTerminator
|| this.isLXDE;
// xterm and rxvt - not accurate
this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
this.isXterm = false;
this.tmux = !!process.env.TMUX;
this.tmuxVersion = (function() {
if (!self.tmux) return 2;
try {
var version = cp.execFileSync('tmux', ['-V'], { encoding: 'utf8' });
return +/^tmux ([\d.]+)/i.exec(version.trim().split('\n')[0])[1];
} catch (e) {
return 2;
}
})();
this._buf = '';
this._flush = this.flush.bind(this);
if (options.tput !== false) {
this.setupTput();
}
this.listen();
}n/a
function ProgressBar(options) {
var self = this;
if (!(this instanceof Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}n/a
function Prompt(options) {
if (!(this instanceof Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function Question(options) {
if (!(this instanceof Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.okay = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function RadioButton(options) {
var self = this;
if (!(this instanceof Node)) {
return new RadioButton(options);
}
options = options || {};
Checkbox.call(this, options);
this.on('check', function() {
var el = self;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self) {
return;
}
el.uncheck();
});
});
}n/a
function RadioSet(options) {
if (!(this instanceof Node)) {
return new RadioSet(options);
}
options = options || {};
// Possibly inherit parent's style.
// options.style = this.parent.style;
Box.call(this, options);
}n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}n/a
function ScrollableBox(options) {
var self = this;
if (!(this instanceof Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll( ...n/a
function ScrollableText(options) {
if (!(this instanceof Node)) {
return new ScrollableText(options);
}
options = options || {};
options.alwaysScroll = true;
ScrollableBox.call(this, options);
}n/a
function Table(options) {
var self = this;
if (!(this instanceof Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}n/a
function Terminal(options) {
if (!(this instanceof Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = false;
Box.call(this, options);
// XXX Workaround for all motion
if (this.screen.program.tmux && this.screen.program.tmuxVersion >= 2) {
this.screen.program.enableMouse();
}
this.handler = options.handler;
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
this.termName = options.terminal
|| options.term
|| process.env.TERM
|| 'xterm';
this.bootstrap();
}n/a
function Text(options) {
if (!(this instanceof Node)) {
return new Text(options);
}
options = options || {};
options.shrink = true;
Element.call(this, options);
}n/a
function Textarea(options) {
var self = this;
if (!(this instanceof Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}n/a
function Textbox(options) {
if (!(this instanceof Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = false;
Textarea.call(this, options);
this.secret = options.secret;
this.censor = options.censor;
}n/a
function Tput(options) {
if (!(this instanceof Tput)) {
return new Tput(options);
}
options = options || {};
if (typeof options === 'string') {
options = { terminal: options };
}
this.options = options;
this.terminal = options.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this.terminal = this.terminal.toLowerCase();
this.debug = options.debug;
this.padding = options.padding;
this.extended = options.extended;
this.printf = options.printf;
this.termcap = options.termcap;
this.error = null;
this.terminfoPrefix = options.terminfoPrefix;
this.terminfoFile = options.terminfoFile;
this.termcapFile = options.termcapFile;
if (options.terminal || options.term) {
this.setup();
}
}n/a
function Video(options) {
var self = this
, shell
, args;
if (!(this instanceof Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
asort = function (obj) {
return obj.sort(function(a, b) {
a = a.name.toLowerCase();
b = b.name.toLowerCase();
if (a[0] === '.' && b[0] === '.') {
a = a[1];
b = b[1];
} else {
a = a[0];
b = b[0];
}
return a > b ? 1 : (a < b ? -1 : 0);
});
}n/a
attrToBinary = function (style, element) {
return helpers.Element.prototype.sattr.call(element || {}, style);
}n/a
function BigText(options) {
if (!(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}n/a
function Box(options) {
if (!(this instanceof Node)) {
return new Box(options);
}
options = options || {};
Element.call(this, options);
}...
var screen = blessed.screen({
smartCSR: true
});
screen.title = 'my window title';
// Create a box perfectly centered horizontally and vertically.
var box = blessed.box({
top: 'center',
left: 'center',
width: '50%',
height: '50%',
content: 'Hello {bold}world{/bold}!',
tags: true,
border: {
...function Button(options) {
var self = this;
if (!(this instanceof Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}n/a
function Checkbox(options) {
var self = this;
if (!(this instanceof Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}n/a
cleanTags = function (text) {
return helpers.stripTags(text).trim();
}n/a
dropUnicode = function (text) {
if (!text) return '';
return text
.replace(unicode.chars.all, '??')
.replace(unicode.chars.combining, '')
.replace(unicode.chars.surrogate, '?');
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
escape = function (text) {
return text.replace(/[{}]/g, function(ch) {
return ch === '{' ? '{open}' : '{close}';
});
}...
- __merge(a, b)__ - Merge objects `a` and `b` into object `a`.
- __asort(obj)__ - Sort array alphabetically by `name` prop.
- __hsort(obj)__ - Sort array numerically by `index` prop.
- __findFile(start, target)__ - Find a file at `start` directory with name
`target`.
- __escape(text)__ - Escape content's tags to be passed into `el.setContent()`.
Example: `box.setContent('escaped tag: ' + blessed.escape('{bold}{/bold
}'));`
- __parseTags(text)__ - Parse tags into SGR escape codes.
- __generateTags(style, text)__ - Generate text tags based on `style` object.
- __attrToBinary(style, element)__ - Convert `style` attributes to binary
format.
- __stripTags(text)__ - Strip text of tags and SGR sequences.
- __cleanTags(text)__ - Strip text of tags, SGR escape code, and
leading/trailing whitespace.
...function EventEmitter() {
if (!this._events) this._events = {};
}n/a
function FileManager(options) {
var self = this;
if (!(this instanceof Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', file));
}
self.refresh();
} else {
self.emit('file', file);
}
});
});
}n/a
findFile = function (start, target) {
return (function read(dir) {
var files, file, stat, out;
if (dir === '/dev' || dir === '/sys'
|| dir === '/proc' || dir === '/net') {
return null;
}
try {
files = fs.readdirSync(dir);
} catch (e) {
files = [];
}
for (var i = 0; i < files.length; i++) {
file = files[i];
if (file === target) {
return (dir === '/' ? '' : dir) + '/' + file;
}
try {
stat = fs.lstatSync((dir === '/' ? '' : dir) + '/' + file);
} catch (e) {
stat = null;
}
if (stat && stat.isDirectory() && !stat.isSymbolicLink()) {
out = read((dir === '/' ? '' : dir) + '/' + file);
if (out) return out;
}
}
return null;
})(start);
}n/a
function Form(options) {
var self = this;
if (!(this instanceof Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}n/a
generateTags = function (style, text) {
var open = ''
, close = '';
Object.keys(style || {}).forEach(function(key) {
var val = style[key];
if (typeof val === 'string') {
val = val.replace(/^light(?!-)/, 'light-');
val = val.replace(/^bright(?!-)/, 'bright-');
open = '{' + val + '-' + key + '}' + open;
close += '{/' + val + '-' + key + '}';
} else {
if (val === true) {
open = '{' + key + '}' + open;
close += '{/' + key + '}';
}
}
});
if (text != null) {
return open + text + close;
}
return {
open: open,
close: close
};
}n/a
function GpmClient(options) {
if (!(this instanceof GpmClient)) {
return new GpmClient(options);
}
EventEmitter.call(this);
var pid = process.pid;
// check tty for /dev/tty[n]
var path;
try {
path = fs.readlinkSync('/proc/' + pid + '/fd/0');
} catch (e) {
;
}
var tty = /tty[0-9]+$/.exec(path);
if (tty === null) {
// TODO: should also check for /dev/input/..
}
var vc;
if (tty) {
tty = tty[0];
vc = +/[0-9]+$/.exec(tty)[0];
}
var self = this;
if (tty) {
fs.stat(GPM_SOCKET, function(err, stat) {
if (err || !stat.isSocket()) {
return;
}
var conf = {
eventMask: 0xffff,
defaultMask: GPM_MOVE | GPM_HARD,
minMod: 0,
maxMod: 0xffff,
pid: pid,
vc: vc
};
var gpm = net.createConnection(GPM_SOCKET);
this.gpm = gpm;
gpm.on('connect', function() {
send_config(gpm, conf, function() {
conf.pid = 0;
conf.vc = GPM_REQ_NOPASTE;
//send_config(gpm, conf);
});
});
gpm.on('data', function(packet) {
var evnt = parseEvent(packet);
switch (evnt.type & 15) {
case GPM_MOVE:
if (evnt.dx || evnt.dy) {
self.emit('move', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
if (evnt.wdx || evnt.wdy) {
self.emit('mousewheel',
evnt.buttons, evnt.modifiers,
evnt.x, evnt.y, evnt.wdx, evnt.wdy);
}
break;
case GPM_DRAG:
if (evnt.dx || evnt.dy) {
self.emit('drag', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
if (evnt.wdx || evnt.wdy) {
self.emit('mousewheel',
evnt.buttons, evnt.modifiers,
evnt.x, evnt.y, evnt.wdx, evnt.wdy);
}
break;
case GPM_DOWN:
self.emit('btndown', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
if (evnt.type & GPM_DOUBLE) {
self.emit('dblclick', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
case GPM_UP:
self.emit('btnup', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
if (!(evnt.type & GPM_MFLAG)) {
self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
}
});
gpm.on('error', function() {
self.stop();
});
});
}
}n/a
hsort = function (obj) {
return obj.sort(function(a, b) {
return b.index - a.index;
});
}n/a
function Image(options) {
if (!(this instanceof Node)) {
return new Image(options);
}
options = options || {};
options.type = options.itype || options.type || 'ansi';
Box.call(this, options);
if (options.type === 'ansi' && this.type !== 'ansiimage') {
var ANSIImage = require('./ansiimage');
Object.getOwnPropertyNames(ANSIImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ANSIImage.prototype, key));
}, this);
ANSIImage.call(this, options);
return this;
}
if (options.type === 'overlay' && this.type !== 'overlayimage') {
var OverlayImage = require('./overlayimage');
Object.getOwnPropertyNames(OverlayImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(OverlayImage.prototype, key));
}, this);
OverlayImage.call(this, options);
return this;
}
throw new Error('`type` must either be `ansi` or `overlay`.');
}...
}
});
// Append our box to the screen.
screen.append(box);
// Add a png icon to the box
var icon = blessed.image({
parent: box,
top: 0,
left: 0,
type: 'overlay',
width: 'shrink',
height: 'shrink',
file: __dirname + '/my-program-icon.png',
...function Input(options) {
if (!(this instanceof Node)) {
return new Input(options);
}
options = options || {};
Box.call(this, options);
}n/a
function Layout(options) {
if (!(this instanceof Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}...
Here is an example of how to provide a renderer. Note that this is also the
default renderer if none is provided. This renderer will render each child as
though they were `display: inline-block;` in CSS, as if there were a
dynamically sized horizontal grid from left to right.
``` js
var layout = blessed.layout({
parent: screen,
top: 'center',
left: 'center',
width: '50%',
height: '50%',
border: 'line',
style: {
...function Line(options) {
if (!(this instanceof Node)) {
return new Line(options);
}
options = options || {};
var orientation = options.orientation || 'vertical';
delete options.orientation;
if (orientation === 'vertical') {
options.width = 1;
} else {
options.height = 1;
}
Box.call(this, options);
this.ch = !options.type || options.type === 'line'
? orientation === 'horizontal' ? '─' : '│'
: options.ch || ' ';
this.border = {
type: 'bg',
__proto__: this
};
this.style.border = this.style;
}n/a
function List(options) {
var self = this;
if (!(this instanceof Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(sel ...n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function ListTable(options) {
var self = this;
if (!(this instanceof Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}n/a
function Loading(options) {
if (!(this instanceof Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}n/a
function Log(options) {
var self = this;
if (!(this instanceof Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this.on('set content', function() {
if (!self._userScrolled || self.scrollOnInput) {
nextTick(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self.screen.render();
});
}
});
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...merge = function (a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
return a;
}n/a
function Message(options) {
if (!(this instanceof Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}n/a
function Node(options) {
var self = this;
var Screen = require('./screen');
if (!(this instanceof Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}n/a
function OverlayImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}n/a
parseTags = function (text, screen) {
return helpers.Element.prototype._parseTags.call(
{ parseTags: true, screen: screen || helpers.Screen.global }, text);
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function Program(options) {
var self = this;
if (!(this instanceof Program)) {
return new Program(options);
}
Program.bind(this);
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.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this._terminal = this._terminal.toLowerCase();
// OSX
this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
|| !!process.env.ITERM_SESSION_ID;
// VTE
// NOTE: lxterminal does not provide an env variable to check for.
// NOTE: gnome-terminal and sakura use a later version of VTE
// which provides VTE_VERSION as well as supports SGR events.
this.isXFCE = /xfce/i.test(process.env.COLORTERM);
this.isTerminator = !!process.env.TERMINATOR_UUID;
this.isLXDE = false;
this.isVTE = !!process.env.VTE_VERSION
|| this.isXFCE
|| this.isTerminator
|| this.isLXDE;
// xterm and rxvt - not accurate
this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
this.isXterm = false;
this.tmux = !!process.env.TMUX;
this.tmuxVersion = (function() {
if (!self.tmux) return 2;
try {
var version = cp.execFileSync('tmux', ['-V'], { encoding: 'utf8' });
return +/^tmux ([\d.]+)/i.exec(version.trim().split('\n')[0])[1];
} catch (e) {
return 2;
}
})();
this._buf = '';
this._flush = this.flush.bind(this);
if (options.tput !== false) {
this.setupTput();
}
this.listen();
}...
$ echo "$(tput.js setaf 2)Hello World$(tput.js sgr0)"
```
The main functionality is exposed in the main `blessed` module:
``` js
var blessed = require('blessed')
, program = blessed.program();
program.key('q', function(ch, key) {
program.clear();
program.disableMouse();
program.showCursor();
program.normalBuffer();
process.exit(0);
...function ProgressBar(options) {
var self = this;
if (!(this instanceof Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}n/a
function Prompt(options) {
if (!(this instanceof Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function Question(options) {
if (!(this instanceof Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.okay = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function RadioButton(options) {
var self = this;
if (!(this instanceof Node)) {
return new RadioButton(options);
}
options = options || {};
Checkbox.call(this, options);
this.on('check', function() {
var el = self;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self) {
return;
}
el.uncheck();
});
});
}n/a
function RadioSet(options) {
if (!(this instanceof Node)) {
return new RadioSet(options);
}
options = options || {};
// Possibly inherit parent's style.
// options.style = this.parent.style;
Box.call(this, options);
}n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}...
`blessed.screen` option. This will enable CSR when scrolling text in elements
or when manipulating lines.
``` js
var blessed = require('blessed');
// Create a screen object.
var screen = blessed.screen({
smartCSR: true
});
screen.title = 'my window title';
// Create a box perfectly centered horizontally and vertically.
var box = blessed.box({
...function ScrollableBox(options) {
var self = this;
if (!(this instanceof Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll( ...n/a
function ScrollableText(options) {
if (!(this instanceof Node)) {
return new ScrollableText(options);
}
options = options || {};
options.alwaysScroll = true;
ScrollableBox.call(this, options);
}n/a
function sprintf(src) {
var params = Array.prototype.slice.call(arguments, 1)
, rule = /%([\-+# ]{1,4})?(\d+(?:\.\d+)?)?([doxXsc])/g
, i = 0;
return src.replace(rule, function(_, flag, width, type) {
var flags = (flag || '').split('')
, param = params[i] != null ? params[i] : ''
, initial = param
// , width = +width
, opt = {}
, pre = '';
i++;
switch (type) {
case 'd': // signed int
param = (+param).toString(10);
break;
case 'o': // unsigned octal
param = (+param).toString(8);
break;
case 'x': // unsigned hex int
param = (+param).toString(16);
break;
case 'X': // unsigned hex int uppercase
param = (+param).toString(16).toUppercase();
break;
case 's': // string
break;
case 'c': // char
param = isFinite(param)
? String.fromCharCode(param || 0200)
: '';
break;
}
flags.forEach(function(flag) {
switch (flag) {
// left-justify by width
case '-':
opt.left = true;
break;
// always precede numbers with their signs
case '+':
opt.signs = true;
break;
// used with o, x, X - value is preceded with 0, 0x, or 0X respectively.
// used with a, A, e, E, f, F, g, G - forces written output to contain
// a decimal point even if no more digits follow
case '#':
opt.hexpoint = true;
break;
// if no sign is going to be written, black space in front of the value
case ' ':
opt.space = true;
break;
}
});
width = +width.split('.')[0];
// Should this be for opt.left too?
// Example: %2.2X - turns 0 into 00
if (width && !opt.left) {
param = param + '';
while (param.length < width) {
param = '0' + param;
}
}
if (opt.signs) {
if (+initial >= 0) {
pre += '+';
}
}
if (opt.space) {
if (!opt.signs && +initial >= 0) {
pre += ' ';
}
}
if (opt.hexpoint) {
switch (type) {
case 'o': // unsigned octal
pre += '0';
break;
case 'x': // unsigned hex int
pre += '0x';
break;
case 'X': // unsigned hex int uppercase
pre += '0X';
break;
}
}
if (opt.left) {
if (width > (pre.length + param.length)) {
width -= pre.length + param.length;
pre = Array(width + 1).join(' ') + pre;
}
}
return pre + param;
});
}n/a
stripTags = function (text) {
if (!text) return '';
return text
.replace(/{(\/?)([\w\-,;!#]*)}/g, '')
.replace(/\x1b\[[\d;]*m/g, '');
}n/a
function Table(options) {
var self = this;
if (!(this instanceof Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}n/a
function Terminal(options) {
if (!(this instanceof Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = false;
Box.call(this, options);
// XXX Workaround for all motion
if (this.screen.program.tmux && this.screen.program.tmuxVersion >= 2) {
this.screen.program.enableMouse();
}
this.handler = options.handler;
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
this.termName = options.terminal
|| options.term
|| process.env.TERM
|| 'xterm';
this.bootstrap();
}n/a
function Text(options) {
if (!(this instanceof Node)) {
return new Text(options);
}
options = options || {};
options.shrink = true;
Element.call(this, options);
}n/a
function Textarea(options) {
var self = this;
if (!(this instanceof Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}n/a
function Textbox(options) {
if (!(this instanceof Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = false;
Textarea.call(this, options);
this.secret = options.secret;
this.censor = options.censor;
}n/a
function Tput(options) {
if (!(this instanceof Tput)) {
return new Tput(options);
}
options = options || {};
if (typeof options === 'string') {
options = { terminal: options };
}
this.options = options;
this.terminal = options.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this.terminal = this.terminal.toLowerCase();
this.debug = options.debug;
this.padding = options.padding;
this.extended = options.extended;
this.printf = options.printf;
this.termcap = options.termcap;
this.error = null;
this.terminfoPrefix = options.terminfoPrefix;
this.terminfoFile = options.terminfoFile;
this.termcapFile = options.termcapFile;
if (options.terminal || options.term) {
this.setup();
}
}...
This will actually parse the xterm terminfo and compile every
string capability to a javascript function:
``` js
var blessed = require('blessed');
var tput = blessed.tput({
terminal: 'xterm-256color',
extended: true
});
process.stdout.write(tput.setaf(4) + 'Hello' + tput.sgr0() + '\n');
```
...function tryRead(file) {
if (Array.isArray(file)) {
for (var i = 0; i < file.length; i++) {
var data = tryRead(file[i]);
if (data) return data;
}
return '';
}
if (!file) return '';
file = path.resolve.apply(path, arguments);
try {
return fs.readFileSync(file, 'utf8');
} catch (e) {
return '';
}
}n/a
function Video(options) {
var self = this
, shell
, args;
if (!(this instanceof Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
_align = function (line, width, align) {
if (!align) return line;
//if (!align && !~line.indexOf('{|}')) return line;
var cline = line.replace(/\x1b\[[\d;]*m/g, '')
, len = cline.length
, s = width - len;
if (this.shrink) {
s = 0;
}
if (len === 0) return line;
if (s < 0) return line;
if (align === 'center') {
s = Array(((s / 2) | 0) + 1).join(' ');
return s + line + s;
} else if (align === 'right') {
s = Array(s + 1).join(' ');
return s + line;
} else if (this.parseTags && ~line.indexOf('{|}')) {
var parts = line.split('{|}');
var cparts = cline.split('{|}');
s = Math.max(width - cparts[0].length - cparts[1].length, 0);
s = Array(s + 1).join(' ');
return parts[0] + s + parts[1];
}
return line;
}n/a
_getBottom = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, bottom;
if (this.position.bottom == null && this.position.top != null) {
bottom = this.screen.rows - (this._getTop(get) + this._getHeight(get));
if (this.screen.autoPadding) {
bottom += this.parent.ibottom;
}
return bottom;
}
bottom = (parent.abottom || 0) + (this.position.bottom || 0);
if (this.screen.autoPadding) {
bottom += this.parent.ibottom;
}
return bottom;
}n/a
_getCoords = function (get, noscroll) {
if (this.hidden) return;
// if (this.parent._rendering) {
// get = true;
// }
var xi = this._getLeft(get)
, xl = xi + this._getWidth(get)
, yi = this._getTop(get)
, yl = yi + this._getHeight(get)
, base = this.childBase || 0
, el = this
, fixed = this.fixed
, coords
, v
, noleft
, noright
, notop
, nobot
, ppos
, b;
// Attempt to shrink the element base on the
// size of the content and child elements.
if (this.shrink) {
coords = this._getShrink(xi, xl, yi, yl, get);
xi = coords.xi, xl = coords.xl;
yi = coords.yi, yl = coords.yl;
}
// Find a scrollable ancestor if we have one.
while (el = el.parent) {
if (el.scrollable) {
if (fixed) {
fixed = false;
continue;
}
break;
}
}
// Check to make sure we're visible and
// inside of the visible scroll area.
// NOTE: Lists have a property where only
// the list items are obfuscated.
// Old way of doing things, this would not render right if a shrunken element
// with lots of boxes in it was within a scrollable element.
// See: $ node test/widget-shrink-fail.js
// var thisparent = this.parent;
var thisparent = el;
if (el && !noscroll) {
ppos = thisparent.lpos;
// The shrink option can cause a stack overflow
// by calling _getCoords on the child again.
// if (!get && !thisparent.shrink) {
// ppos = thisparent._getCoords();
// }
if (!ppos) return;
// TODO: Figure out how to fix base (and cbase to only
// take into account the *parent's* padding.
yi -= ppos.base;
yl -= ppos.base;
b = thisparent.border ? 1 : 0;
// XXX
// Fixes non-`fixed` labels to work with scrolling (they're ON the border):
// if (this.position.left < 0
// || this.position.right < 0
// || this.position.top < 0
// || this.position.bottom < 0) {
if (this._isLabel) {
b = 0;
}
if (yi < ppos.yi + b) {
if (yl - 1 < ppos.yi + b) {
// Is above.
return;
} else {
// Is partially covered above.
notop = true;
v = ppos.yi - yi;
if (this.border) v--;
if (thisparent.border) v++;
base += v;
yi += v;
}
} else if (yl > ppos.yl - b) {
if (yi > ppos.yl - 1 - b) {
// Is below.
return;
} else {
// Is partially covered below.
nobot = true;
v = yl - ppos.yl;
if (this.border) v--;
if (thisparent.border) v++;
yl -= v;
}
}
// Shouldn't be necessary.
// assert.ok(yi < yl);
if (yi >= yl) return;
// Could allow overlapping stuff in scrolling elements
// if we cleared the pending buffer before every draw.
if (xi < el.lpos.xi) {
xi = el.lpos.xi;
noleft = true;
if (this.border) xi--;
if (thisparent.border) xi++;
}
if (xl > el.lpos.xl) {
xl = el.lpos.xl;
noright = true;
if (this.border) xl++;
if (thisparent.border) xl--;
}
//if (xi > xl) return;
if (xi >= xl) return;
}
if (this.noOverflow && this.parent.lpos) {
if (xi < this.parent.lpos.xi + this.parent.ileft) {
xi = this.parent.lpos.xi + this.parent.ileft;
}
if (xl > this.parent.lpos.xl - this.parent.iright) {
xl = this.parent.lpos.xl - this.parent.iright;
}
if (yi < this.parent.lpos.yi + this.parent.itop) {
yi = this.parent.lpos.yi + this.parent.itop;
}
if (yl > this.parent.lpos.yl - this.parent.ibottom) {
yl = this.parent.lpos.yl - this.parent.ibottom;
}
}
// if (this.parent.lpos) {
// this.parent.lpos._scrollBottom = Math.max(
// this.parent.lpos._scrollBottom, yl);
// }
return {
xi: xi,
xl: xl,
yi: yi,
yl: yl,
base: base,
noleft: noleft,
noright: noright,
notop: notop,
nobot: nobot,
renders: this.screen.renders
};
}n/a
_getHeight = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, height = this.position.height
, top
, expr;
if (typeof height === 'string') {
if (height === 'half') height = '50%';
expr = height.split(/(?=\+|-)/);
height = expr[0];
height = +height.slice(0, -1) / 100;
height = parent.height * height | 0;
height += +(expr[1] || 0);
return height;
}
// This is for if the element is being streched or shrunken.
// Although the width for shrunken elements is calculated
// in the render function, it may be calculated based on
// the content width, and the content width is initially
// decided by the width the element, so it needs to be
// calculated here.
if (height == null) {
top = this.position.top || 0;
if (typeof top === 'string') {
if (top === 'center') top = '50%';
expr = top.split(/(?=\+|-)/);
top = expr[0];
top = +top.slice(0, -1) / 100;
top = parent.height * top | 0;
top += +(expr[1] || 0);
}
height = parent.height - (this.position.bottom || 0) - top;
if (this.screen.autoPadding) {
if ((this.position.top != null
|| this.position.bottom == null)
&& this.position.top !== 'center') {
height -= this.parent.itop;
}
height -= this.parent.ibottom;
}
}
return height;
}n/a
_getLeft = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, left = this.position.left || 0
, expr;
if (typeof left === 'string') {
if (left === 'center') left = '50%';
expr = left.split(/(?=\+|-)/);
left = expr[0];
left = +left.slice(0, -1) / 100;
left = parent.width * left | 0;
left += +(expr[1] || 0);
if (this.position.left === 'center') {
left -= this._getWidth(get) / 2 | 0;
}
}
if (this.position.left == null && this.position.right != null) {
return this.screen.cols - this._getWidth(get) - this._getRight(get);
}
if (this.screen.autoPadding) {
if ((this.position.left != null
|| this.position.right == null)
&& this.position.left !== 'center') {
left += this.parent.ileft;
}
}
return (parent.aleft || 0) + left;
}n/a
_getPos = function () {
var pos = this.lpos;
assert.ok(pos);
if (pos.aleft != null) return pos;
pos.aleft = pos.xi;
pos.atop = pos.yi;
pos.aright = this.screen.cols - pos.xl;
pos.abottom = this.screen.rows - pos.yl;
pos.width = pos.xl - pos.xi;
pos.height = pos.yl - pos.yi;
return pos;
}n/a
_getRight = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, right;
if (this.position.right == null && this.position.left != null) {
right = this.screen.cols - (this._getLeft(get) + this._getWidth(get));
if (this.screen.autoPadding) {
right += this.parent.iright;
}
return right;
}
right = (parent.aright || 0) + (this.position.right || 0);
if (this.screen.autoPadding) {
right += this.parent.iright;
}
return right;
}n/a
_getShrink = function (xi, xl, yi, yl, get) {
var shrinkBox = this._getShrinkBox(xi, xl, yi, yl, get)
, shrinkContent = this._getShrinkContent(xi, xl, yi, yl, get)
, xll = xl
, yll = yl;
// Figure out which one is bigger and use it.
if (shrinkBox.xl - shrinkBox.xi > shrinkContent.xl - shrinkContent.xi) {
xi = shrinkBox.xi;
xl = shrinkBox.xl;
} else {
xi = shrinkContent.xi;
xl = shrinkContent.xl;
}
if (shrinkBox.yl - shrinkBox.yi > shrinkContent.yl - shrinkContent.yi) {
yi = shrinkBox.yi;
yl = shrinkBox.yl;
} else {
yi = shrinkContent.yi;
yl = shrinkContent.yl;
}
// Recenter shrunken elements.
if (xl < xll && this.position.left === 'center') {
xll = (xll - xl) / 2 | 0;
xi += xll;
xl += xll;
}
if (yl < yll && this.position.top === 'center') {
yll = (yll - yl) / 2 | 0;
yi += yll;
yl += yll;
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
}n/a
_getShrinkBox = function (xi, xl, yi, yl, get) {
if (!this.children.length) {
return { xi: xi, xl: xi + 1, yi: yi, yl: yi + 1 };
}
var i, el, ret, mxi = xi, mxl = xi + 1, myi = yi, myl = yi + 1;
// This is a chicken and egg problem. We need to determine how the children
// will render in order to determine how this element renders, but it in
// order to figure out how the children will render, they need to know
// exactly how their parent renders, so, we can give them what we have so
// far.
var _lpos;
if (get) {
_lpos = this.lpos;
this.lpos = { xi: xi, xl: xl, yi: yi, yl: yl };
//this.shrink = false;
}
for (i = 0; i < this.children.length; i++) {
el = this.children[i];
ret = el._getCoords(get);
// Or just (seemed to work, but probably not good):
// ret = el.lpos || this.lpos;
if (!ret) continue;
// Since the parent element is shrunk, and the child elements think it's
// going to take up as much space as possible, an element anchored to the
// right or bottom will inadvertantly make the parent's shrunken size as
// large as possible. So, we can just use the height and/or width the of
// element.
// if (get) {
if (el.position.left == null && el.position.right != null) {
ret.xl = xi + (ret.xl - ret.xi);
ret.xi = xi;
if (this.screen.autoPadding) {
// Maybe just do this no matter what.
ret.xl += this.ileft;
ret.xi += this.ileft;
}
}
if (el.position.top == null && el.position.bottom != null) {
ret.yl = yi + (ret.yl - ret.yi);
ret.yi = yi;
if (this.screen.autoPadding) {
// Maybe just do this no matter what.
ret.yl += this.itop;
ret.yi += this.itop;
}
}
if (ret.xi < mxi) mxi = ret.xi;
if (ret.xl > mxl) mxl = ret.xl;
if (ret.yi < myi) myi = ret.yi;
if (ret.yl > myl) myl = ret.yl;
}
if (get) {
this.lpos = _lpos;
//this.shrink = true;
}
if (this.position.width == null
&& (this.position.left == null
|| this.position.right == null)) {
if (this.position.left == null && this.position.right != null) {
xi = xl - (mxl - mxi);
if (!this.screen.autoPadding) {
xi -= this.padding.left + this.padding.right;
} else {
xi -= this.ileft;
}
} else {
xl = mxl;
if (!this.screen.autoPadding) {
xl += this.padding.left + this.padding.right;
// XXX Temporary workaround until we decide to make autoPadding default.
// See widget-listtable.js for an example of why this is necessary.
// XXX Maybe just to this for all this being that this would affect
// width shrunken normal shrunken lists as well.
// if (this._isList) {
if (this.type === 'list-table') {
xl -= this.padding.left + this.padding.right;
xl += this.iright;
}
} else {
//xl += this.padding.right;
xl += this.iright;
}
}
}
if (this.position.height == null
&& (this.position.top == null
|| this.position.bottom == null)
&& (!this.scrollable || this._isList)) {
// NOTE: Lists get special treatment if they are shrunken - assume they
// want all list items showing. This is one case we can calculate the
// height based on items/boxes.
if (this._isList) {
myi = 0 - this.itop;
myl = this.items.length + this.ibottom;
}
if (this.position.top == null && this.position.bottom != null) {
yi = yl - (myl - myi);
if (!this.screen.autoPadding) {
yi -= this.padding.top + this.padding.bottom;
} else {
yi -= this.itop;
}
} else {
yl = myl;
if (!this.screen.autoPadding) {
yl += this.padding.top + this.padding.bottom;
} else {
yl += this.ibottom;
}
}
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
}n/a
_getShrinkContent = function (xi, xl, yi, yl) {
var h = this._clines.length
, w = this._clines.mwidth || 1;
if (this.position.width == null
&& (this.position.left == null
|| this.position.right == null)) {
if (this.position.left == null && this.position.right != null) {
xi = xl - w - this.iwidth;
} else {
xl = xi + w + this.iwidth;
}
}
if (this.position.height == null
&& (this.position.top == null
|| this.position.bottom == null)
&& (!this.scrollable || this._isList)) {
if (this.position.top == null && this.position.bottom != null) {
yi = yl - h - this.iheight;
} else {
yl = yi + h + this.iheight;
}
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
}n/a
_getTop = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, top = this.position.top || 0
, expr;
if (typeof top === 'string') {
if (top === 'center') top = '50%';
expr = top.split(/(?=\+|-)/);
top = expr[0];
top = +top.slice(0, -1) / 100;
top = parent.height * top | 0;
top += +(expr[1] || 0);
if (this.position.top === 'center') {
top -= this._getHeight(get) / 2 | 0;
}
}
if (this.position.top == null && this.position.bottom != null) {
return this.screen.rows - this._getHeight(get) - this._getBottom(get);
}
if (this.screen.autoPadding) {
if ((this.position.top != null
|| this.position.bottom == null)
&& this.position.top !== 'center') {
top += this.parent.itop;
}
}
return (parent.atop || 0) + top;
}n/a
_getWidth = function (get) {
var parent = get ? this.parent._getPos() : this.parent
, width = this.position.width
, left
, expr;
if (typeof width === 'string') {
if (width === 'half') width = '50%';
expr = width.split(/(?=\+|-)/);
width = expr[0];
width = +width.slice(0, -1) / 100;
width = parent.width * width | 0;
width += +(expr[1] || 0);
return width;
}
// This is for if the element is being streched or shrunken.
// Although the width for shrunken elements is calculated
// in the render function, it may be calculated based on
// the content width, and the content width is initially
// decided by the width the element, so it needs to be
// calculated here.
if (width == null) {
left = this.position.left || 0;
if (typeof left === 'string') {
if (left === 'center') left = '50%';
expr = left.split(/(?=\+|-)/);
left = expr[0];
left = +left.slice(0, -1) / 100;
left = parent.width * left | 0;
left += +(expr[1] || 0);
}
width = parent.width - (this.position.right || 0) - left;
if (this.screen.autoPadding) {
if ((this.position.left != null || this.position.right == null)
&& this.position.left !== 'center') {
width -= this.parent.ileft;
}
width -= this.parent.iright;
}
}
return width;
}n/a
_parseAttr = function (lines) {
var dattr = this.sattr(this.style)
, attr = dattr
, attrs = []
, line
, i
, j
, c;
if (lines[0].attr === attr) {
return;
}
for (j = 0; j < lines.length; j++) {
line = lines[j];
attrs[j] = attr;
for (i = 0; i < line.length; i++) {
if (line[i] === '\x1b') {
if (c = /^\x1b\[[\d;]*m/.exec(line.substring(i))) {
attr = this.screen.attrCode(c[0], attr, dattr);
i += c[0].length - 1;
}
}
}
}
return attrs;
}n/a
_parseTags = function (text) {
if (!this.parseTags) return text;
if (!/{\/?[\w\-,;!#]*}/.test(text)) return text;
var program = this.screen.program
, out = ''
, state
, bg = []
, fg = []
, flag = []
, cap
, slash
, param
, attr
, esc;
for (;;) {
if (!esc && (cap = /^{escape}/.exec(text))) {
text = text.substring(cap[0].length);
esc = true;
continue;
}
if (esc && (cap = /^([\s\S]+?){\/escape}/.exec(text))) {
text = text.substring(cap[0].length);
out += cap[1];
esc = false;
continue;
}
if (esc) {
// throw new Error('Unterminated escape tag.');
out += text;
break;
}
if (cap = /^{(\/?)([\w\-,;!#]*)}/.exec(text)) {
text = text.substring(cap[0].length);
slash = cap[1] === '/';
param = cap[2].replace(/-/g, ' ');
if (param === 'open') {
out += '{';
continue;
} else if (param === 'close') {
out += '}';
continue;
}
if (param.slice(-3) === ' bg') state = bg;
else if (param.slice(-3) === ' fg') state = fg;
else state = flag;
if (slash) {
if (!param) {
out += program._attr('normal');
bg.length = 0;
fg.length = 0;
flag.length = 0;
} else {
attr = program._attr(param, false);
if (attr == null) {
out += cap[0];
} else {
// if (param !== state[state.length - 1]) {
// throw new Error('Misnested tags.');
// }
state.pop();
if (state.length) {
out += program._attr(state[state.length - 1]);
} else {
out += attr;
}
}
}
} else {
if (!param) {
out += cap[0];
} else {
attr = program._attr(param);
if (attr == null) {
out += cap[0];
} else {
state.push(param);
out += attr;
}
}
}
continue;
}
if (cap = /^[\s\S]+?(?={\/?[\w\-,;!#]*})/.exec(text)) {
text = text.substring(cap[0].length);
out += cap[0];
continue;
}
out += text;
break;
}
return out;
}n/a
_render = function () {
this._emit('prerender');
this.parseContent();
var coords = this._getCoords(true);
if (!coords) {
delete this.lpos;
return;
}
if (coords.xl - coords.xi <= 0) {
coords.xl = Math.max(coords.xl, coords.xi);
return;
}
if (coords.yl - coords.yi <= 0) {
coords.yl = Math.max(coords.yl, coords.yi);
return;
}
var lines = this.screen.lines
, xi = coords.xi
, xl = coords.xl
, yi = coords.yi
, yl = coords.yl
, x
, y
, cell
, attr
, ch
, content = this._pcontent
, ci = this._clines.ci[coords.base]
, battr
, dattr
, c
, visible
, i
, bch = this.ch;
// Clip content if it's off the edge of the screen
// if (xi + this.ileft < 0 || yi + this.itop < 0) {
// var clines = this._clines.slice();
// if (xi + this.ileft < 0) {
// for (var i = 0; i < clines.length; i++) {
// var t = 0;
// var csi = '';
// var csis = '';
// for (var j = 0; j < clines[i].length; j++) {
// while (clines[i][j] === '\x1b') {
// csi = '\x1b';
// while (clines[i][j++] !== 'm') csi += clines[i][j];
// csis += csi;
// }
// if (++t === -(xi + this.ileft) + 1) break;
// }
// clines[i] = csis + clines[i].substring(j);
// }
// }
// if (yi + this.itop < 0) {
// clines = clines.slice(-(yi + this.itop));
// }
// content = clines.join('\n');
// }
if (coords.base >= this._clines.ci.length) {
ci = this._pcontent.length;
}
this.lpos = coords;
if (this.border && this.border.type === 'line') {
this.screen._borderStops[coords.yi] = true;
this.screen._borderStops[coords.yl - 1] = true;
// if (!this.screen._borderStops[coords.yi]) {
// this.screen._borderStops[coords.yi] = { xi: coords.xi, xl: coords.xl };
// } else {
// if (this.screen._borderStops[coords.yi].xi > coords.xi) {
// this.screen._borderStops[coords.yi].xi = coords.xi;
// }
// if (this.screen._borderStops[coords.yi].xl < coords.xl) {
// this.screen._borderStops[coords.yi].xl = coords.xl;
// }
// }
// this.screen._borderStops[coords.yl - 1] = this.screen._borderStops[coords.yi];
}
dattr = this.sattr(this.style);
attr = dattr;
// If we're in a scrollable text box, check to
// see which attributes this line starts with.
if (ci > 0) {
attr = this._clines.attr[Math.min(coords.base, this._clines.length - 1)];
}
if (this.border) xi++, xl--, yi++, yl--;
// If we have padding/valign, that means the
// content-drawing loop will skip a few cells/lines.
// To deal with this, we can just fill the whole thing
// ahead of time. This could be optimized.
if (this.tpadding || (this.valign && this.valign !== 'top')) {
if (this.style.transparent) {
for (y = Math.max(yi, 0); y < yl; y++) {
if (!lines[y]) break;
for (x = Math.max(xi, 0); x < xl; x++) {
if (!lines[y][x]) break;
lines[y][x][0] = colors.blend(attr, lines[y][x][0]);
// lines[y][x][1] = bch;
lines[y].dirty = true;
}
}
} else {
this.screen.fillRegion(dattr, bch, xi, xl, yi, yl);
}
}
if (this.tpadding) {
xi += this.padding.left, xl -= this.padding.right;
yi += this.padding.top, yl -= this.padding.bottom;
}
// Determine where to place the text if it's vertically aligned.
if (this.valign === 'middle' || this.valign === 'bottom') {
visible = yl - yi;
if (this._clines.length < visible) {
if (this.valign === 'middle') {
visible = visible / 2 | 0;
visible -= this._clines.length / 2 | 0;
} else if (this.valign === 'bottom') {
visible -= this._clines.length;
}
ci -= visible * (xl - xi);
}
}
// Draw the content and background.
for (y = yi; y < yl; y++) {
if (!lines[y]) {
if (y >= this.screen.height || yl < this.ibottom) {
break;
} else {
continue;
}
}
for (x = xi; x < ...n/a
_wrapContent = function (content, width) {
var tags = this.parseTags
, state = this.align
, wrap = this.wrap
, margin = 0
, rtof = []
, ftor = []
, out = []
, no = 0
, line
, align
, cap
, total
, i
, part
, j
, lines
, rest;
lines = content.split('\n');
if (!content) {
out.push(content);
out.rtof = [0];
out.ftor = [[0]];
out.fake = lines;
out.real = out;
out.mwidth = 0;
return out;
}
if (this.scrollbar) margin++;
if (this.type === 'textarea') margin++;
if (width > margin) width -= margin;
main:
for (; no < lines.length; no++) {
line = lines[no];
align = state;
ftor.push([]);
// Handle alignment tags.
if (tags) {
if (cap = /^{(left|center|right)}/.exec(line)) {
line = line.substring(cap[0].length);
align = state = cap[1] !== 'left'
? cap[1]
: null;
}
if (cap = /{\/(left|center|right)}$/.exec(line)) {
line = line.slice(0, -cap[0].length);
//state = null;
state = this.align;
}
}
// If the string is apparently too long, wrap it.
while (line.length > width) {
// Measure the real width of the string.
for (i = 0, total = 0; i < line.length; i++) {
while (line[i] === '\x1b') {
while (line[i] && line[i++] !== 'm');
}
if (!line[i]) break;
if (++total === width) {
// If we're not wrapping the text, we have to finish up the rest of
// the control sequences before cutting off the line.
i++;
if (!wrap) {
rest = line.substring(i).match(/\x1b\[[^m]*m/g);
rest = rest ? rest.join('') : '';
out.push(this._align(line.substring(0, i) + rest, width, align));
ftor[no].push(out.length - 1);
rtof.push(no);
continue main;
}
if (!this.screen.fullUnicode) {
// Try to find a space to break on.
if (i !== line.length) {
j = i;
while (j > i - 10 && j > 0 && line[--j] !== ' ');
if (line[j] === ' ') i = j + 1;
}
} else {
// Try to find a character to break on.
if (i !== line.length) {
// <XXX>
// Compensate for surrogate length
// counts on wrapping (experimental):
// NOTE: Could optimize this by putting
// it in the parent for loop.
if (unicode.isSurrogate(line, i)) i--;
for (var s = 0, n = 0; n < i; n++) {
if (unicode.isSurrogate(line, n)) s++, n++;
}
i += s;
// </XXX>
j = i;
// Break _past_ space.
// Break _past_ double-width chars.
// Break _past_ surrogate pairs.
// Break _past_ combining chars.
while (j > i - 10 && j > 0) {
j--;
if (line[j] === ' '
|| line[j] === '\x03'
|| (unicode.isSurrogate(line, j - 1) && line[j + 1] !== '\x03')
|| unicode.isCombining(line, j)) {
break;
}
}
if (line[j] === ' '
|| line[j] === '\x03'
|| (unicode.isSurrogate(line, j - 1) && line[j + 1] !== '\x03')
|| unicode.isCombining(line, j)) {
i = j + 1;
}
}
}
break;
}
}
part = line.substring(0, i);
line = line.substring(i);
out.push(this._align(part, width, align));
ftor[no].push(out.length - 1);
rtof.push(no);
// Make sure we didn't wrap the line to the very end, otherwise
// we get a pointless empty line after a newline.
if (line === '') continue main;
// If only an escape code got cut off, at it to `part`.
if (/^(?:\x1b[\[\d;]*m)+$/.test(line)) {
out[out.length - 1] += line; ...n/a
clearBaseLine = function (i) {
var fake = this._clines.rtof[this.childBase || 0];
return this.clearLine(fake + i);
}n/a
clearLine = function (i) {
i = Math.min(i, this._clines.fake.length - 1);
return this.setLine(i, '');
}n/a
clearPos = function (get, override) {
if (this.detached) return;
var lpos = this._getCoords(get);
if (!lpos) return;
this.screen.clearRegion(
lpos.xi, lpos.xl,
lpos.yi, lpos.yl,
override);
}n/a
deleteBottom = function (n) {
var h = (this.childBase || 0) + this.height - 1 - this.iheight
, i = Math.min(h, this._clines.length - 1)
, fake = this._clines.rtof[i];
n = n || 1;
return this.deleteLine(fake - (n - 1), n);
}n/a
deleteLine = function (i, n) {
n = n || 1;
if (i !== i || i == null) {
i = this._clines.ftor.length - 1;
}
i = Math.max(i, 0);
i = Math.min(i, this._clines.ftor.length - 1);
// NOTE: Could possibly compare the first and last ftor line numbers to see
// if they're the same, or if they fit in the visible region entirely.
var start = this._clines.length
, diff
, real = this._clines.ftor[i][0];
while (n--) {
this._clines.fake.splice(i, 1);
}
this.setContent(this._clines.fake.join('\n'), true);
diff = start - this._clines.length;
// XXX clearPos() without diff statement?
var height = 0;
if (diff > 0) {
var pos = this._getCoords();
if (!pos) return;
height = pos.yl - pos.yi - this.iheight;
var base = this.childBase || 0
, visible = real >= base && real - base < height;
if (pos && visible && this.screen.cleanSides(this)) {
this.screen.deleteLine(diff,
pos.yi + this.itop + real - base,
pos.yi,
pos.yl - this.ibottom - 1);
}
}
if (this._clines.length < height) {
this.clearPos();
}
}n/a
deleteTop = function (n) {
var fake = this._clines.rtof[this.childBase || 0];
return this.deleteLine(fake, n);
}n/a
disableDrag = function () {
if (!this._draggable) return false;
delete this.screen._dragging;
delete this._drag;
this.removeListener('mousedown', this._dragMD);
this.removeScreenEvent('mouse', this._dragM);
return this._draggable = false;
}n/a
enableDrag = function (verify) {
var self = this;
if (this._draggable) return true;
if (typeof verify !== 'function') {
verify = function() { return true; };
}
this.enableMouse();
this.on('mousedown', this._dragMD = function(data) {
if (self.screen._dragging) return;
if (!verify(data)) return;
self.screen._dragging = self;
self._drag = {
x: data.x - self.aleft,
y: data.y - self.atop
};
self.setFront();
});
this.onScreenEvent('mouse', this._dragM = function(data) {
if (self.screen._dragging !== self) return;
if (data.action !== 'mousedown' && data.action !== 'mousemove') {
delete self.screen._dragging;
delete self._drag;
return;
}
// This can happen in edge cases where the user is
// already dragging and element when it is detached.
if (!self.parent) return;
var ox = self._drag.x
, oy = self._drag.y
, px = self.parent.aleft
, py = self.parent.atop
, x = data.x - px - ox
, y = data.y - py - oy;
if (self.position.right != null) {
if (self.position.left != null) {
self.width = '100%-' + (self.parent.width - self.width);
}
self.position.right = null;
}
if (self.position.bottom != null) {
if (self.position.top != null) {
self.height = '100%-' + (self.parent.height - self.height);
}
self.position.bottom = null;
}
self.rleft = x;
self.rtop = y;
self.screen.render();
});
return this._draggable = true;
}n/a
enableInput = function () {
this.screen._listenMouse(this);
this.screen._listenKeys(this);
}n/a
enableKeys = function () {
this.screen._listenKeys(this);
}n/a
enableMouse = function () {
this.screen._listenMouse(this);
}...
- __resize__ - Received on screen resize.
- __mouse__ - Received on mouse events.
- __keypress__ - Received on key events.
- __element [name]__ - Global events received for all elements.
- __key [name]__ - Received on key event for [name].
- __focus, blur__ - Received when the terminal window focuses/blurs. Requires a
terminal supporting the focus protocol and focus needs to be passed to
program.enableMouse().
- __prerender__ - Received before render.
- __render__ - Received on render.
- __warning__ - Received when blessed notices something untoward (output is not
a tty, terminfo not found, etc).
- __destroy__ - Received when the screen is destroyed (only useful when using
multiple screens).
...focus = function () {
return this.screen.focused = this;
}...
// Quit on Escape, q, or Control-C.
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
// Focus our element.
box.focus();
// Render the screen.
screen.render();
```
## Documentation
...free = function () {
var listeners = this._slisteners = this._slisteners || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
this.screen.removeListener(listener.type, listener.handler);
}
delete this._slisteners;
}n/a
getBaseLine = function (i) {
var fake = this._clines.rtof[this.childBase || 0];
return this.getLine(fake + i);
}n/a
getContent = function () {
if (!this._clines) return '';
return this._clines.fake.join('\n');
}n/a
getLine = function (i) {
i = Math.max(i, 0);
i = Math.min(i, this._clines.fake.length - 1);
return this._clines.fake[i];
}n/a
getLines = function () {
return this._clines.fake.slice();
}n/a
getScreenLines = function () {
return this._clines.slice();
}n/a
getText = function () {
return this.getContent().replace(/\x1b\[[\d;]*m/g, '');
}n/a
hide = function () {
if (this.hidden) return;
this.clearPos();
this.hidden = true;
this.emit('hide');
if (this.screen.focused === this) {
this.screen.rewindFocus();
}
}n/a
insertBottom = function (line) {
var h = (this.childBase || 0) + this.height - this.iheight
, i = Math.min(h, this._clines.length)
, fake = this._clines.rtof[i - 1] + 1;
return this.insertLine(fake, line);
}n/a
insertLine = function (i, line) {
if (typeof line === 'string') line = line.split('\n');
if (i !== i || i == null) {
i = this._clines.ftor.length;
}
i = Math.max(i, 0);
while (this._clines.fake.length < i) {
this._clines.fake.push('');
this._clines.ftor.push([this._clines.push('') - 1]);
this._clines.rtof(this._clines.fake.length - 1);
}
// NOTE: Could possibly compare the first and last ftor line numbers to see
// if they're the same, or if they fit in the visible region entirely.
var start = this._clines.length
, diff
, real;
if (i >= this._clines.ftor.length) {
real = this._clines.ftor[this._clines.ftor.length - 1];
real = real[real.length - 1] + 1;
} else {
real = this._clines.ftor[i][0];
}
for (var j = 0; j < line.length; j++) {
this._clines.fake.splice(i + j, 0, line[j]);
}
this.setContent(this._clines.fake.join('\n'), true);
diff = this._clines.length - start;
if (diff > 0) {
var pos = this._getCoords();
if (!pos) return;
var height = pos.yl - pos.yi - this.iheight
, base = this.childBase || 0
, visible = real >= base && real - base < height;
if (pos && visible && this.screen.cleanSides(this)) {
this.screen.insertLine(diff,
pos.yi + this.itop + real - base,
pos.yi,
pos.yl - this.ibottom - 1);
}
}
}...
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
...insertTop = function (line) {
var fake = this._clines.rtof[this.childBase || 0];
return this.insertLine(fake, line);
}n/a
key = function () {
return this.screen.program.key.apply(this, arguments);
}...
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
...onScreenEvent = function (type, handler) {
var listeners = this._slisteners = this._slisteners || [];
listeners.push({ type: type, handler: handler });
this.screen.on(type, handler);
}n/a
onceKey = function () {
return this.screen.program.onceKey.apply(this, arguments);
}n/a
onceScreenEvent = function (type, handler) {
var listeners = this._slisteners = this._slisteners || [];
var entry = { type: type, handler: handler };
listeners.push(entry);
this.screen.once(type, function() {
var i = listeners.indexOf(entry);
if (~i) listeners.splice(i, 1);
return handler.apply(this, arguments);
});
}n/a
parseContent = function (noTags) {
if (this.detached) return false;
var width = this.width - this.iwidth;
if (this._clines == null
|| this._clines.width !== width
|| this._clines.content !== this.content) {
var content = this.content;
content = content
.replace(/[\x00-\x08\x0b-\x0c\x0e-\x1a\x1c-\x1f\x7f]/g, '')
.replace(/\x1b(?!\[[\d;]*m)/g, '')
.replace(/\r\n|\r/g, '\n')
.replace(/\t/g, this.screen.tabc);
if (this.screen.fullUnicode) {
// double-width chars will eat the next char after render. create a
// blank character after it so it doesn't eat the real next char.
content = content.replace(unicode.chars.all, '$1\x03');
// iTerm2 cannot render combining characters properly.
if (this.screen.program.isiTerm2) {
content = content.replace(unicode.chars.combining, '');
}
} else {
// no double-width: replace them with question-marks.
content = content.replace(unicode.chars.all, '??');
// delete combining characters since they're 0-width anyway.
// NOTE: We could drop this, the non-surrogates would get changed to ? by
// the unicode filter, and surrogates changed to ? by the surrogate
// regex. however, the user might expect them to be 0-width.
// NOTE: Might be better for performance to drop!
content = content.replace(unicode.chars.combining, '');
// no surrogate pairs: replace them with question-marks.
content = content.replace(unicode.chars.surrogate, '?');
// XXX Deduplicate code here:
// content = helpers.dropUnicode(content);
}
if (!noTags) {
content = this._parseTags(content);
}
this._clines = this._wrapContent(content, width);
this._clines.width = width;
this._clines.content = this.content;
this._clines.attr = this._parseAttr(this._clines);
this._clines.ci = [];
this._clines.reduce(function(total, line) {
this._clines.ci.push(total);
return total + line.length + 1;
}.bind(this), 0);
this._pcontent = this._clines.join('\n');
this.emit('parsed content');
return true;
}
// Need to calculate this every time because the default fg/bg may change.
this._clines.attr = this._parseAttr(this._clines) || this._clines.attr;
return false;
}n/a
popLine = function (n) {
return this.deleteLine(this._clines.fake.length - 1, n);
}n/a
pushLine = function (line) {
if (!this.content) return this.setLine(0, line);
return this.insertLine(this._clines.fake.length, line);
}n/a
removeHover = function () {
delete this._hoverOptions;
if (!this.screen._hoverText || this.screen._hoverText.detached) return;
this.screen._hoverText.detach();
this.screen.render();
}n/a
removeKey = function () {
return this.screen.program.unkey.apply(this, arguments);
}n/a
removeLabel = function () {
if (!this._label) return;
this.removeListener('scroll', this._labelScroll);
this.removeListener('resize', this._labelResize);
this._label.detach();
delete this._labelScroll;
delete this._labelResize;
delete this._label;
}n/a
removeScreenEvent = function (type, handler) {
var listeners = this._slisteners = this._slisteners || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
if (listener.type === type && listener.handler === handler) {
listeners.splice(i, 1);
if (this._slisteners.length === 0) {
delete this._slisteners;
}
break;
}
}
this.screen.removeListener(type, handler);
}n/a
render = function () {
this._emit('prerender');
this.parseContent();
var coords = this._getCoords(true);
if (!coords) {
delete this.lpos;
return;
}
if (coords.xl - coords.xi <= 0) {
coords.xl = Math.max(coords.xl, coords.xi);
return;
}
if (coords.yl - coords.yi <= 0) {
coords.yl = Math.max(coords.yl, coords.yi);
return;
}
var lines = this.screen.lines
, xi = coords.xi
, xl = coords.xl
, yi = coords.yi
, yl = coords.yl
, x
, y
, cell
, attr
, ch
, content = this._pcontent
, ci = this._clines.ci[coords.base]
, battr
, dattr
, c
, visible
, i
, bch = this.ch;
// Clip content if it's off the edge of the screen
// if (xi + this.ileft < 0 || yi + this.itop < 0) {
// var clines = this._clines.slice();
// if (xi + this.ileft < 0) {
// for (var i = 0; i < clines.length; i++) {
// var t = 0;
// var csi = '';
// var csis = '';
// for (var j = 0; j < clines[i].length; j++) {
// while (clines[i][j] === '\x1b') {
// csi = '\x1b';
// while (clines[i][j++] !== 'm') csi += clines[i][j];
// csis += csi;
// }
// if (++t === -(xi + this.ileft) + 1) break;
// }
// clines[i] = csis + clines[i].substring(j);
// }
// }
// if (yi + this.itop < 0) {
// clines = clines.slice(-(yi + this.itop));
// }
// content = clines.join('\n');
// }
if (coords.base >= this._clines.ci.length) {
ci = this._pcontent.length;
}
this.lpos = coords;
if (this.border && this.border.type === 'line') {
this.screen._borderStops[coords.yi] = true;
this.screen._borderStops[coords.yl - 1] = true;
// if (!this.screen._borderStops[coords.yi]) {
// this.screen._borderStops[coords.yi] = { xi: coords.xi, xl: coords.xl };
// } else {
// if (this.screen._borderStops[coords.yi].xi > coords.xi) {
// this.screen._borderStops[coords.yi].xi = coords.xi;
// }
// if (this.screen._borderStops[coords.yi].xl < coords.xl) {
// this.screen._borderStops[coords.yi].xl = coords.xl;
// }
// }
// this.screen._borderStops[coords.yl - 1] = this.screen._borderStops[coords.yi];
}
dattr = this.sattr(this.style);
attr = dattr;
// If we're in a scrollable text box, check to
// see which attributes this line starts with.
if (ci > 0) {
attr = this._clines.attr[Math.min(coords.base, this._clines.length - 1)];
}
if (this.border) xi++, xl--, yi++, yl--;
// If we have padding/valign, that means the
// content-drawing loop will skip a few cells/lines.
// To deal with this, we can just fill the whole thing
// ahead of time. This could be optimized.
if (this.tpadding || (this.valign && this.valign !== 'top')) {
if (this.style.transparent) {
for (y = Math.max(yi, 0); y < yl; y++) {
if (!lines[y]) break;
for (x = Math.max(xi, 0); x < xl; x++) {
if (!lines[y][x]) break;
lines[y][x][0] = colors.blend(attr, lines[y][x][0]);
// lines[y][x][1] = bch;
lines[y].dirty = true;
}
}
} else {
this.screen.fillRegion(dattr, bch, xi, xl, yi, yl);
}
}
if (this.tpadding) {
xi += this.padding.left, xl -= this.padding.right;
yi += this.padding.top, yl -= this.padding.bottom;
}
// Determine where to place the text if it's vertically aligned.
if (this.valign === 'middle' || this.valign === 'bottom') {
visible = yl - yi;
if (this._clines.length < visible) {
if (this.valign === 'middle') {
visible = visible / 2 | 0;
visible -= this._clines.length / 2 | 0;
} else if (this.valign === 'bottom') {
visible -= this._clines.length;
}
ci -= visible * (xl - xi);
}
}
// Draw the content and background.
for (y = yi; y < yl; y++) {
if (!lines[y]) {
if (y >= this.screen.height || yl < this.ibottom) {
break;
} else {
continue;
}
}
for (x = xi; x < ......
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...sattr = function (style, fg, bg) {
var bold = style.bold
, underline = style.underline
, blink = style.blink
, inverse = style.inverse
, invisible = style.invisible;
// if (arguments.length === 1) {
if (fg == null && bg == null) {
fg = style.fg;
bg = style.bg;
}
// This used to be a loop, but I decided
// to unroll it for performance's sake.
if (typeof bold === 'function') bold = bold(this);
if (typeof underline === 'function') underline = underline(this);
if (typeof blink === 'function') blink = blink(this);
if (typeof inverse === 'function') inverse = inverse(this);
if (typeof invisible === 'function') invisible = invisible(this);
if (typeof fg === 'function') fg = fg(this);
if (typeof bg === 'function') bg = bg(this);
// return (this.uid << 24)
// | ((this.dockBorders ? 32 : 0) << 18)
return ((invisible ? 16 : 0) << 18)
| ((inverse ? 8 : 0) << 18)
| ((blink ? 4 : 0) << 18)
| ((underline ? 2 : 0) << 18)
| ((bold ? 1 : 0) << 18)
| (colors.convert(fg) << 9)
| colors.convert(bg);
}n/a
screenshot = function (xi, xl, yi, yl) {
xi = this.lpos.xi + this.ileft + (xi || 0);
if (xl != null) {
xl = this.lpos.xi + this.ileft + (xl || 0);
} else {
xl = this.lpos.xl - this.iright;
}
yi = this.lpos.yi + this.itop + (yi || 0);
if (yl != null) {
yl = this.lpos.yi + this.itop + (yl || 0);
} else {
yl = this.lpos.yl - this.ibottom;
}
return this.screen.screenshot(xi, xl, yi, yl);
}n/a
setBack = function () {
return this.setIndex(0);
}n/a
setBaseLine = function (i, line) {
var fake = this._clines.rtof[this.childBase || 0];
return this.setLine(fake + i, line);
}n/a
setContent = function (content, noClear, noTags) {
if (!noClear) this.clearPos();
this.content = content || '';
this.parseContent(noTags);
this.emit('set content');
}...
height: 'shrink',
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
...setFront = function () {
return this.setIndex(-1);
}n/a
setHover = function (options) {
if (typeof options === 'string') {
options = { text: options };
}
this._hoverOptions = options;
this.enableMouse();
this.screen._initHover();
}n/a
setIndex = function (index) {
if (!this.parent) return;
if (index < 0) {
index = this.parent.children.length + index;
}
index = Math.max(index, 0);
index = Math.min(index, this.parent.children.length - 1);
var i = this.parent.children.indexOf(this);
if (!~i) return;
var item = this.parent.children.splice(i, 1)[0];
this.parent.children.splice(index, 0, item);
}n/a
setLabel = function (options) {
var self = this;
var Box = require('./box');
if (typeof options === 'string') {
options = { text: options };
}
if (this._label) {
this._label.setContent(options.text);
if (options.side !== 'right') {
this._label.rleft = 2 + (this.border ? -1 : 0);
this._label.position.right = undefined;
if (!this.screen.autoPadding) {
this._label.rleft = 2;
}
} else {
this._label.rright = 2 + (this.border ? -1 : 0);
this._label.position.left = undefined;
if (!this.screen.autoPadding) {
this._label.rright = 2;
}
}
return;
}
this._label = new Box({
screen: this.screen,
parent: this,
content: options.text,
top: -this.itop,
tags: this.parseTags,
shrink: true,
style: this.style.label
});
if (options.side !== 'right') {
this._label.rleft = 2 - this.ileft;
} else {
this._label.rright = 2 - this.iright;
}
this._label._isLabel = true;
if (!this.screen.autoPadding) {
if (options.side !== 'right') {
this._label.rleft = 2;
} else {
this._label.rright = 2;
}
this._label.rtop = 0;
}
var reposition = function() {
self._label.rtop = (self.childBase || 0) - self.itop;
if (!self.screen.autoPadding) {
self._label.rtop = (self.childBase || 0);
}
self.screen.render();
};
this.on('scroll', this._labelScroll = function() {
reposition();
});
this.on('resize', this._labelResize = function() {
nextTick(function() {
reposition();
});
});
}n/a
setLine = function (i, line) {
i = Math.max(i, 0);
while (this._clines.fake.length < i) {
this._clines.fake.push('');
}
this._clines.fake[i] = line;
return this.setContent(this._clines.fake.join('\n'), true);
}...
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
...setText = function (content, noClear) {
content = content || '';
content = content.replace(/\x1b\[[\d;]*m/g, '');
return this.setContent(content, noClear, true);
}n/a
shiftLine = function (n) {
return this.deleteLine(0, n);
}n/a
show = function () {
if (!this.hidden) return;
this.hidden = false;
this.emit('show');
}n/a
strWidth = function (text) {
text = this.parseTags
? helpers.stripTags(text)
: text;
return this.screen.fullUnicode
? unicode.strWidth(text)
: helpers.dropUnicode(text).length;
}n/a
toggle = function () {
return this.hidden ? this.show() : this.hide();
}n/a
unkey = function () {
return this.screen.program.unkey.apply(this, arguments);
}n/a
unshiftLine = function (line) {
return this.insertLine(0, line);
}n/a
function Program(options) {
var self = this;
if (!(this instanceof Program)) {
return new Program(options);
}
Program.bind(this);
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.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this._terminal = this._terminal.toLowerCase();
// OSX
this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
|| !!process.env.ITERM_SESSION_ID;
// VTE
// NOTE: lxterminal does not provide an env variable to check for.
// NOTE: gnome-terminal and sakura use a later version of VTE
// which provides VTE_VERSION as well as supports SGR events.
this.isXFCE = /xfce/i.test(process.env.COLORTERM);
this.isTerminator = !!process.env.TERMINATOR_UUID;
this.isLXDE = false;
this.isVTE = !!process.env.VTE_VERSION
|| this.isXFCE
|| this.isTerminator
|| this.isLXDE;
// xterm and rxvt - not accurate
this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
this.isXterm = false;
this.tmux = !!process.env.TMUX;
this.tmuxVersion = (function() {
if (!self.tmux) return 2;
try {
var version = cp.execFileSync('tmux', ['-V'], { encoding: 'utf8' });
return +/^tmux ([\d.]+)/i.exec(version.trim().split('\n')[0])[1];
} catch (e) {
return 2;
}
})();
this._buf = '';
this._flush = this.flush.bind(this);
if (options.tput !== false) {
this.setupTput();
}
this.listen();
}n/a
bind = function (program) {
if (!Program.global) {
Program.global = program;
}
if (!~Program.instances.indexOf(program)) {
Program.instances.push(program);
program.index = Program.total;
Program.total++;
}
if (Program._bound) return;
Program._bound = true;
unshiftEvent(process, 'exit', Program._exitHandler = function() {
Program.instances.forEach(function(program) {
// Potentially reset window title on exit:
// if (program._originalTitle) {
// program.setTitle(program._originalTitle);
// }
// Ensure the buffer is flushed (it should
// always be at this point, but who knows).
program.flush();
// Ensure _exiting is set (could technically
// use process._exiting).
program._exiting = true;
});
});
}n/a
HPositionRelative = function (param) {
if (this.tput) return this.cuf(param);
this.x += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.hpr(param);
return this._write('\x1b[' + (param || '') + 'a');
}n/a
HVPosition = function (row, col) {
if (!this.zero) {
row = (row || 1) - 1;
col = (col || 1) - 1;
} else {
row = row || 0;
col = col || 0;
}
this.y = row;
this.x = col;
this._ncoords();
// Does not exist (?):
// if (this.tput) return this.put.hvp(row, col);
if (this.tput) return this.put.cup(row, col);
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'f');
}n/a
VPositionRelative = function (param) {
if (this.tput) return this.cud(param);
this.y += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.vpr(param);
return this._write('\x1b[' + (param || '') + 'e');
}n/a
_attr = function (param, val) {
var self = this
, 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';
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':
case 'light grey fg':
case 'light gray fg':
case 'bright grey fg':
case 'bright gray 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'
: '\x1b[41m';
case 'green bg':
return val === false
? '\x1b[49m'
: '\x1b[42m';
case 'yellow bg':
return val === false
? '\x1b[49m'
: '\x1b[43m';
case 'blue bg':
return val === false
? '\x1b[49m'
: '\x1b[44m';
case 'magenta bg':
return val === false
? '\x1b[49m'
: '\x1b[45m';
case 'cyan bg':
return val === false
? '\x1b[49m'
: '\x1b[46m';
case 'white bg':
case 'light grey bg':
case 'light gray bg':
case 'bright grey bg':
case 'bright gray bg':
return val === false
? '\x1b[49m'
: '\x1b[47m';
case 'default bg':
if (val === false) return '';
return '\x1b[49m';
// 16-color foreground
case 'light black fg':
case 'bright black fg':
case 'grey fg':
case 'gray fg':
return val === false
? '\x1b[39m'
: '\x1b[90m';
case 'light red fg':
case 'bright red fg':
return val === false
? '\x1b[39m'
: '\x1b[91m';
case 'light green fg':
case 'bright green fg':
return val === false
? '\x1b[39m'
: '\x1b[92m';
case 'light yellow fg':
case 'bright yellow fg':
return val === false
? '\x1b[39m'
: '\x1b[93m';
case 'light blue fg':
case 'bright blue fg':
return val === false
? '\x1b[39m'
: '\x1b[94m';
case 'light magenta fg':
case 'bright magenta fg':
return val === false
? '\x1b[39m'
: '\x1b[95m';
case 'light cyan fg':
case 'b ...n/a
_bindMouse = function (s, buf) {
var self = this
, key
, parts
, b
, x
, y
, mod
, params
, down
, page
, button;
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');
}
}
// if (this.8bit) {
// s = s.replace(/\233/g, '\x1b[');
// buf = new Buffer(s, 'utf8');
// }
// XTerm / X10 for buggy VTE
// VTE can only send unsigned chars and no unicode for coords. This limits
// them to 0xff. However, normally the x10 protocol does not allow a byte
// under 0x20, but since VTE can have the bytes overflow, we can consider
// bytes below 0x20 to be up to 0xff + 0x20. This gives a limit of 287. Since
// characters ranging from 223 to 248 confuse javascript's utf parser, we
// need to parse the raw binary. We can detect whether the terminal is using
// a bugged VTE version by examining the coordinates and seeing whether they
// are a value they would never otherwise be with a properly implemented x10
// protocol. This method of detecting VTE is only 99% reliable because we
// can't check if the coords are 0x00 (255) since that is a valid x10 coord
// technically.
var bx = s.charCodeAt(4);
var by = s.charCodeAt(5);
if (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d
&& (this.isVTE
|| bx >= 65533 || by >= 65533
|| (bx > 0x00 && bx < 0x20)
|| (by > 0x00 && by < 0x20)
|| (buf[4] > 223 && buf[4] < 248 && buf.length === 6)
|| (buf[5] > 223 && buf[5] < 248 && buf.length === 6))) {
b = buf[3];
x = buf[4];
y = buf[5];
// unsigned char overflow.
if (x < 0x20) x += 0xff;
if (y < 0x20) y += 0xff;
// Convert the coordinates into a
// properly formatted x10 utf8 sequence.
s = '\x1b[M'
+ String.fromCharCode(b)
+ String.fromCharCode(x)
+ String.fromCharCode(y);
}
// XTerm / X10
if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) {
b = parts[1].charCodeAt(0);
x = parts[1].charCodeAt(1);
y = parts[1].charCodeAt(2);
key.name = 'mouse';
key.type = 'X10';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x - 32;
key.y = y - 32;
if (this.zero) key.x--, key.y--;
if (x === 0) key.x = 255;
if (y === 0) key.y = 255;
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) {
// NOTE: x10 and urxvt have no way
// of telling which button mouseup used.
key.action = 'mouseup';
key.button = this._lastButton || 'unknown';
delete this._lastButton;
} else {
key.action = 'mousedown';
button = b & 3;
key.button =
button === 0 ? 'left'
: button === 1 ? 'middle'
: button === 2 ? 'right'
: 'unknown';
this._lastButton = key.button;
}
// Probably a movement.
// The *newer* VTE gets mouse movements comepletely wrong.
// This presents a problem: older versions of VTE that get it right might
// be confused by the second conditional in the if statement.
// NOTE: Possibly just switch back to the if statement below.
// none, shift, ctrl, alt
// gnome: 32, 36, 48, 40
// xterm: 35, _, 51, _
// urxvt: 35, _, _, _
// if (key.action === 'mousedown' && key.button === 'unknown') {
if (b === 35 || b === 39 || b === 51 || b === 43
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
delete key.button;
key.action = 'mousemove';
}
self.emit('mouse', key);
return;
}
// URxvt
if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) {
params = parts[1].split(';');
b = +params[0];
x = +params[1];
y = +params[2];
key.name = 'mouse';
key.type = 'u ...n/a
_bindResponse = function (s) {
var 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 ...n/a
_buffer = function (text) {
if (this._exiting) {
this.flush();
this._owrite(text);
return;
}
if (this._buf) {
this._buf += text;
return;
}
this._buf = text;
nextTick(this._flush);
return true;
}n/a
_listenInput = function () {
var keys = require('./keys')
, self = this;
// Input
this.input.on('keypress', this.input._keypressHandler = function(ch, key) {
key = key || { ch: ch };
if (key.name === 'undefined'
&& (key.code === '[M' || key.code === '[I' || key.code === '[O')) {
// A mouse sequence. The `keys` 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;
Program.instances.forEach(function(program) {
if (program.input !== self.input) return;
program.emit('keypress', ch, key);
program.emit('key ' + name, ch, key);
});
});
this.input.on('data', this.input._dataHandler = function(data) {
Program.instances.forEach(function(program) {
if (program.input !== self.input) return;
program.emit('data', data);
});
});
keys.emitKeypressEvents(this.input);
}n/a
_listenOutput = function () {
var self = this;
if (!this.output.isTTY) {
nextTick(function() {
self.emit('warning', 'Output is not a TTY');
});
}
// Output
function resize() {
Program.instances.forEach(function(program) {
if (program.output !== self.output) return;
program.cols = program.output.columns;
program.rows = program.output.rows;
program.emit('resize');
});
}
this.output.on('resize', this.output._resizeHandler = function() {
Program.instances.forEach(function(program) {
if (program.output !== self.output) return;
if (!program.options.resizeTimeout) {
return resize();
}
if (program._resizeTimer) {
clearTimeout(program._resizeTimer);
delete program._resizeTimer;
}
var time = typeof program.options.resizeTimeout === 'number'
? program.options.resizeTimeout
: 300;
program._resizeTimer = setTimeout(resize, time);
});
});
}n/a
_log = function (pre, msg) {
if (!this._logger) return;
return this._logger.write(pre + ': ' + msg + '\n-\n');
}n/a
_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;
}n/a
_owrite = function (text) {
if (!this.output.writable) return;
return this.output.write(text);
}n/a
_twrite = function (data) {
var self = this
, iterations = 0
, timer;
if (this.tmux) {
// Replace all STs with BELs so they can be nested within the DCS code.
data = data.replace(/\x1b\\/g, '\x07');
// Wrap in tmux forward DCS:
data = '\x1bPtmux;\x1b' + data + '\x1b\\';
// If we've never even flushed yet, it means we're still in
// the normal buffer. Wait for alt screen buffer.
if (this.output.bytesWritten === 0) {
timer = setInterval(function() {
if (self.output.bytesWritten > 0 || ++iterations === 50) {
clearInterval(timer);
self.flush();
self._owrite(data);
}
}, 100);
return true;
}
// NOTE: Flushing the buffer is required in some cases.
// The DCS code must be at the start of the output.
this.flush();
// Write out raw now that the buffer is flushed.
return this._owrite(data);
}
return this._write(data);
}n/a
_write = function (text) {
if (this.ret) return text;
if (this.useBuffer) {
return this._buffer(text);
}
return this._owrite(text);
}n/a
ae = function () {
return this.charset('ascii');
}n/a
alternate = function () {
this.isAlt = true;
if (this.tput) return this.put.smcup();
if (this.term('vt') || this.term('linux')) return;
this.setMode('?47');
return this.setMode('?1049');
}n/a
alternateBuffer = function () {
this.isAlt = true;
if (this.tput) return this.put.smcup();
if (this.term('vt') || this.term('linux')) return;
this.setMode('?47');
return this.setMode('?1049');
}...
program.move(data.x, data.y);
program.bg('red');
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
program.enableMouse();
program.hideCursor();
program.clear();
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
...as = function () {
return this.charset('acs');
}n/a
attr = function (param, val) {
return this._write(this._attr(param, val));
}n/a
back = function (param) {
this.x -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_left_cursor) {
return this._write(this.repeat(this.tput.cub1(), param));
}
return this.put.cub(param);
}
return this._write('\x1b[' + (param || '') + 'D');
}n/a
backspace = function () {
this.x--;
this._ncoords();
if (this.has('kbs')) return this.put.kbs();
return this._write('\x08');
}n/a
bel = function () {
if (this.has('bel')) return this.put.bel();
return this._write('\x07');
}n/a
bell = function () {
if (this.has('bel')) return this.put.bel();
return this._write('\x07');
}n/a
bg = function (color, val) {
color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
return this.attr(color, val);
}...
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
program.bg('red');
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
program.enableMouse();
...bindMouse = function () {
if (this._boundMouse) return;
this._boundMouse = true;
var decoder = new StringDecoder('utf8')
, self = this;
this.on('data', function(data) {
var text = decoder.write(data);
if (!text) return;
self._bindMouse(text, data);
});
}n/a
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);
});
}n/a
cbt = function (param) {
this.x -= 8;
this._ncoords();
if (this.tput) return this.put.cbt(param);
return this._write('\x1b[' + (param || 1) + 'Z');
}n/a
cha = function (param) {
if (!this.zero) {
param = (param || 1) - 1;
} else {
param = param || 0;
}
this.x = param;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.hpa(param);
return this._write('\x1b[' + (param + 1) + 'G');
}n/a
charAttributes = function (param, val) {
return this._write(this._attr(param, val));
}n/a
charPosAbsolute = function (param) {
this.x = param || 0;
this._ncoords();
if (this.tput) {
return this.put.hpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + '`');
}n/a
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);
}n/a
cht = function (param) {
this.x += 8;
this._ncoords();
if (this.tput) return this.put.tab(param);
return this._write('\x1b[' + (param || 1) + 'I');
}n/a
civis = function () {
this.cursorHidden = true;
if (this.tput) return this.put.civis();
return this.resetMode('?25');
}n/a
clear = function () {
this.x = 0;
this.y = 0;
if (this.tput) return this.put.clear();
return this._write('\x1b[H\x1b[J');
}...
The main functionality is exposed in the main `blessed` module:
``` js
var blessed = require('blessed')
, program = blessed.program();
program.key('q', function(ch, key) {
program.clear();
program.disableMouse();
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
...cnl = function (param) {
this.y += param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'E');
}n/a
cnorm = function () {
this.cursorHidden = false;
// NOTE: In xterm terminfo:
// cnorm stops blinking cursor
// cvvis starts blinking cursor
if (this.tput) return this.put.cnorm();
//if (this.tput) return this.put.cvvis();
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
// return this._write('\x1b[?12;25h'); // cursor_visible
return this.setMode('?25');
}n/a
copyRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$v');
}n/a
copyToClipboard = function (text) {
if (this.isiTerm2) {
this._twrite('\x1b]50;CopyToCliboard=' + text + '\x07');
return true;
}
return false;
}n/a
cpl = function (param) {
this.y -= param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'F');
}n/a
cr = function () {
this.x = 0;
if (this.has('cr')) return this.put.cr();
return this._write('\r');
}n/a
csr = function (top, bottom) {
if (!this.zero) {
top = (top || 1) - 1;
bottom = (bottom || this.rows) - 1;
} else {
top = top || 0;
bottom = bottom || (this.rows - 1);
}
this.scrollTop = top;
this.scrollBottom = bottom;
this.x = 0;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.csr(top, bottom);
return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
}n/a
cub = function (param) {
this.x -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_left_cursor) {
return this._write(this.repeat(this.tput.cub1(), param));
}
return this.put.cub(param);
}
return this._write('\x1b[' + (param || '') + 'D');
}n/a
cud = function (param) {
this.y += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_down_cursor) {
return this._write(this.repeat(this.tput.cud1(), param));
}
return this.put.cud(param);
}
return this._write('\x1b[' + (param || '') + 'B');
}n/a
cuf = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_right_cursor) {
return this._write(this.repeat(this.tput.cuf1(), param));
}
return this.put.cuf(param);
}
return this._write('\x1b[' + (param || '') + 'C');
}n/a
cup = 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');
}n/a
cursorBackward = function (param) {
this.x -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_left_cursor) {
return this._write(this.repeat(this.tput.cub1(), param));
}
return this.put.cub(param);
}
return this._write('\x1b[' + (param || '') + 'D');
}n/a
cursorBackwardTab = function (param) {
this.x -= 8;
this._ncoords();
if (this.tput) return this.put.cbt(param);
return this._write('\x1b[' + (param || 1) + 'Z');
}n/a
cursorCharAbsolute = function (param) {
if (!this.zero) {
param = (param || 1) - 1;
} else {
param = param || 0;
}
this.x = param;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.hpa(param);
return this._write('\x1b[' + (param + 1) + 'G');
}n/a
cursorColor = function (color) {
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
this._twrite('\x1b]12;' + color + '\x07');
return true;
}
return false;
}n/a
cursorDown = function (param) {
this.y += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_down_cursor) {
return this._write(this.repeat(this.tput.cud1(), param));
}
return this.put.cud(param);
}
return this._write('\x1b[' + (param || '') + 'B');
}n/a
cursorForward = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_right_cursor) {
return this._write(this.repeat(this.tput.cuf1(), param));
}
return this.put.cuf(param);
}
return this._write('\x1b[' + (param || '') + 'C');
}n/a
cursorForwardTab = function (param) {
this.x += 8;
this._ncoords();
if (this.tput) return this.put.tab(param);
return this._write('\x1b[' + (param || 1) + 'I');
}n/a
cursorNextLine = function (param) {
this.y += param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'E');
}n/a
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');
}n/a
cursorPrecedingLine = function (param) {
this.y -= param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'F');
}n/a
cursorReset = function () {
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
// XXX
// return this.resetColors();
this._twrite('\x1b[0 q');
this._twrite('\x1b]112\x07');
// urxvt doesnt support OSC 112
this._twrite('\x1b]12;white\x07');
return true;
}
return false;
}n/a
cursorShape = function (shape, blink) {
if (this.isiTerm2) {
switch (shape) {
case 'block':
if (!blink) {
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07');
} else {
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=1\x07');
}
break;
case 'underline':
if (!blink) {
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=0\x07');
} else {
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=1\x07');
}
break;
case 'line':
if (!blink) {
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=0\x07');
} else {
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07');
}
break;
}
return true;
} else if (this.term('xterm') || this.term('screen')) {
switch (shape) {
case 'block':
if (!blink) {
this._twrite('\x1b[0 q');
} else {
this._twrite('\x1b[1 q');
}
break;
case 'underline':
if (!blink) {
this._twrite('\x1b[2 q');
} else {
this._twrite('\x1b[3 q');
}
break;
case 'line':
if (!blink) {
this._twrite('\x1b[4 q');
} else {
this._twrite('\x1b[5 q');
}
break;
}
return true;
}
return false;
}n/a
cursorUp = function (param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_up_cursor) {
return this._write(this.repeat(this.tput.cuu1(), param));
}
return this.put.cuu(param);
}
return this._write('\x1b[' + (param || '') + 'A');
}n/a
cursor_invisible = function () {
this.cursorHidden = true;
if (this.tput) return this.put.civis();
return this.resetMode('?25');
}n/a
cuu = function (param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_up_cursor) {
return this._write(this.repeat(this.tput.cuu1(), param));
}
return this.put.cuu(param);
}
return this._write('\x1b[' + (param || '') + 'A');
}n/a
cvvis = function () {
this.cursorHidden = false;
// NOTE: In xterm terminfo:
// cnorm stops blinking cursor
// cvvis starts blinking cursor
if (this.tput) return this.put.cnorm();
//if (this.tput) return this.put.cvvis();
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
// return this._write('\x1b[?12;25h'); // cursor_visible
return this.setMode('?25');
}n/a
da = function (param, callback) {
return this.response('device-attributes',
'\x1b[' + (param || '') + 'c', callback);
}n/a
dch = function (param) {
if (this.tput) return this.put.dch(param);
return this._write('\x1b[' + (param || '') + 'P');
}n/a
debug = function () {
if (!this.options.debug) return;
return this._log('DEBUG', util.format.apply(util, arguments));
}n/a
deccara = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$r');
}n/a
deccra = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$v');
}n/a
decdc = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' ~');
}n/a
decefr = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'w');
}n/a
decelr = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'z');
}n/a
decera = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$z');
}n/a
decfra = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$x');
}n/a
decic = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' }');
}n/a
decll = function (param) {
return this._write('\x1b[' + (param || '') + 'q');
}n/a
decrara = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$t');
}n/a
decreqtparm = function (param) {
return this._write('\x1b[' + (param || 0) + 'x');
}n/a
decrqlp = function (param, callback) {
// See also:
// get_mouse / getm / Gm
// mouse_info / minfo / Mi
// Correct for tput?
if (this.has('req_mouse_pos')) {
var code = this.tput.req_mouse_pos(param);
return this.response('locator-position', code, callback);
}
return this.response('locator-position',
'\x1b[' + (param || '') + '\'|', callback);
}n/a
decrqm = function (param) {
return this._write('\x1b[' + (param || '') + '$p');
}n/a
decrqmp = function (param) {
return this._write('\x1b[?' + (param || '') + '$p');
}n/a
decrst = function () {
var param = slice.call(arguments).join(';');
return this.resetMode('?' + param);
}n/a
decsace = function (param) {
return this._write('\x1b[' + (param || 0) + 'x');
}n/a
decsca = function (param) {
return this._write('\x1b[' + (param || 0) + '"q');
}n/a
decscl = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '"p');
}n/a
decscusr = function (param) {
switch (param) {
case 'blinking block':
param = 1;
break;
case 'block':
case 'steady block':
param = 2;
break;
case 'blinking underline':
param = 3;
break;
case 'underline':
case 'steady underline':
param = 4;
break;
case 'blinking bar':
param = 5;
break;
case 'bar':
case 'steady bar':
param = 6;
break;
}
if (param === 2 && this.has('Se')) {
return this.put.Se();
}
if (this.has('Ss')) {
return this.put.Ss(param);
}
return this._write('\x1b[' + (param || 1) + ' q');
}n/a
decsera = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '${');
}n/a
decset = function () {
var param = slice.call(arguments).join(';');
return this.setMode('?' + param);
}n/a
decsle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'{');
}n/a
decsmbv = function (param) {
return this._write('\x1b[' + (param || '') + ' u');
}n/a
decstbm = function (top, bottom) {
if (!this.zero) {
top = (top || 1) - 1;
bottom = (bottom || this.rows) - 1;
} else {
top = top || 0;
bottom = bottom || (this.rows - 1);
}
this.scrollTop = top;
this.scrollBottom = bottom;
this.x = 0;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.csr(top, bottom);
return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
}n/a
decstr = function () {
//if (this.tput) return this.put.init_2string();
//if (this.tput) return this.put.reset_2string();
if (this.tput) return this.put.rs2();
//return this._write('\x1b[!p');
//return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
}n/a
decswbv = function (param) {
return this._write('\x1b[' + (param || '') + ' t');
}n/a
dectcem = function () {
this.cursorHidden = false;
// NOTE: In xterm terminfo:
// cnorm stops blinking cursor
// cvvis starts blinking cursor
if (this.tput) return this.put.cnorm();
//if (this.tput) return this.put.cvvis();
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
// return this._write('\x1b[?12;25h'); // cursor_visible
return this.setMode('?25');
}n/a
dectcemh = function () {
this.cursorHidden = true;
if (this.tput) return this.put.civis();
return this.resetMode('?25');
}n/a
deleteChars = function (param) {
if (this.tput) return this.put.dch(param);
return this._write('\x1b[' + (param || '') + 'P');
}n/a
deleteColumns = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' ~');
}n/a
deleteLines = function (param) {
if (this.tput) return this.put.dl(param);
return this._write('\x1b[' + (param || '') + 'M');
}n/a
destroy = function () {
var index = Program.instances.indexOf(this);
if (~index) {
Program.instances.splice(index, 1);
Program.total--;
this.flush();
this._exiting = true;
Program.global = Program.instances[0];
if (Program.total === 0) {
Program.global = null;
process.removeListener('exit', Program._exitHandler);
delete Program._exitHandler;
delete Program._bound;
}
this.input._blessedInput--;
this.output._blessedOutput--;
if (this.input._blessedInput === 0) {
this.input.removeListener('keypress', this.input._keypressHandler);
this.input.removeListener('data', this.input._dataHandler);
delete this.input._keypressHandler;
delete this.input._dataHandler;
if (this.input.setRawMode) {
if (this.input.isRaw) {
this.input.setRawMode(false);
}
if (!this.input.destroyed) {
this.input.pause();
}
}
}
if (this.output._blessedOutput === 0) {
this.output.removeListener('resize', this.output._resizeHandler);
delete this.output._resizeHandler;
}
this.removeListener('newListener', this._newHandler);
delete this._newHandler;
this.destroyed = true;
this.emit('destroy');
}
}...
output: client,
terminal: 'xterm-256color',
fullUnicode: true
});
client.on('close', function() {
if (!screen.destroyed) {
screen.destroy();
}
});
screen.key(['C-c', 'q'], function(ch, key) {
screen.destroy();
});
...deviceStatus = function (param, callback, dec, noBypass) {
if (dec) {
return this.response('device-status',
'\x1b[?' + (param || '0') + 'n', callback, noBypass);
}
return this.response('device-status',
'\x1b[' + (param || '0') + 'n', callback, noBypass);
}n/a
disableGpm = function () {
if (this.gpm) {
this.gpm.stop();
delete this.gpm;
}
}n/a
disableModifiers = function (param) {
return this._write('\x1b[>' + (param || '') + 'n');
}n/a
disableMouse = function () {
if (!this._currentMouse) return;
var obj = {};
Object.keys(this._currentMouse).forEach(function(key) {
obj[key] = false;
});
return this.setMouse(obj, false);
}...
``` js
var blessed = require('blessed')
, program = blessed.program();
program.key('q', function(ch, key) {
program.clear();
program.disableMouse();
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
...dl = function (param) {
if (this.tput) return this.put.dl(param);
return this._write('\x1b[' + (param || '') + 'M');
}n/a
down = function (param) {
this.y += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_down_cursor) {
return this._write(this.repeat(this.tput.cud1(), param));
}
return this.put.cud(param);
}
return this._write('\x1b[' + (param || '') + 'B');
}...
program.hideCursor();
program.clear();
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
program.setx((program.cols / 2 | 0) - 4);
program.down(5);
program.write('Hi again!');
program.bg('!black');
program.feed();
```
#### Testing
...dsr = function (param, callback, dec, noBypass) {
if (dec) {
return this.response('device-status',
'\x1b[?' + (param || '0') + 'n', callback, noBypass);
}
return this.response('device-status',
'\x1b[' + (param || '0') + 'n', callback, noBypass);
}n/a
dynamicColors = function (param) {
if (this.has('Cs')) {
return this.put.Cs(param);
}
return this._twrite('\x1b]12;' + param + '\x07');
}n/a
ech = function (param) {
if (this.tput) return this.put.ech(param);
return this._write('\x1b[' + (param || '') + 'X');
}n/a
echo = function (text, attr) {
return attr
? this._write(this.text(text, attr))
: this._write(text);
}...
// Make the client look like a tty:
client.setRawMode = function(mode) {
client.isRaw = mode;
if (!client.writable) return;
if (mode) {
client.do.suppress_go_ahead();
client.will.suppress_go_ahead();
client.will.echo();
} else {
client.dont.suppress_go_ahead();
client.wont.suppress_go_ahead();
client.wont.echo();
}
};
client.isTTY = true;
...ed = 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');
}
}n/a
el = 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');
}
}n/a
enableFilterRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'w');
}n/a
enableGpm = function () {
var self = this;
var gpmclient = require('./gpmclient');
if (this.gpm) return;
this.gpm = gpmclient();
this.gpm.on('btndown', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousedown',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('btnup', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mouseup',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('move', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousemove',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('drag', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousemove',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('mousewheel', function(btn, modifier, x, y, dx, dy) {
var key = {
name: 'mouse',
type: 'GPM',
action: dy > 0 ? 'wheelup' : 'wheeldown',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y, dx, dy],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
}n/a
enableLocatorReporting = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'z');
}n/a
enableMouse = function () {
if (process.env.BLESSED_FORCE_MODES) {
var modes = process.env.BLESSED_FORCE_MODES.split(',');
var options = {};
for (var n = 0; n < modes.length; ++n) {
var pair = modes[n].split('=');
var v = pair[1] !== '0';
switch (pair[0].toUpperCase()) {
case 'SGRMOUSE':
options.sgrMouse = v;
break;
case 'UTFMOUSE':
options.utfMouse = v;
break;
case 'VT200MOUSE':
options.vt200Mouse = v;
break;
case 'URXVTMOUSE':
options.urxvtMouse = v;
break;
case 'X10MOUSE':
options.x10Mouse = v;
break;
case 'DECMOUSE':
options.decMouse = v;
break;
case 'PTERMMOUSE':
options.ptermMouse = v;
break;
case 'JSBTERMMOUSE':
options.jsbtermMouse = v;
break;
case 'VT200HILITE':
options.vt200Hilite = v;
break;
case 'GPMMOUSE':
options.gpmMouse = v;
break;
case 'CELLMOTION':
options.cellMotion = v;
break;
case 'ALLMOTION':
options.allMotion = v;
break;
case 'SENDFOCUS':
options.sendFocus = v;
break;
}
}
return this.setMouse(options, true);
}
// NOTE:
// Cell Motion isn't normally need for anything below here, but we'll
// activate it for tmux (whether using it or not) in case our all-motion
// passthrough does not work. It can't hurt.
if (this.term('rxvt-unicode')) {
return this.setMouse({
urxvtMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
// rxvt does not support the X10 UTF extensions
if (this.term('rxvt')) {
return this.setMouse({
vt200Mouse: true,
x10Mouse: true,
cellMotion: true,
allMotion: true
}, true);
}
// libvte is broken. Older versions do not support the
// X10 UTF extension. However, later versions do support
// SGR/URXVT.
if (this.isVTE) {
return this.setMouse({
// NOTE: Could also use urxvtMouse here.
sgrMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
if (this.term('linux')) {
return this.setMouse({
vt200Mouse: true,
gpmMouse: true
}, true);
}
if (this.term('xterm')
|| this.term('screen')
|| (this.tput && this.tput.strings.key_mouse)) {
return this.setMouse({
vt200Mouse: true,
utfMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
}...
- __resize__ - Received on screen resize.
- __mouse__ - Received on mouse events.
- __keypress__ - Received on key events.
- __element [name]__ - Global events received for all elements.
- __key [name]__ - Received on key event for [name].
- __focus, blur__ - Received when the terminal window focuses/blurs. Requires a
terminal supporting the focus protocol and focus needs to be passed to
program.enableMouse().
- __prerender__ - Received before render.
- __render__ - Received on render.
- __warning__ - Received when blessed notices something untoward (output is not
a tty, terminfo not found, etc).
- __destroy__ - Received when the screen is destroyed (only useful when using
multiple screens).
...enter_alt_charset_mode = function () {
return this.charset('acs');
}n/a
eraseChars = function (param) {
if (this.tput) return this.put.ech(param);
return this._write('\x1b[' + (param || '') + 'X');
}n/a
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');
}
}n/a
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');
}
}n/a
eraseRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$z');
}n/a
exit_alt_charset_mode = function () {
return this.charset('ascii');
}n/a
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');
}...
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
program.setx((program.cols / 2 | 0) - 4);
program.down(5);
program.write('Hi again!');
program.bg('!black');
program.feed();
```
#### Testing
Most tests contained in the `test/` directory are interactive. It's up to the
programmer to determine whether the test is properly displayed. In the future
...ff = function () {
if (this.has('ff')) return this.put.ff();
return this._write('\x0c');
}n/a
fg = function (color, val) {
color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
return this.attr(color, val);
}n/a
fillRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$x');
}n/a
flush = function () {
if (!this._buf) return;
this._owrite(this._buf);
this._buf = '';
}n/a
form = function () {
if (this.has('ff')) return this.put.ff();
return this._write('\x0c');
}n/a
forward = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_right_cursor) {
return this._write(this.repeat(this.tput.cuf1(), param));
}
return this.put.cuf(param);
}
return this._write('\x1b[' + (param || '') + 'C');
}n/a
getCursor = function (callback) {
return this.deviceStatus(6, callback, false, true);
}n/a
getCursorColor = function (callback) {
return this.getTextParams(12, callback);
}n/a
getTextParams = function (param, callback) {
return this.response('text-params', '\x1b]' + param + ';?\x07', function(err, data) {
if (err) return callback(err);
return callback(null, data.pt);
});
}n/a
getWindowSize = function (callback) {
return this.manipulateWindow(18, callback);
}n/a
has = function (name) {
return this.tput
? this.tput.has(name)
: false;
}n/a
hideCursor = function () {
this.cursorHidden = true;
if (this.tput) return this.put.civis();
return this.resetMode('?25');
}...
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
program.enableMouse();
program.hideCursor();
program.clear();
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
program.setx((program.cols / 2 | 0) - 4);
program.down(5);
...hpa = function (param) {
this.x = param || 0;
this._ncoords();
if (this.tput) {
return this.put.hpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + '`');
}n/a
hpr = function (param) {
if (this.tput) return this.cuf(param);
this.x += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.hpr(param);
return this._write('\x1b[' + (param || '') + 'a');
}n/a
ht = function () {
this.x += 8;
this._ncoords();
if (this.has('ht')) return this.put.ht();
return this._write('\t');
}n/a
hvp = function (row, col) {
if (!this.zero) {
row = (row || 1) - 1;
col = (col || 1) - 1;
} else {
row = row || 0;
col = col || 0;
}
this.y = row;
this.x = col;
this._ncoords();
// Does not exist (?):
// if (this.tput) return this.put.hvp(row, col);
if (this.tput) return this.put.cup(row, col);
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'f');
}n/a
ich = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.ich(param);
return this._write('\x1b[' + (param || 1) + '@');
}n/a
il = function (param) {
if (this.tput) return this.put.il(param);
return this._write('\x1b[' + (param || '') + 'L');
}n/a
ind = function () {
this.y++;
this._ncoords();
if (this.tput) return this.put.ind();
return this._write('\x1bD');
}n/a
index = function () {
this.y++;
this._ncoords();
if (this.tput) return this.put.ind();
return this._write('\x1bD');
}n/a
initMouseTracking = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + 'T');
}n/a
insertChars = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.ich(param);
return this._write('\x1b[' + (param || 1) + '@');
}n/a
insertColumns = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' }');
}n/a
insertLines = function (param) {
if (this.tput) return this.put.il(param);
return this._write('\x1b[' + (param || '') + 'L');
}n/a
kbs = function () {
this.x--;
this._ncoords();
if (this.has('kbs')) return this.put.kbs();
return this._write('\x08');
}n/a
key = function (key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.on('key ' + key, listener);
}, this);
}...
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
...left = function (param) {
this.x -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_left_cursor) {
return this._write(this.repeat(this.tput.cub1(), param));
}
return this.put.cub(param);
}
return this._write('\x1b[' + (param || '') + 'D');
}n/a
lineHeight = function () {
return this._write('\x1b#');
}n/a
linePosAbsolute = function (param) {
this.y = param || 1;
this._ncoords();
if (this.tput) {
return this.put.vpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'd');
}n/a
listen = function () {
var self = this;
// Potentially reset window title on exit:
// if (!this.isRxvt) {
// if (!this.isVTE) this.setTitleModeFeature(3);
// this.manipulateWindow(21, function(err, data) {
// if (err) return;
// self._originalTitle = data.text;
// });
// }
// Listen for keys/mouse on input
if (!this.input._blessedInput) {
this.input._blessedInput = 1;
this._listenInput();
} else {
this.input._blessedInput++;
}
this.on('newListener', this._newHandler = function fn(type) {
if (type === 'keypress' || type === 'mouse') {
self.removeListener('newListener', fn);
if (self.input.setRawMode && !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();
}
});
// Listen for resize on output
if (!this.output._blessedOutput) {
this.output._blessedOutput = 1;
this._listenOutput();
} else {
this.output._blessedOutput++;
}
}...
width: '80%',
height: '90%',
border: 'line',
content: 'Welcome to my server. Here is your own private session.'
});
screen.render();
}).listen(2300);
```
Once you've written something similar and started it, you can simply telnet
into your blessed app:
``` bash
$ telnet localhost 2300
...loadLEDs = function (param) {
return this._write('\x1b[' + (param || '') + 'q');
}n/a
log = function () {
return this._log('LOG', util.format.apply(util, arguments));
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...lrestoreCursor = function (key, hide) {
var pos;
key = key || 'local';
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();
}
}
}n/a
lsaveCursor = function (key) {
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;
}n/a
manipulateWindow = function () {
var args = slice.call(arguments);
var callback = typeof args[args.length - 1] === 'function'
? args.pop()
: function() {};
return this.response('window-manipulation',
'\x1b[' + args.join(';') + 't', callback);
}n/a
mc = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + 'i');
}n/a
mc0 = function () {
if (this.tput) return this.put.mc0();
return this.mc('0');
}n/a
mc4 = function () {
if (this.tput) return this.put.mc4();
return this.mc('4');
}n/a
mc5 = function () {
if (this.tput) return this.put.mc5();
return this.mc('5');
}n/a
mc5p = function () {
if (this.tput) return this.put.mc5p();
return this.mc('?5');
}n/a
mediaCopy = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + 'i');
}n/a
move = function (x, y) {
return this.cursorPos(y, x);
}...
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
program.bg('red');
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
...nel = 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');
}n/a
newline = 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');
}n/a
nextLine = function () {
this.y++;
this.x = 0;
this._ncoords();
if (this.has('nel')) return this.put.nel();
return this._write('\x1bE');
}n/a
normalBuffer = function () {
this.isAlt = false;
if (this.tput) return this.put.rmcup();
this.resetMode('?47');
return this.resetMode('?1049');
}...
var blessed = require('blessed')
, program = blessed.program();
program.key('q', function(ch, key) {
program.clear();
program.disableMouse();
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
program.bg('red');
...nul = function () {
//if (this.has('pad')) return this.put.pad();
return this._write('\200');
}n/a
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);
}
}n/a
onceKey = function (key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.once('key ' + key, listener);
}, this);
}n/a
out = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
this.ret = true;
var out = this[name].apply(this, args);
this.ret = false;
return out;
}n/a
pO = function () {
if (this.tput) return this.put.mc5p();
return this.mc('?5');
}n/a
pause = function (callback) {
var self = this
, isAlt = this.isAlt
, mouseEnabled = this.mouseEnabled;
this.lsaveCursor('pause');
//this.csr(0, screen.height - 1);
if (isAlt) this.normalBuffer();
this.showCursor();
if (mouseEnabled) this.disableMouse();
var write = this.output.write;
this.output.write = function() {};
if (this.input.setRawMode) {
this.input.setRawMode(false);
}
this.input.pause();
return this._resume = function() {
delete self._resume;
if (self.input.setRawMode) {
self.input.setRawMode(true);
}
self.input.resume();
self.output.write = write;
if (isAlt) self.alternateBuffer();
//self.csr(0, screen.height - 1);
if (mouseEnabled) self.enableMouse();
self.lrestoreCursor('pause', true);
if (callback) callback();
};
}n/a
pf = function () {
if (this.tput) return this.put.mc4();
return this.mc('4');
}n/a
po = function () {
if (this.tput) return this.put.mc5();
return this.mc('5');
}n/a
pos = 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');
}n/a
print = function (text, attr) {
return attr
? this._write(this.text(text, attr))
: this._write(text);
}n/a
print_screen = function () {
if (this.tput) return this.put.mc0();
return this.mc('0');
}n/a
prtr_non = function () {
if (this.tput) return this.put.mc5p();
return this.mc('?5');
}n/a
prtr_off = function () {
if (this.tput) return this.put.mc4();
return this.mc('4');
}n/a
prtr_on = function () {
if (this.tput) return this.put.mc5();
return this.mc('5');
}n/a
ps = function () {
if (this.tput) return this.put.mc0();
return this.mc('0');
}n/a
rc = 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');
}n/a
rcA = function () {
this.x = this.savedX || 0;
this.y = this.savedY || 0;
if (this.tput) return this.put.rc();
return this._write('\x1b[u');
}n/a
removeKey = function (key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.removeListener('key ' + key, listener);
}, this);
}n/a
rep = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.rep(param);
return this._write('\x1b[' + (param || 1) + 'b');
}n/a
repeat = function (ch, i) {
if (!i || i < 0) i = 0;
return Array(i + 1).join(ch);
}n/a
repeatPrecedingCharacter = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.rep(param);
return this._write('\x1b[' + (param || 1) + 'b');
}n/a
req_mouse_pos = function (param, callback) {
// See also:
// get_mouse / getm / Gm
// mouse_info / minfo / Mi
// Correct for tput?
if (this.has('req_mouse_pos')) {
var code = this.tput.req_mouse_pos(param);
return this.response('locator-position', code, callback);
}
return this.response('locator-position',
'\x1b[' + (param || '') + '\'|', callback);
}n/a
reqmp = function (param, callback) {
// See also:
// get_mouse / getm / Gm
// mouse_info / minfo / Mi
// Correct for tput?
if (this.has('req_mouse_pos')) {
var code = this.tput.req_mouse_pos(param);
return this.response('locator-position', code, callback);
}
return this.response('locator-position',
'\x1b[' + (param || '') + '\'|', callback);
}n/a
requestAnsiMode = function (param) {
return this._write('\x1b[' + (param || '') + '$p');
}n/a
requestLocatorPosition = function (param, callback) {
// See also:
// get_mouse / getm / Gm
// mouse_info / minfo / Mi
// Correct for tput?
if (this.has('req_mouse_pos')) {
var code = this.tput.req_mouse_pos(param);
return this.response('locator-position', code, callback);
}
return this.response('locator-position',
'\x1b[' + (param || '') + '\'|', callback);
}n/a
requestParameters = function (param) {
return this._write('\x1b[' + (param || 0) + 'x');
}n/a
requestPrivateMode = function (param) {
return this._write('\x1b[?' + (param || '') + '$p');
}n/a
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');
}n/a
resetColors = function (param) {
if (this.has('Cr')) {
return this.put.Cr(param);
}
return this._twrite('\x1b]112\x07');
//return this._twrite('\x1b]112;' + param + '\x07');
}n/a
resetCursor = function () {
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
// XXX
// return this.resetColors();
this._twrite('\x1b[0 q');
this._twrite('\x1b]112\x07');
// urxvt doesnt support OSC 112
this._twrite('\x1b]12;white\x07');
return true;
}
return false;
}n/a
resetMode = function () {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'l');
}n/a
resetTitleModes = function () {
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'T');
}n/a
response = function (name, text, callback, noBypass) {
var self = this;
if (arguments.length === 2) {
callback = text;
text = name;
name = null;
}
if (!callback) {
callback = function() {};
}
this.bindResponse();
name = name
? 'response ' + name
: 'response';
var onresponse;
this.once(name, onresponse = function(event) {
if (timeout) clearTimeout(timeout);
if (event.type === 'error') {
return callback(new Error(event.event + ': ' + event.text));
}
return callback(null, event);
});
var timeout = setTimeout(function() {
self.removeListener(name, onresponse);
return callback(new Error('Timeout.'));
}, 2000);
return noBypass
? this._write(text)
: this._twrite(text);
}n/a
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');
}n/a
restoreCursorA = function () {
this.x = this.savedX || 0;
this.y = this.savedY || 0;
if (this.tput) return this.put.rc();
return this._write('\x1b[u');
}n/a
restorePrivateValues = function () {
return this._write('\x1b[?' + slice.call(arguments).join(';') + 'r');
}n/a
restoreReportedCursor = function () {
if (this._rx == null) return;
return this.cup(this._ry, this._rx);
// return this.nel();
}n/a
resume = function () {
if (this._resume) return this._resume();
}n/a
return = function () {
this.x = 0;
if (this.has('cr')) return this.put.cr();
return this._write('\r');
}n/a
reverse = function () {
this.y--;
this._ncoords();
if (this.tput) return this.put.ri();
return this._write('\x1bM');
}n/a
reverseAttrInRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$t');
}n/a
reverseIndex = function () {
this.y--;
this._ncoords();
if (this.tput) return this.put.ri();
return this._write('\x1bM');
}n/a
ri = function () {
this.y--;
this._ncoords();
if (this.tput) return this.put.ri();
return this._write('\x1bM');
}n/a
right = function (param) {
this.x += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_right_cursor) {
return this._write(this.repeat(this.tput.cuf1(), param));
}
return this.put.cuf(param);
}
return this._write('\x1b[' + (param || '') + 'C');
}n/a
rm = function () {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'l');
}n/a
rmacs = function () {
return this.charset('ascii');
}n/a
rmcup = function () {
this.isAlt = false;
if (this.tput) return this.put.rmcup();
this.resetMode('?47');
return this.resetMode('?1049');
}n/a
rmove = function (x, y) {
this.rsetx(x);
this.rsety(y);
}n/a
rs2 = function () {
//if (this.tput) return this.put.init_2string();
//if (this.tput) return this.put.reset_2string();
if (this.tput) return this.put.rs2();
//return this._write('\x1b[!p');
//return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
}n/a
rsetx = function (x) {
// return this.HPositionRelative(x);
if (!x) return;
return x > 0
? this.forward(x)
: this.back(-x);
}n/a
rsety = function (y) {
// return this.VPositionRelative(y);
if (!y) return;
return y > 0
? this.up(y)
: this.down(-y);
}n/a
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');
}n/a
saveCursorA = function () {
this.savedX = this.x;
this.savedY = this.y;
if (this.tput) return this.put.sc();
return this._write('\x1b[s');
}n/a
savePrivateValues = function () {
return this._write('\x1b[?' + slice.call(arguments).join(';') + 's');
}n/a
saveReportedCursor = function (callback) {
var self = this;
if (this.tput.strings.user7 === '\x1b[6n' || this.term('screen')) {
return this.getCursor(function(err, data) {
if (data) {
self._rx = data.status.x;
self._ry = data.status.y;
}
if (!callback) return;
return callback(err);
});
}
if (!callback) return;
return callback();
}n/a
sc = 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');
}n/a
scA = function () {
this.savedX = this.x;
this.savedY = this.y;
if (this.tput) return this.put.sc();
return this._write('\x1b[s');
}n/a
scrollDown = function (param) {
this.y += param || 1;
this._ncoords();
if (this.tput) return this.put.parm_rindex(param);
return this._write('\x1b[' + (param || 1) + 'T');
}n/a
scrollUp = function (param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) return this.put.parm_index(param);
return this._write('\x1b[' + (param || 1) + 'S');
}n/a
sd = function (param) {
this.y += param || 1;
this._ncoords();
if (this.tput) return this.put.parm_rindex(param);
return this._write('\x1b[' + (param || 1) + 'T');
}n/a
selData = function (a, b) {
if (this.has('Ms')) {
return this.put.Ms(a, b);
}
return this._twrite('\x1b]52;' + a + ';' + b + '\x07');
}n/a
selectChangeExtent = function (param) {
return this._write('\x1b[' + (param || 0) + 'x');
}n/a
selectiveEraseRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '${');
}n/a
sendDeviceAttributes = function (param, callback) {
return this.response('device-attributes',
'\x1b[' + (param || '') + 'c', callback);
}n/a
setAttrInRectangle = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$r');
}n/a
setBackground = function (color, val) {
color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
return this.attr(color, val);
}n/a
setCharProtectionAttr = function (param) {
return this._write('\x1b[' + (param || 0) + '"q');
}n/a
setConformanceLevel = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '"p');
}n/a
setCursorStyle = function (param) {
switch (param) {
case 'blinking block':
param = 1;
break;
case 'block':
case 'steady block':
param = 2;
break;
case 'blinking underline':
param = 3;
break;
case 'underline':
case 'steady underline':
param = 4;
break;
case 'blinking bar':
param = 5;
break;
case 'bar':
case 'steady bar':
param = 6;
break;
}
if (param === 2 && this.has('Se')) {
return this.put.Se();
}
if (this.has('Ss')) {
return this.put.Ss(param);
}
return this._write('\x1b[' + (param || 1) + ' q');
}n/a
setForeground = function (color, val) {
color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
return this.attr(color, val);
}n/a
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);
}n/a
setLocatorEvents = function () {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'{');
}n/a
setMarginBellVolume = function (param) {
return this._write('\x1b[' + (param || '') + ' u');
}n/a
setMode = function () {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'h');
}n/a
setMouse = function (opt, enable) {
if (opt.normalMouse != null) {
opt.vt200Mouse = opt.normalMouse;
opt.allMotion = opt.normalMouse;
}
if (opt.hiliteTracking != null) {
opt.vt200Hilite = opt.hiliteTracking;
}
if (enable === true) {
if (this._currentMouse) {
this.setMouse(opt);
Object.keys(opt).forEach(function(key) {
this._currentMouse[key] = opt[key];
}, this);
return;
}
this._currentMouse = opt;
this.mouseEnabled = true;
} else if (enable === false) {
delete this._currentMouse;
this.mouseEnabled = false;
}
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking.
// Ps = 9 -> Don't send Mouse X & Y on button press.
// x10 mouse
if (opt.x10Mouse != null) {
if (opt.x10Mouse) this.setMode('?9');
else this.resetMode('?9');
}
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// vt200 mouse
if (opt.vt200Mouse != null) {
if (opt.vt200Mouse) this.setMode('?1000');
else this.resetMode('?1000');
}
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
if (opt.vt200Hilite != null) {
if (opt.vt200Hilite) this.setMode('?1001');
else this.resetMode('?1001');
}
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
// button event mouse
if (opt.cellMotion != null) {
if (opt.cellMotion) this.setMode('?1002');
else this.resetMode('?1002');
}
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
// any event mouse
if (opt.allMotion != null) {
// NOTE: Latest versions of tmux seem to only support cellMotion (not
// allMotion). We pass all motion through to the terminal.
if (this.tmux && this.tmuxVersion >= 2) {
if (opt.allMotion) this._twrite('\x1b[?1003h');
else this._twrite('\x1b[?1003l');
} else {
if (opt.allMotion) this.setMode('?1003');
else this.resetMode('?1003');
}
}
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
if (opt.sendFocus != null) {
if (opt.sendFocus) this.setMode('?1004');
else this.resetMode('?1004');
}
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
if (opt.utfMouse != null) {
if (opt.utfMouse) this.setMode('?1005');
else this.resetMode('?1005');
}
// sgr mouse
if (opt.sgrMouse != null) {
if (opt.sgrMouse) this.setMode('?1006');
else this.resetMode('?1006');
}
// urxvt mouse
if (opt.urxvtMouse != null) {
if (opt.urxvtMouse) this.setMode('?1015');
else this.resetMode('?1015');
}
// dec mouse
if (opt.decMouse != null) {
if (opt.decMouse) this._write('\x1b[1;2\'z\x1b[1;3\'{');
else this._write('\x1b[\'z');
}
// pterm mouse
if (opt.ptermMouse != null) {
if (opt.ptermMouse) this._write('\x1b[>1h\x1b[>6h\x1b[>7h\x1b[>1h\x1b[>9l');
else this._write('\x1b[>1l\x1b[>6l\x1b[>7l\x1b[>1l\x1b[>9h');
}
// jsbterm mouse
if (opt.jsbtermMouse != null) {
// + = advanced mode
if (opt.jsbtermMouse) this._write('\x1b[0~ZwLMRK+1Q\x1b\\');
else this._write('\x1b[0~ZwQ\x1b\\');
}
// gpm mouse
if (opt.gpmMouse != null) {
if (opt.gpmMouse) this.enableGpm();
else this.disableGpm();
}
}n/a
setPointerMode = function (param) {
return this._write('\x1b[>' + (param || '') + 'p');
}n/a
setResources = function () {
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'm');
}n/a
setScrollRegion = function (top, bottom) {
if (!this.zero) {
top = (top || 1) - 1;
bottom = (bottom || this.rows) - 1;
} else {
top = top || 0;
bottom = bottom || (this.rows - 1);
}
this.scrollTop = top;
this.scrollBottom = bottom;
this.x = 0;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.csr(top, bottom);
return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
}n/a
setTerminal = function (terminal) {
this._terminal = terminal.toLowerCase();
delete this._tputSetup;
this.setupTput();
}...
- __aleft__ - Absolute left offset, always zero.
- __aright__ - Absolute right offset, always zero.
- __atop__ - Absolute top offset, always zero.
- __abottom__ - Absolute bottom offset, always zero.
- __grabKeys__ - Whether the focused element grabs all keypresses.
- __lockKeys__ - Prevent keypresses from being received by any element.
- __hover__ - The currently hovered element. Only set if mouse events are bound.
- __terminal__ - Set or get terminal name. `Set` calls `screen.setTerminal()`
internally.
- __title__ - Set or get window title.
##### Events:
- Inherits all from Node.
- __resize__ - Received on screen resize.
...setTitle = function (title) {
this._title = title;
// if (this.term('screen')) {
// // Tmux pane
// // if (this.tmux) {
// // return this._write('\x1b]2;' + title + '\x1b\\');
// // }
// return this._write('\x1bk' + title + '\x1b\\');
// }
return this._twrite('\x1b]0;' + title + '\x07');
}n/a
setTitleModeFeature = function () {
return this._twrite('\x1b[>' + slice.call(arguments).join(';') + 't');
}n/a
setWarningBellVolume = function (param) {
return this._write('\x1b[' + (param || '') + ' t');
}n/a
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);
};
}n/a
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({
terminal: this.terminal,
padding: options.padding,
extended: options.extended,
printf: options.printf,
termcap: options.termcap,
forceUnicode: options.forceUnicode
});
if (tput.error) {
nextTick(function() {
self.emit('warning', tput.error.message);
});
}
if (tput.padding) {
nextTick(function() {
self.emit('warning', 'Terminfo padding has been enabled.');
});
}
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 (tput.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));
};
}
});
}n/a
setx = function (x) {
return this.cursorCharAbsolute(x);
// return this.charPosAbsolute(x);
}...
program.enableMouse();
program.hideCursor();
program.clear();
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
program.setx((program.cols / 2 | 0) - 4);
program.down(5);
program.write('Hi again!');
program.bg('!black');
program.feed();
```
...sety = function (y) {
return this.linePosAbsolute(y);
}n/a
sgr = function (param, val) {
return this._write(this._attr(param, val));
}n/a
shiftIn = function () {
// if (this.has('S3')) return this.put.S3();
return this._write('\x0f');
}n/a
shiftOut = function () {
// if (this.has('S2')) return this.put.S2();
return this._write('\x0e');
}n/a
showCursor = function () {
this.cursorHidden = false;
// NOTE: In xterm terminfo:
// cnorm stops blinking cursor
// cvvis starts blinking cursor
if (this.tput) return this.put.cnorm();
//if (this.tput) return this.put.cvvis();
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
// return this._write('\x1b[?12;25h'); // cursor_visible
return this.setMode('?25');
}...
``` js
var blessed = require('blessed')
, program = blessed.program();
program.key('q', function(ch, key) {
program.clear();
program.disableMouse();
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
...sigtstp = function (callback) {
var resume = this.pause();
process.once('SIGCONT', function() {
resume();
if (callback) callback();
});
process.kill(process.pid, 'SIGTSTP');
}n/a
simpleInsert = function (ch, i, attr) {
return this._write(this.repeat(ch, i), attr);
}n/a
sm = function () {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'h');
}n/a
smacs = function () {
return this.charset('acs');
}n/a
smcup = function () {
this.isAlt = true;
if (this.tput) return this.put.smcup();
if (this.term('vt') || this.term('linux')) return;
this.setMode('?47');
return this.setMode('?1049');
}n/a
softReset = function () {
//if (this.tput) return this.put.init_2string();
//if (this.tput) return this.put.reset_2string();
if (this.tput) return this.put.rs2();
//return this._write('\x1b[!p');
//return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
}n/a
su = function (param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) return this.put.parm_index(param);
return this._write('\x1b[' + (param || 1) + 'S');
}n/a
tab = function () {
this.x += 8;
this._ncoords();
if (this.has('ht')) return this.put.ht();
return this._write('\t');
}n/a
tabClear = function (param) {
if (this.tput) return this.put.tbc(param);
return this._write('\x1b[' + (param || 0) + 'g');
}n/a
tabSet = function () {
if (this.tput) return this.put.hts();
return this._write('\x1bH');
}n/a
tbc = function (param) {
if (this.tput) return this.put.tbc(param);
return this._write('\x1b[' + (param || 0) + 'g');
}n/a
term = function (is) {
return this.terminal.indexOf(is) === 0;
}n/a
text = function (text, attr) {
return this._attr(attr, true) + text + this._attr(attr, false);
}n/a
unkey = function (key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.removeListener('key ' + key, listener);
}, this);
}n/a
up = function (param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_up_cursor) {
return this._write(this.repeat(this.tput.cuu1(), param));
}
return this.put.cuu(param);
}
return this._write('\x1b[' + (param || '') + 'A');
}n/a
vi = function () {
this.cursorHidden = true;
if (this.tput) return this.put.civis();
return this.resetMode('?25');
}n/a
vpa = function (param) {
this.y = param || 1;
this._ncoords();
if (this.tput) {
return this.put.vpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'd');
}n/a
vpr = function (param) {
if (this.tput) return this.cud(param);
this.y += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.vpr(param);
return this._write('\x1b[' + (param || '') + 'e');
}n/a
vtab = function () {
this.y++;
this._ncoords();
return this._write('\x0b');
}n/a
write = function (text) {
if (!this.output.writable) return;
return this.output.write(text);
}...
var blessed = require('blessed');
var tput = blessed.tput({
terminal: 'xterm-256color',
extended: true
});
process.stdout.write(tput.setaf(4) + 'Hello' + tput.sgr0() + '\n');
```
To play around with it on the command line, it works just like tput:
``` bash
$ tput.js setaf 2
$ tput.js sgr0
...function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}n/a
bind = function (screen) {
if (!Screen.global) {
Screen.global = screen;
}
if (!~Screen.instances.indexOf(screen)) {
Screen.instances.push(screen);
screen.index = Screen.total;
Screen.total++;
}
if (Screen._bound) return;
Screen._bound = true;
process.on('uncaughtException', Screen._exceptionHandler = function(err) {
if (process.listeners('uncaughtException').length > 1) {
return;
}
Screen.instances.slice().forEach(function(screen) {
screen.destroy();
});
err = err || new Error('Uncaught Exception.');
console.error(err.stack ? err.stack + '' : err + '');
nextTick(function() {
process.exit(1);
});
});
['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(function(signal) {
var name = '_' + signal.toLowerCase() + 'Handler';
process.on(signal, Screen[name] = function() {
if (process.listeners(signal).length > 1) {
return;
}
nextTick(function() {
process.exit(0);
});
});
});
process.on('exit', Screen._exitHandler = function() {
Screen.instances.slice().forEach(function(screen) {
screen.destroy();
});
});
}n/a
_cursorAttr = function (cursor, dattr) {
var attr = dattr || this.dattr
, cattr
, ch;
if (cursor.shape === 'line') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
ch = '\u2502';
} else if (cursor.shape === 'underline') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
attr |= 2 << 18;
} else if (cursor.shape === 'block') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
attr |= 8 << 18;
} else if (typeof cursor.shape === 'object' && cursor.shape) {
cattr = Element.prototype.sattr.call(cursor, cursor.shape);
if (cursor.shape.bold || cursor.shape.underline
|| cursor.shape.blink || cursor.shape.inverse
|| cursor.shape.invisible) {
attr &= ~(0x1ff << 18);
attr |= ((cattr >> 18) & 0x1ff) << 18;
}
if (cursor.shape.fg) {
attr &= ~(0x1ff << 9);
attr |= ((cattr >> 9) & 0x1ff) << 9;
}
if (cursor.shape.bg) {
attr &= ~(0x1ff << 0);
attr |= cattr & 0x1ff;
}
if (cursor.shape.ch) {
ch = cursor.shape.ch;
}
}
if (cursor.color != null) {
attr &= ~(0x1ff << 9);
attr |= cursor.color << 9;
}
return {
ch: ch,
attr: attr
};
}n/a
_destroy = function () {
this.detach();
this.forDescendants(function(el) {
el.free();
el.destroyed = true;
el.emit('destroy');
}, this);
}n/a
_dockBorders = function () {
var lines = this.lines
, stops = this._borderStops
, i
, y
, x
, ch;
// var keys, stop;
//
// keys = Object.keys(this._borderStops)
// .map(function(k) { return +k; })
// .sort(function(a, b) { return a - b; });
//
// for (i = 0; i < keys.length; i++) {
// y = keys[i];
// if (!lines[y]) continue;
// stop = this._borderStops[y];
// for (x = stop.xi; x < stop.xl; x++) {
stops = Object.keys(stops)
.map(function(k) { return +k; })
.sort(function(a, b) { return a - b; });
for (i = 0; i < stops.length; i++) {
y = stops[i];
if (!lines[y]) continue;
for (x = 0; x < this.width; x++) {
ch = lines[y][x][1];
if (angles[ch]) {
lines[y][x][1] = this._getAngle(lines, x, y);
lines[y].dirty = true;
}
}
}
}n/a
_focus = function (self, old) {
// Find a scrollable ancestor if we have one.
var el = self;
while (el = el.parent) {
if (el.scrollable) break;
}
// If we're in a scrollable element,
// automatically scroll to the focused element.
if (el && !el.detached) {
// NOTE: This is different from the other "visible" values - it needs the
// visible height of the scrolling element itself, not the element within
// it.
var visible = self.screen.height - el.atop - el.itop - el.abottom - el.ibottom;
if (self.rtop < el.childBase) {
el.scrollTo(self.rtop);
self.screen.render();
} else if (self.rtop + self.height - self.ibottom > el.childBase + visible) {
// Explanation for el.itop here: takes into account scrollable elements
// with borders otherwise the element gets covered by the bottom border:
el.scrollTo(self.rtop - (el.height - self.height) + el.itop, true);
self.screen.render();
}
}
if (old) {
old.emit('blur', self);
}
self.emit('focus', old);
}n/a
_getAngle = function (lines, x, y) {
var angle = 0
, attr = lines[y][x][0]
, ch = lines[y][x][1];
if (lines[y][x - 1] && langles[lines[y][x - 1][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y][x - 1][0] !== attr) return ch;
}
angle |= 1 << 3;
}
if (lines[y - 1] && uangles[lines[y - 1][x][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y - 1][x][0] !== attr) return ch;
}
angle |= 1 << 2;
}
if (lines[y][x + 1] && rangles[lines[y][x + 1][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y][x + 1][0] !== attr) return ch;
}
angle |= 1 << 1;
}
if (lines[y + 1] && dangles[lines[y + 1][x][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y + 1][x][0] !== attr) return ch;
}
angle |= 1 << 0;
}
// Experimental: fixes this situation:
// +----------+
// | <-- empty space here, should be a T angle
// +-------+ |
// | | |
// +-------+ |
// | |
// +----------+
// if (uangles[lines[y][x][1]]) {
// if (lines[y + 1] && cdangles[lines[y + 1][x][1]]) {
// if (!this.options.ignoreDockContrast) {
// if (lines[y + 1][x][0] !== attr) return ch;
// }
// angle |= 1 << 0;
// }
// }
return angleTable[angle] || ch;
}n/a
_getPos = function () {
return this;
}n/a
_initHover = function () {
var self = this;
if (this._hoverText) {
return;
}
this._hoverText = new Box({
screen: this,
left: 0,
top: 0,
tags: false,
height: 'shrink',
width: 'shrink',
border: 'line',
style: {
border: {
fg: 'default'
},
bg: 'default',
fg: 'default'
}
});
this.on('mousemove', function(data) {
if (self._hoverText.detached) return;
self._hoverText.rleft = data.x + 1;
self._hoverText.rtop = data.y;
self.render();
});
this.on('element mouseover', function(el, data) {
if (!el._hoverOptions) return;
self._hoverText.parseTags = el.parseTags;
self._hoverText.setContent(el._hoverOptions.text);
self.append(self._hoverText);
self._hoverText.rleft = data.x + 1;
self._hoverText.rtop = data.y;
self.render();
});
this.on('element mouseout', function() {
if (self._hoverText.detached) return;
self._hoverText.detach();
self.render();
});
// XXX This can cause problems if the
// terminal does not support allMotion.
// Workaround: check to see if content is set.
this.on('element mouseup', function(el) {
if (!self._hoverText.getContent()) return;
if (!el._hoverOptions) return;
self.append(self._hoverText);
self.render();
});
}n/a
_listenKeys = function (el) {
var self = this;
if (el && !~this.keyable.indexOf(el)) {
el.keyable = true;
this.keyable.push(el);
}
if (this._listenedKeys) return;
this._listenedKeys = true;
// NOTE: The event emissions used to be reversed:
// element + screen
// They are now:
// screen + element
// After the first keypress emitted, the handler
// checks to make sure grabKeys, lockKeys, and focused
// weren't changed, and handles those situations appropriately.
this.program.on('keypress', function(ch, key) {
if (self.lockKeys && !~self.ignoreLocked.indexOf(key.full)) {
return;
}
var focused = self.focused
, grabKeys = self.grabKeys;
if (!grabKeys || ~self.ignoreLocked.indexOf(key.full)) {
self.emit('keypress', ch, key);
self.emit('key ' + key.full, ch, key);
}
// If something changed from the screen key handler, stop.
if (self.grabKeys !== grabKeys || self.lockKeys) {
return;
}
if (focused && focused.keyable) {
focused.emit('keypress', ch, key);
focused.emit('key ' + key.full, ch, key);
}
});
}n/a
_listenMouse = function (el) {
var self = this;
if (el && !~this.clickable.indexOf(el)) {
el.clickable = true;
this.clickable.push(el);
}
if (this._listenedMouse) return;
this._listenedMouse = true;
this.program.enableMouse();
if (this.options.sendFocus) {
this.program.setMouse({ sendFocus: true }, true);
}
this.on('render', function() {
self._needsClickableSort = true;
});
this.program.on('mouse', function(data) {
if (self.lockKeys) return;
if (self._needsClickableSort) {
self.clickable = helpers.hsort(self.clickable);
self._needsClickableSort = false;
}
var i = 0
, el
, set
, pos;
for (; i < self.clickable.length; i++) {
el = self.clickable[i];
if (el.detached || !el.visible) {
continue;
}
// if (self.grabMouse && self.focused !== el
// && !el.hasAncestor(self.focused)) continue;
pos = el.lpos;
if (!pos) continue;
if (data.x >= pos.xi && data.x < pos.xl
&& data.y >= pos.yi && data.y < pos.yl) {
el.emit('mouse', data);
if (data.action === 'mousedown') {
self.mouseDown = el;
} else if (data.action === 'mouseup') {
(self.mouseDown || el).emit('click', data);
self.mouseDown = null;
} else if (data.action === 'mousemove') {
if (self.hover && el.index > self.hover.index) {
set = false;
}
if (self.hover !== el && !set) {
if (self.hover) {
self.hover.emit('mouseout', data);
}
el.emit('mouseover', data);
self.hover = el;
}
set = true;
}
el.emit(data.action, data);
break;
}
}
// Just mouseover?
if ((data.action === 'mousemove'
|| data.action === 'mousedown'
|| data.action === 'mouseup')
&& self.hover
&& !set) {
self.hover.emit('mouseout', data);
self.hover = null;
}
self.emit('mouse', data);
self.emit(data.action, data);
});
// Autofocus highest element.
// this.on('element click', function(el, data) {
// var target;
// do {
// if (el.clickable === true && el.options.autoFocus !== false) {
// target = el;
// }
// } while (el = el.parent);
// if (target) target.focus();
// });
// Autofocus elements with the appropriate option.
this.on('element click', function(el) {
if (el.clickable === true && el.options.autoFocus !== false) {
el.focus();
}
});
}n/a
_reduceColor = function (color) {
return colors.reduce(color, this.tput.colors);
}n/a
alloc = function (dirty) {
var x, y;
this.lines = [];
for (y = 0; y < this.rows; y++) {
this.lines[y] = [];
for (x = 0; x < this.cols; x++) {
this.lines[y][x] = [this.dattr, ' '];
}
this.lines[y].dirty = !!dirty;
}
this.olines = [];
for (y = 0; y < this.rows; y++) {
this.olines[y] = [];
for (x = 0; x < this.cols; x++) {
this.olines[y][x] = [this.dattr, ' '];
}
}
this.program.clear();
}n/a
attrCode = function (code, cur, def) {
var flags = (cur >> 18) & 0x1ff
, fg = (cur >> 9) & 0x1ff
, bg = cur & 0x1ff
, c
, i;
code = code.slice(2, -1).split(';');
if (!code[0]) code[0] = '0';
for (i = 0; i < code.length; i++) {
c = +code[i] || 0;
switch (c) {
case 0: // normal
bg = def & 0x1ff;
fg = (def >> 9) & 0x1ff;
flags = (def >> 18) & 0x1ff;
break;
case 1: // bold
flags |= 1;
break;
case 22:
flags = (def >> 18) & 0x1ff;
break;
case 4: // underline
flags |= 2;
break;
case 24:
flags = (def >> 18) & 0x1ff;
break;
case 5: // blink
flags |= 4;
break;
case 25:
flags = (def >> 18) & 0x1ff;
break;
case 7: // inverse
flags |= 8;
break;
case 27:
flags = (def >> 18) & 0x1ff;
break;
case 8: // invisible
flags |= 16;
break;
case 28:
flags = (def >> 18) & 0x1ff;
break;
case 39: // default fg
fg = (def >> 9) & 0x1ff;
break;
case 49: // default bg
bg = def & 0x1ff;
break;
case 100: // default fg/bg
fg = (def >> 9) & 0x1ff;
bg = def & 0x1ff;
break;
default: // color
if (c === 48 && +code[i+1] === 5) {
i += 2;
bg = +code[i];
break;
} else if (c === 48 && +code[i+1] === 2) {
i += 2;
bg = colors.match(+code[i], +code[i+1], +code[i+2]);
if (bg === -1) bg = def & 0x1ff;
i += 2;
break;
} else if (c === 38 && +code[i+1] === 5) {
i += 2;
fg = +code[i];
break;
} else if (c === 38 && +code[i+1] === 2) {
i += 2;
fg = colors.match(+code[i], +code[i+1], +code[i+2]);
if (fg === -1) fg = (def >> 9) & 0x1ff;
i += 2;
break;
}
if (c >= 40 && c <= 47) {
bg = c - 40;
} else if (c >= 100 && c <= 107) {
bg = c - 100;
bg += 8;
} else if (c === 49) {
bg = def & 0x1ff;
} else if (c >= 30 && c <= 37) {
fg = c - 30;
} else if (c >= 90 && c <= 97) {
fg = c - 90;
fg += 8;
} else if (c === 39) {
fg = (def >> 9) & 0x1ff;
} else if (c === 100) {
fg = (def >> 9) & 0x1ff;
bg = def & 0x1ff;
}
break;
}
}
return (flags << 18) | (fg << 9) | bg;
}n/a
blankLine = function (ch, dirty) {
var out = [];
for (var x = 0; x < this.cols; x++) {
out[x] = [this.dattr, ch || ' '];
}
out.dirty = dirty;
return out;
}n/a
cleanSides = function (el) {
var pos = el.lpos;
if (!pos) {
return false;
}
if (pos._cleanSides != null) {
return pos._cleanSides;
}
if (pos.xi <= 0 && pos.xl >= this.width) {
return pos._cleanSides = true;
}
if (this.options.fastCSR) {
// Maybe just do this instead of parsing.
if (pos.yi < 0) return pos._cleanSides = false;
if (pos.yl > this.height) return pos._cleanSides = false;
if (this.width - (pos.xl - pos.xi) < 40) {
return pos._cleanSides = true;
}
return pos._cleanSides = false;
}
if (!this.options.smartCSR) {
return false;
}
// The scrollbar can't update properly, and there's also a
// chance that the scrollbar may get moved around senselessly.
// NOTE: In pratice, this doesn't seem to be the case.
// if (this.scrollbar) {
// return pos._cleanSides = false;
// }
// Doesn't matter if we're only a height of 1.
// if ((pos.yl - el.ibottom) - (pos.yi + el.itop) <= 1) {
// return pos._cleanSides = false;
// }
var yi = pos.yi + el.itop
, yl = pos.yl - el.ibottom
, first
, ch
, x
, y;
if (pos.yi < 0) return pos._cleanSides = false;
if (pos.yl > this.height) return pos._cleanSides = false;
if (pos.xi - 1 < 0) return pos._cleanSides = true;
if (pos.xl > this.width) return pos._cleanSides = true;
for (x = pos.xi - 1; x >= 0; x--) {
if (!this.olines[yi]) break;
first = this.olines[yi][x];
for (y = yi; y < yl; y++) {
if (!this.olines[y] || !this.olines[y][x]) break;
ch = this.olines[y][x];
if (ch[0] !== first[0] || ch[1] !== first[1]) {
return pos._cleanSides = false;
}
}
}
for (x = pos.xl; x < this.width; x++) {
if (!this.olines[yi]) break;
first = this.olines[yi][x];
for (y = yi; y < yl; y++) {
if (!this.olines[y] || !this.olines[y][x]) break;
ch = this.olines[y][x];
if (ch[0] !== first[0] || ch[1] !== first[1]) {
return pos._cleanSides = false;
}
}
}
return pos._cleanSides = true;
}n/a
clearRegion = function (xi, xl, yi, yl, override) {
return this.fillRegion(this.dattr, ' ', xi, xl, yi, yl, override);
}n/a
codeAttr = function (code) {
var flags = (code >> 18) & 0x1ff
, fg = (code >> 9) & 0x1ff
, bg = code & 0x1ff
, out = '';
// bold
if (flags & 1) {
out += '1;';
}
// underline
if (flags & 2) {
out += '4;';
}
// blink
if (flags & 4) {
out += '5;';
}
// inverse
if (flags & 8) {
out += '7;';
}
// invisible
if (flags & 16) {
out += '8;';
}
if (bg !== 0x1ff) {
bg = this._reduceColor(bg);
if (bg < 16) {
if (bg < 8) {
bg += 40;
} else if (bg < 16) {
bg -= 8;
bg += 100;
}
out += bg + ';';
} else {
out += '48;5;' + bg + ';';
}
}
if (fg !== 0x1ff) {
fg = this._reduceColor(fg);
if (fg < 16) {
if (fg < 8) {
fg += 30;
} else if (fg < 16) {
fg -= 8;
fg += 90;
}
out += fg + ';';
} else {
out += '38;5;' + fg + ';';
}
}
if (out[out.length - 1] === ';') out = out.slice(0, -1);
return '\x1b[' + out + 'm';
}n/a
copyToClipboard = function (text) {
return this.program.copyToClipboard(text);
}n/a
cursorColor = function (color) {
this.cursor.color = color != null
? colors.convert(color)
: null;
this.cursor._set = true;
if (this.cursor.artificial) {
return true;
}
return this.program.cursorColor(colors.ncolors[this.cursor.color]);
}n/a
cursorReset = function () {
this.cursor.shape = 'block';
this.cursor.blink = false;
this.cursor.color = null;
this.cursor._set = false;
if (this.cursor.artificial) {
this.cursor.artificial = false;
if (this.program.hideCursor_old) {
this.program.hideCursor = this.program.hideCursor_old;
delete this.program.hideCursor_old;
}
if (this.program.showCursor_old) {
this.program.showCursor = this.program.showCursor_old;
delete this.program.showCursor_old;
}
if (this._cursorBlink) {
clearInterval(this._cursorBlink);
delete this._cursorBlink;
}
return true;
}
return this.program.cursorReset();
}n/a
cursorShape = function (shape, blink) {
var self = this;
this.cursor.shape = shape || 'block';
this.cursor.blink = blink || false;
this.cursor._set = true;
if (this.cursor.artificial) {
if (!this.program.hideCursor_old) {
var hideCursor = this.program.hideCursor;
this.program.hideCursor_old = this.program.hideCursor;
this.program.hideCursor = function() {
hideCursor.call(self.program);
self.cursor._hidden = true;
if (self.renders) self.render();
};
}
if (!this.program.showCursor_old) {
var showCursor = this.program.showCursor;
this.program.showCursor_old = this.program.showCursor;
this.program.showCursor = function() {
self.cursor._hidden = false;
if (self.program._exiting) showCursor.call(self.program);
if (self.renders) self.render();
};
}
if (!this._cursorBlink) {
this._cursorBlink = setInterval(function() {
if (!self.cursor.blink) return;
self.cursor._state ^= 1;
if (self.renders) self.render();
}, 500);
if (this._cursorBlink.unref) {
this._cursorBlink.unref();
}
}
return true;
}
return this.program.cursorShape(this.cursor.shape, this.cursor.blink);
}n/a
debug = function () {
if (this.debugLog) {
this.debugLog.log.apply(this.debugLog, arguments);
}
return this.program.debug.apply(this.program, arguments);
}n/a
deleteBottom = function (top, bottom) {
return this.clearRegion(0, this.width, bottom, bottom);
}n/a
deleteLine = function (n, y, top, bottom) {
// if (y === top) return this.deleteLineNC(n, y, top, bottom);
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line
|| !this.tput.strings.insert_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(y, 0);
this._buf += this.tput.dl(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
}n/a
deleteLineNC = function (n, y, top, bottom) {
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(bottom, 0);
this._buf += Array(n + 1).join('\n');
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
}n/a
deleteTop = function (top, bottom) {
// Same as: return this.insertBottom(top, bottom);
return this.deleteLine(1, top, top, bottom);
}n/a
destroy = function () {
this.leave();
var index = Screen.instances.indexOf(this);
if (~index) {
Screen.instances.splice(index, 1);
Screen.total--;
Screen.global = Screen.instances[0];
if (Screen.total === 0) {
Screen.global = null;
process.removeListener('uncaughtException', Screen._exceptionHandler);
process.removeListener('SIGTERM', Screen._sigtermHandler);
process.removeListener('SIGINT', Screen._sigintHandler);
process.removeListener('SIGQUIT', Screen._sigquitHandler);
process.removeListener('exit', Screen._exitHandler);
delete Screen._exceptionHandler;
delete Screen._sigtermHandler;
delete Screen._sigintHandler;
delete Screen._sigquitHandler;
delete Screen._exitHandler;
delete Screen._bound;
}
this.destroyed = true;
this.emit('destroy');
this._destroy();
}
this.program.destroy();
}...
output: client,
terminal: 'xterm-256color',
fullUnicode: true
});
client.on('close', function() {
if (!screen.destroyed) {
screen.destroy();
}
});
screen.key(['C-c', 'q'], function(ch, key) {
screen.destroy();
});
...displayImage = function (file, callback) {
if (!file) {
if (!callback) return;
return callback(new Error('No image.'));
}
file = path.resolve(process.cwd(), file);
if (!~file.indexOf('://')) {
file = 'file://' + file;
}
var args = ['w3m', '-T', 'text/html'];
var input = '<title>press q to exit</title>'
+ '<img align="center" src="' + file + '">';
var opt = {
stdio: ['pipe', 1, 2],
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(args[0], args.slice(1), opt);
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function(code) {
if (!callback) return;
if (code !== 0) return callback(new Error('Exit Code: ' + code));
return callback(null, code === 0);
});
ps.stdin.write(input + '\n');
ps.stdin.end();
}n/a
draw = function (start, end) {
// this.emit('predraw');
var x
, y
, line
, out
, ch
, data
, attr
, fg
, bg
, flags;
var main = ''
, pre
, post;
var clr
, neq
, xx;
var lx = -1
, ly = -1
, o;
var acs;
if (this._buf) {
main += this._buf;
this._buf = '';
}
for (y = start; y <= end; y++) {
line = this.lines[y];
o = this.olines[y];
if (!line.dirty && !(this.cursor.artificial && y === this.program.y)) {
continue;
}
line.dirty = false;
out = '';
attr = this.dattr;
for (x = 0; x < line.length; x++) {
data = line[x][0];
ch = line[x][1];
// Render the artificial cursor.
if (this.cursor.artificial
&& !this.cursor._hidden
&& this.cursor._state
&& x === this.program.x
&& y === this.program.y) {
var cattr = this._cursorAttr(this.cursor, data);
if (cattr.ch) ch = cattr.ch;
data = cattr.attr;
}
// Take advantage of xterm's back_color_erase feature by using a
// lookahead. Stop spitting out so many damn spaces. NOTE: Is checking
// the bg for non BCE terminals worth the overhead?
if (this.options.useBCE
&& ch === ' '
&& (this.tput.bools.back_color_erase
|| (data & 0x1ff) === (this.dattr & 0x1ff))
&& ((data >> 18) & 8) === ((this.dattr >> 18) & 8)) {
clr = true;
neq = false;
for (xx = x; xx < line.length; xx++) {
if (line[xx][0] !== data || line[xx][1] !== ' ') {
clr = false;
break;
}
if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) {
neq = true;
}
}
if (clr && neq) {
lx = -1, ly = -1;
if (data !== attr) {
out += this.codeAttr(data);
attr = data;
}
out += this.tput.cup(y, x);
out += this.tput.el();
for (xx = x; xx < line.length; xx++) {
o[xx][0] = data;
o[xx][1] = ' ';
}
break;
}
// If there's more than 10 spaces, use EL regardless
// and start over drawing the rest of line. Might
// not be worth it. Try to use ECH if the terminal
// supports it. Maybe only try to use ECH here.
// //if (this.tput.strings.erase_chars)
// if (!clr && neq && (xx - x) > 10) {
// lx = -1, ly = -1;
// if (data !== attr) {
// out += this.codeAttr(data);
// attr = data;
// }
// out += this.tput.cup(y, x);
// if (this.tput.strings.erase_chars) {
// // Use erase_chars to avoid erasing the whole line.
// out += this.tput.ech(xx - x);
// } else {
// out += this.tput.el();
// }
// if (this.tput.strings.parm_right_cursor) {
// out += this.tput.cuf(xx - x);
// } else {
// out += this.tput.cup(y, xx);
// }
// this.fillRegion(data, ' ',
// x, this.tput.strings.erase_chars ? xx : line.length,
// y, y + 1);
// x = xx - 1;
// continue;
// }
// Skip to the next line if the
// rest of the line is already drawn.
// if (!neq) {
// for (; xx < line.length; xx++) {
// if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) {
// neq = true;
// break;
// }
// }
// if (!neq) {
// attr = data;
// break;
// }
// }
}
// Optimize by comparing the real output
// buffer to the pending output buffer.
if (data === o[x][0] && ch === o[x][1]) {
if (lx === -1) {
lx = x;
ly = y;
}
continue;
} else if (lx !== -1) {
if (this.tput.strings.parm_right_cursor) {
out += y === ly
? this.tput.cuf(x - lx)
: this. ...n/a
enableInput = function (el) {
this._listenMouse(el);
this._listenKeys(el);
}n/a
enableKeys = function (el) {
this._listenKeys(el);
}n/a
enableMouse = function (el) {
this._listenMouse(el);
}...
- __resize__ - Received on screen resize.
- __mouse__ - Received on mouse events.
- __keypress__ - Received on key events.
- __element [name]__ - Global events received for all elements.
- __key [name]__ - Received on key event for [name].
- __focus, blur__ - Received when the terminal window focuses/blurs. Requires a
terminal supporting the focus protocol and focus needs to be passed to
program.enableMouse().
- __prerender__ - Received before render.
- __render__ - Received on render.
- __warning__ - Received when blessed notices something untoward (output is not
a tty, terminfo not found, etc).
- __destroy__ - Received when the screen is destroyed (only useful when using
multiple screens).
...enter = function () {
if (this.program.isAlt) return;
if (!this.cursor._set) {
if (this.options.cursor.shape) {
this.cursorShape(this.cursor.shape, this.cursor.blink);
}
if (this.options.cursor.color) {
this.cursorColor(this.cursor.color);
}
}
if (process.platform === 'win32') {
try {
cp.execSync('cls', { stdio: 'ignore', timeout: 1000 });
} catch (e) {
;
}
}
this.program.alternateBuffer();
this.program.put.keypad_xmit();
this.program.csr(0, this.height - 1);
this.program.hideCursor();
this.program.cup(0, 0);
// We need this for tmux now:
if (this.tput.strings.ena_acs) {
this.program._write(this.tput.enacs());
}
this.alloc();
}n/a
exec = function (file, args, options, callback) {
var ps = this.spawn(file, args, options);
ps.on('error', function(err) {
if (!callback) return;
return callback(err, false);
});
ps.on('exit', function(code) {
if (!callback) return;
return callback(null, code === 0);
});
return ps;
}...
// check tty for /dev/tty[n]
var path;
try {
path = fs.readlinkSync('/proc/' + pid + '/fd/0');
} catch (e) {
;
}
var tty = /tty[0-9]+$/.exec(path);
if (tty === null) {
// TODO: should also check for /dev/input/..
}
var vc;
if (tty) {
tty = tty[0];
...fillRegion = function (attr, ch, xi, xl, yi, yl, override) {
var lines = this.lines
, cell
, xx;
if (xi < 0) xi = 0;
if (yi < 0) yi = 0;
for (; yi < yl; yi++) {
if (!lines[yi]) break;
for (xx = xi; xx < xl; xx++) {
cell = lines[yi][xx];
if (!cell) break;
if (override || attr !== cell[0] || ch !== cell[1]) {
lines[yi][xx][0] = attr;
lines[yi][xx][1] = ch;
lines[yi].dirty = true;
}
}
}
}n/a
focusNext = function () {
return this.focusOffset(1);
}n/a
focusOffset = function (offset) {
var shown = this.keyable.filter(function(el) {
return !el.detached && el.visible;
}).length;
if (!shown || !offset) {
return;
}
var i = this.keyable.indexOf(this.focused);
if (!~i) return;
if (offset > 0) {
while (offset--) {
if (++i > this.keyable.length - 1) i = 0;
if (this.keyable[i].detached || !this.keyable[i].visible) offset++;
}
} else {
offset = -offset;
while (offset--) {
if (--i < 0) i = this.keyable.length - 1;
if (this.keyable[i].detached || !this.keyable[i].visible) offset++;
}
}
return this.keyable[i].focus();
}n/a
focusPop = function () {
var old = this.history.pop();
if (this.history.length) {
this._focus(this.history[this.history.length - 1], old);
}
return old;
}n/a
focusPrev = function () {
return this.focusOffset(-1);
}n/a
focusPrevious = function () {
return this.focusOffset(-1);
}n/a
focusPush = function (el) {
if (!el) return;
var old = this.history[this.history.length - 1];
if (this.history.length === 10) {
this.history.shift();
}
this.history.push(el);
this._focus(el, old);
}n/a
insertBottom = function (top, bottom) {
return this.deleteLine(1, top, top, bottom);
}n/a
insertLine = function (n, y, top, bottom) {
// if (y === top) return this.insertLineNC(n, y, top, bottom);
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line
|| !this.tput.strings.insert_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(y, 0);
this._buf += this.tput.il(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(y, 0, this.blankLine());
this.lines.splice(j, 1);
this.olines.splice(y, 0, this.blankLine());
this.olines.splice(j, 1);
}
}...
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
...insertLineNC = function (n, y, top, bottom) {
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(top, 0);
this._buf += this.tput.dl(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
}n/a
insertTop = function (top, bottom) {
return this.insertLine(1, top, top, bottom);
}n/a
key = function () {
return this.program.key.apply(this, arguments);
}...
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
screen.render();
});
// Quit on Escape, q, or Control-C.
...leave = function () {
if (!this.program.isAlt) return;
this.program.put.keypad_local();
if (this.program.scrollTop !== 0
|| this.program.scrollBottom !== this.rows - 1) {
this.program.csr(0, this.height - 1);
}
// XXX For some reason if alloc/clear() is before this
// line, it doesn't work on linux console.
this.program.showCursor();
this.alloc();
if (this._listenedMouse) {
this.program.disableMouse();
}
this.program.normalBuffer();
if (this.cursor._set) this.cursorReset();
this.program.flush();
if (process.platform === 'win32') {
try {
cp.execSync('cls', { stdio: 'ignore', timeout: 1000 });
} catch (e) {
;
}
}
}n/a
log = function () {
return this.program.log.apply(this.program, arguments);
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...onceKey = function () {
return this.program.onceKey.apply(this, arguments);
}n/a
postEnter = function () {
var self = this;
if (this.options.debug) {
this.debugLog = new Log({
screen: this,
parent: this,
hidden: true,
draggable: true,
left: 'center',
top: 'center',
width: '30%',
height: '30%',
border: 'line',
label: ' {bold}Debug Log{/bold} ',
tags: true,
keys: true,
vi: true,
mouse: true,
scrollbar: {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
inverse: true
}
}
});
this.debugLog.toggle = function() {
if (self.debugLog.hidden) {
self.saveFocus();
self.debugLog.show();
self.debugLog.setFront();
self.debugLog.focus();
} else {
self.debugLog.hide();
self.restoreFocus();
}
self.render();
};
this.debugLog.key(['q', 'escape'], self.debugLog.toggle);
this.key('f12', self.debugLog.toggle);
}
if (this.options.warnings) {
this.on('warning', function(text) {
var warning = new Box({
screen: self,
parent: self,
left: 'center',
top: 'center',
width: 'shrink',
padding: 1,
height: 'shrink',
align: 'center',
valign: 'middle',
border: 'line',
label: ' {red-fg}{bold}WARNING{/} ',
content: '{bold}' + text + '{/bold}',
tags: true
});
self.render();
var timeout = setTimeout(function() {
warning.destroy();
self.render();
}, 1500);
if (timeout.unref) {
timeout.unref();
}
});
}
}n/a
readEditor = function (options, callback) {
if (typeof options === 'string') {
options = { editor: options };
}
if (!callback) {
callback = options;
options = null;
}
if (!callback) {
callback = function() {};
}
options = options || {};
var self = this
, editor = options.editor || process.env.EDITOR || 'vi'
, name = options.name || process.title || 'blessed'
, rnd = Math.random().toString(36).split('.').pop()
, file = '/tmp/' + name + '.' + rnd
, args = [file]
, opt;
opt = {
stdio: 'inherit',
env: process.env,
cwd: process.env.HOME
};
function writeFile(callback) {
if (!options.value) return callback();
return fs.writeFile(file, options.value, callback);
}
return writeFile(function(err) {
if (err) return callback(err);
return self.exec(editor, args, opt, function(err, success) {
if (err) return callback(err);
return fs.readFile(file, 'utf8', function(err, data) {
return fs.unlink(file, function() {
if (!success) return callback(new Error('Unsuccessful.'));
if (err) return callback(err);
return callback(null, data);
});
});
});
});
}n/a
realloc = function () {
return this.alloc(true);
}n/a
removeKey = function () {
return this.program.unkey.apply(this, arguments);
}n/a
render = function () {
var self = this;
if (this.destroyed) return;
this.emit('prerender');
this._borderStops = {};
// TODO: Possibly get rid of .dirty altogether.
// TODO: Could possibly drop .dirty and just clear the `lines` buffer every
// time before a screen.render. This way clearRegion doesn't have to be
// called in arbitrary places for the sake of clearing a spot where an
// element used to be (e.g. when an element moves or is hidden). There could
// be some overhead though.
// this.screen.clearRegion(0, this.cols, 0, this.rows);
this._ci = 0;
this.children.forEach(function(el) {
el.index = self._ci++;
//el._rendering = true;
el.render();
//el._rendering = false;
});
this._ci = -1;
if (this.screen.dockBorders) {
this._dockBorders();
}
this.draw(0, this.lines.length - 1);
// XXX Workaround to deal with cursor pos before the screen has rendered and
// lpos is not reliable (stale).
if (this.focused && this.focused._updateCursor) {
this.focused._updateCursor(true);
}
this.renders++;
this.emit('render');
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...resetCursor = function () {
this.cursor.shape = 'block';
this.cursor.blink = false;
this.cursor.color = null;
this.cursor._set = false;
if (this.cursor.artificial) {
this.cursor.artificial = false;
if (this.program.hideCursor_old) {
this.program.hideCursor = this.program.hideCursor_old;
delete this.program.hideCursor_old;
}
if (this.program.showCursor_old) {
this.program.showCursor = this.program.showCursor_old;
delete this.program.showCursor_old;
}
if (this._cursorBlink) {
clearInterval(this._cursorBlink);
delete this._cursorBlink;
}
return true;
}
return this.program.cursorReset();
}n/a
restoreFocus = function () {
if (!this._savedFocus) return;
this._savedFocus.focus();
delete this._savedFocus;
return this.focused;
}n/a
rewindFocus = function () {
var old = this.history.pop()
, el;
while (this.history.length) {
el = this.history.pop();
if (!el.detached && el.visible) {
this.history.push(el);
this._focus(el, old);
return el;
}
}
if (old) {
old.emit('blur');
}
}n/a
saveFocus = function () {
return this._savedFocus = this.focused;
}n/a
screenshot = function (xi, xl, yi, yl, term) {
if (xi == null) xi = 0;
if (xl == null) xl = this.cols;
if (yi == null) yi = 0;
if (yl == null) yl = this.rows;
if (xi < 0) xi = 0;
if (yi < 0) yi = 0;
var x
, y
, line
, out
, ch
, data
, attr;
var sdattr = this.dattr;
if (term) {
this.dattr = term.defAttr;
}
var main = '';
for (y = yi; y < yl; y++) {
line = term
? term.lines[y]
: this.lines[y];
if (!line) break;
out = '';
attr = this.dattr;
for (x = xi; x < xl; x++) {
if (!line[x]) break;
data = line[x][0];
ch = line[x][1];
if (data !== attr) {
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (data !== this.dattr) {
var _data = data;
if (term) {
if (((_data >> 9) & 0x1ff) === 257) _data |= 0x1ff << 9;
if ((_data & 0x1ff) === 256) _data |= 0x1ff;
}
out += this.codeAttr(_data);
}
}
if (this.fullUnicode) {
if (unicode.charWidth(line[x][1]) === 2) {
if (x === xl - 1) {
ch = ' ';
} else {
x++;
}
}
}
out += ch;
attr = data;
}
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (out) {
main += (y > 0 ? '\n' : '') + out;
}
}
main = main.replace(/(?:\s*\x1b\[40m\s*\x1b\[m\s*)*$/, '') + '\n';
if (term) {
this.dattr = sdattr;
}
return main;
}n/a
setEffects = function (el, fel, over, out, effects, temp) {
if (!effects) return;
var tmp = {};
if (temp) el[temp] = tmp;
if (typeof el !== 'function') {
var _el = el;
el = function() { return _el; };
}
fel.on(over, function() {
var element = el();
Object.keys(effects).forEach(function(key) {
var val = effects[key];
if (val !== null && typeof val === 'object') {
tmp[key] = tmp[key] || {};
// element.style[key] = element.style[key] || {};
Object.keys(val).forEach(function(k) {
var v = val[k];
tmp[key][k] = element.style[key][k];
element.style[key][k] = v;
});
return;
}
tmp[key] = element.style[key];
element.style[key] = val;
});
element.screen.render();
});
fel.on(out, function() {
var element = el();
Object.keys(effects).forEach(function(key) {
var val = effects[key];
if (val !== null && typeof val === 'object') {
tmp[key] = tmp[key] || {};
// element.style[key] = element.style[key] || {};
Object.keys(val).forEach(function(k) {
if (tmp[key].hasOwnProperty(k)) {
element.style[key][k] = tmp[key][k];
}
});
return;
}
if (tmp.hasOwnProperty(key)) {
element.style[key] = tmp[key];
}
});
element.screen.render();
});
}n/a
setTerminal = function (terminal) {
var entered = !!this.program.isAlt;
if (entered) {
this._buf = '';
this.program._buf = '';
this.leave();
}
this.program.setTerminal(terminal);
this.tput = this.program.tput;
if (entered) {
this.enter();
}
}...
- __aleft__ - Absolute left offset, always zero.
- __aright__ - Absolute right offset, always zero.
- __atop__ - Absolute top offset, always zero.
- __abottom__ - Absolute bottom offset, always zero.
- __grabKeys__ - Whether the focused element grabs all keypresses.
- __lockKeys__ - Prevent keypresses from being received by any element.
- __hover__ - The currently hovered element. Only set if mouse events are bound.
- __terminal__ - Set or get terminal name. `Set` calls `screen.setTerminal()`
internally.
- __title__ - Set or get window title.
##### Events:
- Inherits all from Node.
- __resize__ - Received on screen resize.
...sigtstp = function (callback) {
var self = this;
this.program.sigtstp(function() {
self.alloc();
self.render();
self.program.lrestoreCursor('pause', true);
if (callback) callback();
});
}n/a
spawn = function (file, args, options) {
if (!Array.isArray(args)) {
options = args;
args = [];
}
var screen = this
, program = screen.program
, spawn = require('child_process').spawn
, mouse = program.mouseEnabled
, ps;
options = options || {};
options.stdio = options.stdio || 'inherit';
program.lsaveCursor('spawn');
// program.csr(0, program.rows - 1);
program.normalBuffer();
program.showCursor();
if (mouse) program.disableMouse();
var write = program.output.write;
program.output.write = function() {};
program.input.pause();
if (program.input.setRawMode) {
program.input.setRawMode(false);
}
var resume = function() {
if (resume.done) return;
resume.done = true;
if (program.input.setRawMode) {
program.input.setRawMode(true);
}
program.input.resume();
program.output.write = write;
program.alternateBuffer();
// program.csr(0, program.rows - 1);
if (mouse) {
program.enableMouse();
if (screen.options.sendFocus) {
screen.program.setMouse({ sendFocus: true }, true);
}
}
screen.alloc();
screen.render();
screen.program.lrestoreCursor('spawn', true);
};
ps = spawn(file, args, options);
ps.on('error', resume);
ps.on('exit', resume);
return ps;
}n/a
unkey = function () {
return this.program.unkey.apply(this, arguments);
}n/a
function Tput(options) {
if (!(this instanceof Tput)) {
return new Tput(options);
}
options = options || {};
if (typeof options === 'string') {
options = { terminal: options };
}
this.options = options;
this.terminal = options.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this.terminal = this.terminal.toLowerCase();
this.debug = options.debug;
this.padding = options.padding;
this.extended = options.extended;
this.printf = options.printf;
this.termcap = options.termcap;
this.error = null;
this.terminfoPrefix = options.terminfoPrefix;
this.terminfoFile = options.terminfoFile;
this.termcapFile = options.termcapFile;
if (options.terminal || options.term) {
this.setup();
}
}n/a
_prefix = function (term) {
// If we have a terminfoFile, or our
// term looks like a filename, use it.
if (term) {
if (~term.indexOf(path.sep)) {
return term;
}
if (this.terminfoFile) {
return this.terminfoFile;
}
}
var paths = Tput.ipaths.slice()
, file;
if (this.terminfoPrefix) {
paths.unshift(this.terminfoPrefix);
}
// Try exact matches.
file = this._tprefix(paths, term);
if (file) return file;
// Try similar matches.
file = this._tprefix(paths, term, true);
if (file) return file;
// Not found.
throw new Error('Terminfo directory not found.');
}n/a
_tprefix = function (prefix, term, soft) {
if (!prefix) return;
var file
, dir
, i
, sdiff
, sfile
, list;
if (Array.isArray(prefix)) {
for (i = 0; i < prefix.length; i++) {
file = this._tprefix(prefix[i], term, soft);
if (file) return file;
}
return;
}
var find = function(word) {
var file, ch;
file = path.resolve(prefix, word[0]);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
ch = word[0].charCodeAt(0).toString(16);
if (ch.length < 2) ch = '0' + ch;
file = path.resolve(prefix, ch);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
};
if (!term) {
// Make sure the directory's sub-directories
// are all one-letter, or hex digits.
// return find('x') ? prefix : null;
try {
dir = fs.readdirSync(prefix).filter(function(file) {
return file.length !== 1 && !/^[0-9a-fA-F]{2}$/.test(file);
});
if (!dir.length) {
return prefix;
}
} catch (e) {
;
}
return;
}
term = path.basename(term);
dir = find(term);
if (!dir) return;
if (soft) {
try {
list = fs.readdirSync(dir);
} catch (e) {
return;
}
list.forEach(function(file) {
if (file.indexOf(term) === 0) {
var diff = file.length - term.length;
if (!sfile || diff < sdiff) {
sdiff = diff;
sfile = file;
}
}
});
return sfile && (soft || sdiff === 0)
? path.resolve(dir, sfile)
: null;
}
file = path.resolve(dir, term);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
}n/a
print = function () {
var fake = {
padding: true,
bools: { needs_xon_xoff: true, xon_xoff: false }
};
return Tput.prototype._print.apply(fake, arguments);
}n/a
function sprintf(src) {
var params = Array.prototype.slice.call(arguments, 1)
, rule = /%([\-+# ]{1,4})?(\d+(?:\.\d+)?)?([doxXsc])/g
, i = 0;
return src.replace(rule, function(_, flag, width, type) {
var flags = (flag || '').split('')
, param = params[i] != null ? params[i] : ''
, initial = param
// , width = +width
, opt = {}
, pre = '';
i++;
switch (type) {
case 'd': // signed int
param = (+param).toString(10);
break;
case 'o': // unsigned octal
param = (+param).toString(8);
break;
case 'x': // unsigned hex int
param = (+param).toString(16);
break;
case 'X': // unsigned hex int uppercase
param = (+param).toString(16).toUppercase();
break;
case 's': // string
break;
case 'c': // char
param = isFinite(param)
? String.fromCharCode(param || 0200)
: '';
break;
}
flags.forEach(function(flag) {
switch (flag) {
// left-justify by width
case '-':
opt.left = true;
break;
// always precede numbers with their signs
case '+':
opt.signs = true;
break;
// used with o, x, X - value is preceded with 0, 0x, or 0X respectively.
// used with a, A, e, E, f, F, g, G - forces written output to contain
// a decimal point even if no more digits follow
case '#':
opt.hexpoint = true;
break;
// if no sign is going to be written, black space in front of the value
case ' ':
opt.space = true;
break;
}
});
width = +width.split('.')[0];
// Should this be for opt.left too?
// Example: %2.2X - turns 0 into 00
if (width && !opt.left) {
param = param + '';
while (param.length < width) {
param = '0' + param;
}
}
if (opt.signs) {
if (+initial >= 0) {
pre += '+';
}
}
if (opt.space) {
if (!opt.signs && +initial >= 0) {
pre += ' ';
}
}
if (opt.hexpoint) {
switch (type) {
case 'o': // unsigned octal
pre += '0';
break;
case 'x': // unsigned hex int
pre += '0x';
break;
case 'X': // unsigned hex int uppercase
pre += '0X';
break;
}
}
if (opt.left) {
if (width > (pre.length + param.length)) {
width -= pre.length + param.length;
pre = Array(width + 1).join(' ') + pre;
}
}
return pre + param;
});
}n/a
function tryRead(file) {
if (Array.isArray(file)) {
for (var i = 0; i < file.length; i++) {
var data = tryRead(file[i]);
if (data) return data;
}
return '';
}
if (!file) return '';
file = path.resolve.apply(path, arguments);
try {
return fs.readFileSync(file, 'utf8');
} catch (e) {
return '';
}
}n/a
GetConsoleCP = function () {
var ccp;
if (process.platform !== 'win32') {
return -1;
}
// Allow unicode on all windows consoles for now:
if (+process.env.NCURSES_UNICODE !== 0) {
return 65001;
}
// cp.execSync('chcp 65001', { stdio: 'ignore', timeout: 1500 });
try {
// Produces something like: 'Active code page: 437\n\n'
ccp = cp.execFileSync(process.env.WINDIR + '\\system32\\chcp.com', [], {
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'ascii',
timeout: 1500
});
// ccp = cp.execSync('chcp', {
// stdio: ['ignore', 'pipe', 'ignore'],
// encoding: 'ascii',
// timeout: 1500
// });
} catch (e) {
;
}
ccp = /\d+/.exec(ccp);
if (!ccp) {
return -1;
}
ccp = +ccp[0];
return ccp;
}n/a
_captoinfo = function (cap, s, parameterized) {
var self = this;
var capstart;
if (parameterized == null) {
parameterized = 0;
}
var MAX_PUSHED = 16
, stack = [];
var stackptr = 0
, onstack = 0
, seenm = 0
, seenn = 0
, seenr = 0
, param = 1
, i = 0
, out = '';
function warn() {
var args = Array.prototype.slice.call(arguments);
args[0] = 'captoinfo: ' + (args[0] || '');
return self._debug.apply(self, args);
}
function isdigit(ch) {
return ch >= '0' && ch <= '9';
}
function isgraph(ch) {
return ch > ' ' && ch <= '~';
}
// convert a character to a terminfo push
function cvtchar(sp) {
var c = '\0'
, len;
var j = i;
switch (sp[j]) {
case '\\':
switch (sp[++j]) {
case '\'':
case '$':
case '\\':
case '%':
c = sp[j];
len = 2;
break;
case '\0':
c = '\\';
len = 1;
break;
case '0':
case '1':
case '2':
case '3':
len = 1;
while (isdigit(sp[j])) {
c = String.fromCharCode(8 * c.charCodeAt(0)
+ (sp[j++].charCodeAt(0) - '0'.charCodeAt(0)));
len++;
}
break;
default:
c = sp[j];
len = 2;
break;
}
break;
case '^':
c = String.fromCharCode(sp[++j].charCodeAt(0) & 0x1f);
len = 2;
break;
default:
c = sp[j];
len = 1;
}
if (isgraph(c) && c !== ',' && c !== '\'' && c !== '\\' && c !== ':') {
out += '%\'';
out += c;
out += '\'';
} else {
out += '%{';
if (c.charCodeAt(0) > 99) {
out += String.fromCharCode(
(c.charCodeAt(0) / 100 | 0) + '0'.charCodeAt(0));
}
if (c.charCodeAt(0) > 9) {
out += String.fromCharCode(
(c.charCodeAt(0) / 10 | 0) % 10 + '0'.charCodeAt(0));
}
out += String.fromCharCode(
c.charCodeAt(0) % 10 + '0'.charCodeAt(0));
out += '}';
}
return len;
}
// push n copies of param on the terminfo stack if not already there
function getparm(parm, n) {
if (seenr) {
if (parm === 1) {
parm = 2;
} else if (parm === 2) {
parm = 1;
}
}
if (onstack === parm) {
if (n > 1) {
warn('string may not be optimal');
out += '%Pa';
while (n--) {
out += '%ga';
}
}
return;
}
if (onstack !== 0) {
push();
}
onstack = parm;
while (n--) {
out += '%p';
out += String.fromCharCode('0'.charCodeAt(0) + parm);
}
if (seenn && parm < 3) {
out += '%{96}%^';
}
if (seenm && parm < 3) {
out += '%{127}%^';
}
}
// push onstack on to the stack
function push() {
if (stackptr >= MAX_PUSHED) {
warn('string too complex to convert');
} else {
stack[stackptr++] = onstack;
}
}
// pop the top of the stack into onstack
function pop() {
if (stackptr === 0) {
if (onstack === 0) {
warn('I\'m confused');
} else {
onstack = 0;
}
} else {
onstack = stack[--stackptr];
}
param++;
}
function see03() {
getparm(param, 1);
out += '%3d';
pop();
}
function invalid() {
out += '%';
i--;
warn('unknown %% code %s (%#x) in %s',
JSON.stringify(s[i]), s[i].charCodeAt(0), cap);
}
// skip the initial padding (if we haven't been told not to)
capstart = null;
if (s == null) s = '';
if (parameterized >= 0 && isdigit(s[i])) {
for (capstart = i;; i++) {
if (!(isdigit(s[i]) || s[i] === '*' || s[i] === '.')) {
break;
}
}
}
while (s[i]) {
switch (s[i]) {
case '%':
i++;
if (parameterized < 1) {
out += '%';
break;
}
switch (s[i++]) {
case '%':
out += '%';
break; ...n/a
_compile = function (info, key, str) {
var v;
this._debug('Compiling %s: %s', key, JSON.stringify(str));
switch (typeof str) {
case 'boolean':
return str;
case 'number':
return str;
case 'string':
break;
default:
return noop;
}
if (!str) {
return noop;
}
// See:
// ~/ncurses/progs/tput.c - tput() - L149
// ~/ncurses/progs/tset.c - set_init() - L992
if (key === 'init_file' || key === 'reset_file') {
try {
str = fs.readFileSync(str, 'utf8');
if (this.debug) {
v = ('return ' + JSON.stringify(str) + ';')
.replace(/\x1b/g, '\\x1b')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');
process.stdout.write(v + '\n');
}
return function() { return str; };
} catch (e) {
return noop;
}
}
var tkey = info.name + '.' + key
, header = 'var v, dyn = {}, stat = {}, stack = [], out = [];'
, footer = ';return out.join("");'
, code = header
, val = str
, buff = ''
, cap
, ch
, fi
, then
, els
, end;
function read(regex, no) {
cap = regex.exec(val);
if (!cap) return;
val = val.substring(cap[0].length);
ch = cap[1];
if (!no) clear();
return cap;
}
function stmt(c) {
if (code[code.length - 1] === ',') {
code = code.slice(0, -1);
}
code += c;
}
function expr(c) {
code += c + ',';
}
function echo(c) {
if (c === '""') return;
expr('out.push(' + c + ')');
}
function print(c) {
buff += c;
}
function clear() {
if (buff) {
echo(JSON.stringify(buff).replace(/\\u00([0-9a-fA-F]{2})/g, '\\x$1'));
buff = '';
}
}
while (val) {
// Ignore newlines
if (read(/^\n /, true)) {
continue;
}
// '^A' -> ^A
if (read(/^\^(.)/i, true)) {
if (!(ch >= ' ' && ch <= '~')) {
this._debug('%s: bad caret char.', tkey);
// NOTE: ncurses appears to simply
// continue in this situation, but
// I could be wrong.
print(cap[0]);
continue;
}
if (ch === '?') {
ch = '\x7f';
} else {
ch = ch.charCodeAt(0) & 31;
if (ch === 0) ch = 128;
ch = String.fromCharCode(ch);
}
print(ch);
continue;
}
// 3 octal digits -> character
if (read(/^\\([0-7]{3})/, true)) {
print(String.fromCharCode(parseInt(ch, 8)));
continue;
}
// '\e' -> ^[
// '\n' -> \n
// '\r' -> \r
// '\0' -> \200 (special case)
if (read(/^\\([eEnlrtbfs\^\\,:0]|.)/, true)) {
switch (ch) {
case 'e':
case 'E':
ch = '\x1b';
break;
case 'n':
ch = '\n';
break;
case 'l':
ch = '\x85';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case 'b':
ch = '\x08';
break;
case 'f':
ch = '\x0c';
break;
case 's':
ch = ' ';
break;
case '^':
ch = '^';
break;
case '\\':
ch = '\\';
break;
case ',':
ch = ',';
break;
case ':':
ch = ':';
break;
case '0':
ch = '\200';
break;
case 'a':
ch = '\x07';
break;
default:
this._debug('%s: bad backslash char.', tkey);
ch = cap[0];
break;
}
print(ch);
continue;
}
// $<5> -> padding
// e.g. flash_screen: '\u001b[?5h$<100/>\u001b[?5l',
if (read(/^\$<(\d+)([*\/]{0,2})>/, true)) {
if (this.padding) print(cap[0]);
continue;
}
// %% outputs `%'
if (read(/^%%/, true)) {
print('%');
continue;
}
// %[[:]flags][width[.precision]][doxXs]
// as in printf, flags are [-+#] and space. Use a `:' to allow the
// next character to be a `-' flag, avoiding interpreting "%-" as an
// operator.
// %c ...n/a
_debug = function () {
if (!this.debug) return;
return console.log.apply(console, arguments);
}n/a
_prefix = function (term) {
// If we have a terminfoFile, or our
// term looks like a filename, use it.
if (term) {
if (~term.indexOf(path.sep)) {
return term;
}
if (this.terminfoFile) {
return this.terminfoFile;
}
}
var paths = Tput.ipaths.slice()
, file;
if (this.terminfoPrefix) {
paths.unshift(this.terminfoPrefix);
}
// Try exact matches.
file = this._tprefix(paths, term);
if (file) return file;
// Try similar matches.
file = this._tprefix(paths, term, true);
if (file) return file;
// Not found.
throw new Error('Terminfo directory not found.');
}n/a
_print = function (code, print, done) {
var xon = !this.bools.needs_xon_xoff || this.bools.xon_xoff;
print = print || write;
done = done || noop;
if (!this.padding) {
print(code);
return done();
}
var parts = code.split(/(?=\$<[\d.]+[*\/]{0,2}>)/)
, i = 0;
(function next() {
if (i === parts.length) {
return done();
}
var part = parts[i++]
, padding = /^\$<([\d.]+)([*\/]{0,2})>/.exec(part)
, amount
, suffix;
// , affect;
if (!padding) {
print(part);
return next();
}
part = part.substring(padding[0].length);
amount = +padding[1];
suffix = padding[2];
// A `/' suffix indicates that the padding is mandatory and forces a
// delay of the given number of milliseconds even on devices for which xon
// is present to indicate flow control.
if (xon && !~suffix.indexOf('/')) {
print(part);
return next();
}
// A `*' indicates that the padding required is proportional to the number
// of lines affected by the operation, and the amount given is the
// per-affected-unit padding required. (In the case of insert character,
// the factor is still the number of lines affected.) Normally, padding is
// advisory if the device has the xon capability; it is used for cost
// computation but does not trigger delays.
if (~suffix.indexOf('*')) {
// XXX Disable this for now.
amount = amount;
// if (affect = /\x1b\[(\d+)[LM]/.exec(part)) {
// amount *= +affect[1];
// }
// The above is a huge workaround. In reality, we need to compile
// `_print` into the string functions and check the cap name and
// params.
// if (cap === 'insert_line' || cap === 'delete_line') {
// amount *= params[0];
// }
// if (cap === 'clear_screen') {
// amount *= process.stdout.rows;
// }
}
return setTimeout(function() {
print(part);
return next();
}, amount);
})();
}n/a
_tprefix = function (prefix, term, soft) {
if (!prefix) return;
var file
, dir
, i
, sdiff
, sfile
, list;
if (Array.isArray(prefix)) {
for (i = 0; i < prefix.length; i++) {
file = this._tprefix(prefix[i], term, soft);
if (file) return file;
}
return;
}
var find = function(word) {
var file, ch;
file = path.resolve(prefix, word[0]);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
ch = word[0].charCodeAt(0).toString(16);
if (ch.length < 2) ch = '0' + ch;
file = path.resolve(prefix, ch);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
};
if (!term) {
// Make sure the directory's sub-directories
// are all one-letter, or hex digits.
// return find('x') ? prefix : null;
try {
dir = fs.readdirSync(prefix).filter(function(file) {
return file.length !== 1 && !/^[0-9a-fA-F]{2}$/.test(file);
});
if (!dir.length) {
return prefix;
}
} catch (e) {
;
}
return;
}
term = path.basename(term);
dir = find(term);
if (!dir) return;
if (soft) {
try {
list = fs.readdirSync(dir);
} catch (e) {
return;
}
list.forEach(function(file) {
if (file.indexOf(term) === 0) {
var diff = file.length - term.length;
if (!sfile || diff < sdiff) {
sdiff = diff;
sfile = file;
}
}
});
return sfile && (soft || sdiff === 0)
? path.resolve(dir, sfile)
: null;
}
file = path.resolve(dir, term);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
}n/a
_tryCap = function (file, term) {
if (!file) return;
var terms
, data
, i;
if (Array.isArray(file)) {
for (i = 0; i < file.length; i++) {
data = this._tryCap(file[i], term);
if (data) return data;
}
return;
}
// If the termcap string starts with `/`,
// ncurses considers it a filename.
data = file[0] === '/'
? tryRead(file)
: file;
if (!data) return;
terms = this.parseTermcap(data, file);
if (term && !terms[term]) {
return;
}
return terms;
}n/a
_useInternalCap = function (name) {
name = path.basename(name);
return this.injectTermcap(__dirname + '/../usr/' + name + '.termcap');
}n/a
_useInternalInfo = function (name) {
name = path.basename(name);
return this.injectTerminfo(__dirname + '/../usr/' + name);
}n/a
_useVt102Cap = function () {
return this.injectTermcap('vt102');
}n/a
_useXtermCap = function () {
return this.injectTermcap(__dirname + '/../usr/xterm.termcap');
}n/a
_useXtermInfo = function () {
return this.injectTerminfo(__dirname + '/../usr/xterm');
}n/a
compile = function (info) {
var self = this;
if (!info) {
throw new Error('Terminal not found.');
}
this.detectFeatures(info);
this._debug(info);
info.all = {};
info.methods = {};
['bools', 'numbers', 'strings'].forEach(function(type) {
Object.keys(info[type]).forEach(function(key) {
info.all[key] = info[type][key];
info.methods[key] = self._compile(info, key, info.all[key]);
});
});
Tput.bools.forEach(function(key) {
if (info.methods[key] == null) info.methods[key] = false;
});
Tput.numbers.forEach(function(key) {
if (info.methods[key] == null) info.methods[key] = -1;
});
Tput.strings.forEach(function(key) {
if (!info.methods[key]) info.methods[key] = noop;
});
Object.keys(info.methods).forEach(function(key) {
if (!Tput.alias[key]) return;
Tput.alias[key].forEach(function(alias) {
info.methods[alias] = info.methods[key];
});
// Could just use:
// Object.keys(Tput.aliasMap).forEach(function(key) {
// info.methods[key] = info.methods[Tput.aliasMap[key]];
// });
});
return info;
}n/a
compileAll = function (start) {
var self = this
, all = {};
this.getAll().forEach(function(name) {
if (start && name !== start) {
return;
} else {
start = null;
}
all[name] = self.compileTerminfo(name);
});
return all;
}n/a
compileTermcap = function (term) {
return this.compile(this.readTermcap(term));
}n/a
compileTerminfo = function (term) {
return this.compile(this.readTerminfo(term));
}n/a
detectBrokenACS = function (info) {
// ncurses-compatible env variable.
if (process.env.NCURSES_NO_UTF8_ACS != null) {
return !!+process.env.NCURSES_NO_UTF8_ACS;
}
// If the terminal supports unicode, we don't need ACS.
if (info.numbers.U8 >= 0) {
return !!info.numbers.U8;
}
// The linux console is just broken for some reason.
// Apparently the Linux console does not support ACS,
// but it does support the PC ROM character set.
if (info.name === 'linux') {
return true;
}
// PC alternate charset
// if (acsc.indexOf('+\x10,\x11-\x18.\x190') === 0) {
if (this.detectPCRomSet(info)) {
return true;
}
// screen termcap is bugged?
if (this.termcap
&& info.name.indexOf('screen') === 0
&& process.env.TERMCAP
&& ~process.env.TERMCAP.indexOf('screen')
&& ~process.env.TERMCAP.indexOf('hhII00')) {
if (~info.strings.enter_alt_charset_mode.indexOf('\016')
|| ~info.strings.enter_alt_charset_mode.indexOf('\017')
|| ~info.strings.set_attributes.indexOf('\016')
|| ~info.strings.set_attributes.indexOf('\017')) {
return true;
}
}
return false;
}n/a
detectFeatures = function (info) {
var data = this.parseACS(info);
info.features = {
unicode: this.detectUnicode(info),
brokenACS: this.detectBrokenACS(info),
PCRomSet: this.detectPCRomSet(info),
magicCookie: this.detectMagicCookie(info),
padding: this.detectPadding(info),
setbuf: this.detectSetbuf(info),
acsc: data.acsc,
acscr: data.acscr
};
return info.features;
}n/a
detectMagicCookie = function () {
return process.env.NCURSES_NO_MAGIC_COOKIE == null;
}n/a
detectPCRomSet = function (info) {
var s = info.strings;
if (s.enter_pc_charset_mode && s.enter_alt_charset_mode
&& s.enter_pc_charset_mode === s.enter_alt_charset_mode
&& s.exit_pc_charset_mode === s.exit_alt_charset_mode) {
return true;
}
return false;
}n/a
detectPadding = function () {
return process.env.NCURSES_NO_PADDING == null;
}n/a
detectSetbuf = function () {
return process.env.NCURSES_NO_SETBUF == null;
}n/a
detectUnicode = function () {
if (this.options.forceUnicode != null) {
return this.options.forceUnicode;
}
var LANG = process.env.LANG
+ ':' + process.env.LANGUAGE
+ ':' + process.env.LC_ALL
+ ':' + process.env.LC_CTYPE;
return /utf-?8/i.test(LANG) || (this.GetConsoleCP() === 65001);
}n/a
getAll = function () {
var dir = this._prefix()
, list = asort(fs.readdirSync(dir))
, infos = [];
list.forEach(function(letter) {
var terms = asort(fs.readdirSync(path.resolve(dir, letter)));
infos.push.apply(infos, terms);
});
function asort(obj) {
return obj.sort(function(a, b) {
a = a.toLowerCase().charCodeAt(0);
b = b.toLowerCase().charCodeAt(0);
return a - b;
});
}
return infos;
}n/a
has = function (name) {
name = Tput.aliasMap[name];
var val = this.all[name];
if (!name) return false;
if (typeof val === 'number') {
return val !== -1;
}
return !!val;
}n/a
inject = function (info) {
var self = this
, methods = info.methods || info;
Object.keys(methods).forEach(function(key) {
if (typeof methods[key] !== 'function') {
self[key] = methods[key];
return;
}
self[key] = function() {
var args = Array.prototype.slice.call(arguments);
return methods[key].call(self, args);
};
});
this.info = info;
this.all = info.all;
this.methods = info.methods;
this.bools = info.bools;
this.numbers = info.numbers;
this.strings = info.strings;
if (!~info.names.indexOf(this.terminal)) {
this.terminal = info.name;
}
this.features = info.features;
Object.keys(info.features).forEach(function(key) {
if (key === 'padding') {
if (!info.features.padding && self.options.padding !== true) {
self.padding = false;
}
return;
}
self[key] = info.features[key];
});
}n/a
injectTermcap = function (term) {
return this.inject(this.compileTermcap(term));
}n/a
injectTerminfo = function (term) {
return this.inject(this.compileTerminfo(term));
}n/a
parseACS = function (info) {
var data = {};
data.acsc = {};
data.acscr = {};
// Possibly just return an empty object, as done here, instead of
// specifically saying ACS is "broken" above. This would be more
// accurate to ncurses logic. But it doesn't really matter.
if (this.detectPCRomSet(info)) {
return data;
}
// See: ~/ncurses/ncurses/tinfo/lib_acs.c: L208
Object.keys(Tput.acsc).forEach(function(ch) {
var acs_chars = info.strings.acs_chars || ''
, i = acs_chars.indexOf(ch)
, next = acs_chars[i + 1];
if (!next || i === -1 || !Tput.acsc[next]) {
return;
}
data.acsc[ch] = Tput.acsc[next];
data.acscr[Tput.acsc[next]] = ch;
});
return data;
}n/a
parseExtended = function (data) {
var info = {}
, l = data.length
, i = 0;
var h = info.header = {
dataSize: data.length,
headerSize: 10,
boolCount: (data[i + 1] << 8) | data[i + 0],
numCount: (data[i + 3] << 8) | data[i + 2],
strCount: (data[i + 5] << 8) | data[i + 4],
strTableSize: (data[i + 7] << 8) | data[i + 6],
lastStrTableOffset: (data[i + 9] << 8) | data[i + 8]
};
// h.symOffsetCount = h.strTableSize - h.strCount;
h.total = h.headerSize
+ h.boolCount
+ h.numCount * 2
+ h.strCount * 2
+ h.strTableSize;
i += h.headerSize;
// Booleans Section
// One byte for each flag
var _bools = [];
l = i + h.boolCount;
for (; i < l; i++) {
_bools.push(data[i] === 1);
}
// Null byte in between to make sure numbers begin on an even byte.
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
// Numbers Section
var _numbers = [];
l = i + h.numCount * 2;
for (; i < l; i += 2) {
if (data[i + 1] === 0377 && data[i] === 0377) {
_numbers.push(-1);
} else {
_numbers.push((data[i + 1] << 8) | data[i]);
}
}
// Strings Section
var _strings = [];
l = i + h.strCount * 2;
for (; i < l; i += 2) {
if (data[i + 1] === 0377 && data[i] === 0377) {
_strings.push(-1);
} else {
_strings.push((data[i + 1] << 8) | data[i]);
}
}
// Pass over the sym offsets and get to the string table.
i = data.length - h.lastStrTableOffset;
// Might be better to do this instead if the file has trailing bytes:
// i += h.symOffsetCount * 2;
// String Table
var high = 0;
_strings.forEach(function(offset, k) {
if (offset === -1) {
_strings[k] = '';
return;
}
var s = i + offset
, j = s;
while (data[j]) j++;
assert(j < data.length);
// Find out where the string table ends by
// getting the highest string length.
if (high < j - i) {
high = j - i;
}
_strings[k] = data.toString('ascii', s, j);
});
// Symbol Table
// Add one to the highest string length because we didn't count \0.
i += high + 1;
l = data.length;
var sym = []
, j;
for (; i < l; i++) {
j = i;
while (data[j]) j++;
sym.push(data.toString('ascii', i, j));
i = j;
}
// Identify by name
j = 0;
info.bools = {};
_bools.forEach(function(bool) {
info.bools[sym[j++]] = bool;
});
info.numbers = {};
_numbers.forEach(function(number) {
info.numbers[sym[j++]] = number;
});
info.strings = {};
_strings.forEach(function(string) {
info.strings[sym[j++]] = string;
});
// Should be the very last bit of data.
assert.equal(i, data.length);
return info;
}n/a
parseTermcap = function (data, file) {
var terms = {}
, parts
, term
, entries
, fields
, field
, names
, i
, j
, k;
// remove escaped newlines
data = data.replace(/\\\n[ \t]*/g, '');
// remove comments
data = data.replace(/^#[^\n]+/gm, '');
// split entries
entries = data.trim().split(/\n+/);
for (i = 0; i < entries.length; i++) {
fields = entries[i].split(/:+/);
for (j = 0; j < fields.length; j++) {
field = fields[j].trim();
if (!field) continue;
if (j === 0) {
names = field.split('|');
term = {
name: names[0],
names: names,
desc: names.pop(),
file: ~file.indexOf(path.sep)
? path.resolve(file)
: file,
termcap: true
};
for (k = 0; k < names.length; k++) {
terms[names[k]] = term;
}
term.bools = {};
term.numbers = {};
term.strings = {};
continue;
}
if (~field.indexOf('=')) {
parts = field.split('=');
term.strings[parts[0]] = parts.slice(1).join('=');
} else if (~field.indexOf('#')) {
parts = field.split('#');
term.numbers[parts[0]] = +parts.slice(1).join('#');
} else {
term.bools[field] = true;
}
}
}
return terms;
}n/a
parseTerminfo = function (data, file) {
var info = {}
, extended
, l = data.length
, i = 0
, v
, o;
var h = info.header = {
dataSize: data.length,
headerSize: 12,
magicNumber: (data[1] << 8) | data[0],
namesSize: (data[3] << 8) | data[2],
boolCount: (data[5] << 8) | data[4],
numCount: (data[7] << 8) | data[6],
strCount: (data[9] << 8) | data[8],
strTableSize: (data[11] << 8) | data[10]
};
h.total = h.headerSize
+ h.namesSize
+ h.boolCount
+ h.numCount * 2
+ h.strCount * 2
+ h.strTableSize;
i += h.headerSize;
// Names Section
var names = data.toString('ascii', i, i + h.namesSize - 1)
, parts = names.split('|')
, name = parts[0]
, desc = parts.pop();
info.name = name;
info.names = parts;
info.desc = desc;
info.dir = path.resolve(file, '..', '..');
info.file = file;
i += h.namesSize - 1;
// Names is nul-terminated.
assert.equal(data[i], 0);
i++;
// Booleans Section
// One byte for each flag
// Same order as <term.h>
info.bools = {};
l = i + h.boolCount;
o = 0;
for (; i < l; i++) {
v = Tput.bools[o++];
info.bools[v] = data[i] === 1;
}
// Null byte in between to make sure numbers begin on an even byte.
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
// Numbers Section
info.numbers = {};
l = i + h.numCount * 2;
o = 0;
for (; i < l; i += 2) {
v = Tput.numbers[o++];
if (data[i + 1] === 0377 && data[i] === 0377) {
info.numbers[v] = -1;
} else {
info.numbers[v] = (data[i + 1] << 8) | data[i];
}
}
// Strings Section
info.strings = {};
l = i + h.strCount * 2;
o = 0;
for (; i < l; i += 2) {
v = Tput.strings[o++];
if (data[i + 1] === 0377 && data[i] === 0377) {
info.strings[v] = -1;
} else {
info.strings[v] = (data[i + 1] << 8) | data[i];
}
}
// String Table
Object.keys(info.strings).forEach(function(key) {
if (info.strings[key] === -1) {
delete info.strings[key];
return;
}
// Workaround: fix an odd bug in the screen-256color terminfo where it tries
// to set -1, but it appears to have {0xfe, 0xff} instead of {0xff, 0xff}.
// TODO: Possibly handle errors gracefully below, as well as in the
// extended info. Also possibly do: `if (info.strings[key] >= data.length)`.
if (info.strings[key] === 65534) {
delete info.strings[key];
return;
}
var s = i + info.strings[key]
, j = s;
while (data[j]) j++;
assert(j < data.length);
info.strings[key] = data.toString('ascii', s, j);
});
// Extended Header
if (this.extended !== false) {
i--;
i += h.strTableSize;
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
l = data.length;
if (i < l - 1) {
try {
extended = this.parseExtended(data.slice(i));
} catch (e) {
if (this.debug) {
throw e;
}
return info;
}
info.header.extended = extended.header;
['bools', 'numbers', 'strings'].forEach(function(key) {
merge(info[key], extended[key]);
});
}
}
return info;
}...
);
function readMethods() {
Tput._infoBuffer = new Buffer(TERMINFO, 'base64');
Tput.prototype.readTerminfo = function() {
this.terminal = TERMINFO_NAME;
return this.parseTerminfo(Tput._infoBuffer, TERMINFO_PATH);
};
Tput.cpaths = [];
Tput.termcap = TERMCAP;
Tput.prototype._readTermcap = Tput.prototype.readTermcap;
Tput.prototype.readTermcap = function() {
...readTermcap = function (term) {
var self = this
, terms
, term_
, root
, paths;
term = term || this.terminal;
// Termcap has a bunch of terminals usually stored in one file/string,
// so we need to find the one containing our desired terminal.
if (~term.indexOf(path.sep) && (terms = this._tryCap(path.resolve(term)))) {
term_ = path.basename(term).split('.')[0];
if (terms[process.env.TERM]) {
term = process.env.TERM;
} else if (terms[term_]) {
term = term_;
} else {
term = Object.keys(terms)[0];
}
} else {
paths = Tput.cpaths.slice();
if (this.termcapFile) {
paths.unshift(this.termcapFile);
}
paths.push(Tput.termcap);
terms = this._tryCap(paths, term);
}
if (!terms) {
throw new Error('Cannot find termcap for: ' + term);
}
root = terms[term];
if (this.debug) {
this._termcap = terms;
}
(function tc(term) {
if (term && term.strings.tc) {
root.inherits = root.inherits || [];
root.inherits.push(term.strings.tc);
var names = terms[term.strings.tc]
? terms[term.strings.tc].names
: [term.strings.tc];
self._debug('%s inherits from %s.',
term.names.join('/'), names.join('/'));
var inherit = tc(terms[term.strings.tc]);
if (inherit) {
['bools', 'numbers', 'strings'].forEach(function(type) {
merge(term[type], inherit[type]);
});
}
}
return term;
})(root);
// Translate termcap names to terminfo-style names.
root = this.translateTermcap(root);
return root;
}n/a
readTerminfo = function (term) {
var data
, file
, info;
term = term || this.terminal;
file = path.normalize(this._prefix(term));
data = fs.readFileSync(file);
info = this.parseTerminfo(data, file);
if (this.debug) {
this._terminfo = info;
}
return info;
}n/a
setup = function () {
this.error = null;
try {
if (this.termcap) {
try {
this.injectTermcap();
} catch (e) {
if (this.debug) throw e;
this.error = new Error('Termcap parse error.');
this._useInternalCap(this.terminal);
}
} else {
try {
this.injectTerminfo();
} catch (e) {
if (this.debug) throw e;
this.error = new Error('Terminfo parse error.');
this._useInternalInfo(this.terminal);
}
}
} catch (e) {
// If there was an error, fallback
// to an internally stored terminfo/cap.
if (this.debug) throw e;
this.error = new Error('Terminfo not found.');
this._useXtermInfo();
}
}n/a
term = function (is) {
return this.terminal.indexOf(is) === 0;
}n/a
translateTermcap = function (info) {
var self = this
, out = {};
if (!info) return;
this._debug(info);
['name', 'names', 'desc', 'file', 'termcap'].forEach(function(key) {
out[key] = info[key];
});
// Separate aliases for termcap
var map = (function() {
var out = {};
Object.keys(Tput.alias).forEach(function(key) {
var aliases = Tput.alias[key];
out[aliases.termcap] = key;
});
return out;
})();
// Translate termcap cap names to terminfo cap names.
// e.g. `up` -> `cursor_up`
['bools', 'numbers', 'strings'].forEach(function(key) {
out[key] = {};
Object.keys(info[key]).forEach(function(cap) {
if (key === 'strings') {
info.strings[cap] = self._captoinfo(cap, info.strings[cap], 1);
}
if (map[cap]) {
out[key][map[cap]] = info[key][cap];
} else {
// NOTE: Possibly include all termcap names
// in a separate alias.js file. Some are
// missing from the terminfo alias.js file
// which is why we have to do this:
// See: $ man termcap
out[key][cap] = info[key][cap];
}
});
});
return out;
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
curl = function (url) {
try {
return cp.execFileSync('curl',
['-s', '-A', '', url],
{ stdio: ['ignore', 'pipe', 'ignore'] });
} catch (e) {
;
}
try {
return cp.execFileSync('wget',
['-U', '', '-O', '-', url],
{ stdio: ['ignore', 'pipe', 'ignore'] });
} catch (e) {
;
}
throw new Error('curl or wget failed.');
}n/a
clearImage = function () {
this.stop();
this.setContent('');
this.img = null;
this.cellmap = null;
}n/a
pause = function () {
if (!this.img) return;
return this.img.pause();
}n/a
play = function () {
var self = this;
if (!this.img) return;
return this.img.play(function(bmp, cellmap) {
self.cellmap = cellmap;
self.screen.render();
});
}n/a
render = function () {
var coords = this._render();
if (!coords) return;
if (this.img && this.cellmap) {
this.img.renderElement(this.cellmap, this);
}
return coords;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...setImage = function (file) {
this.file = typeof file === 'string' ? file : null;
if (/^https?:/.test(file)) {
file = ANSIImage.curl(file);
}
var width = this.position.width;
var height = this.position.height;
if (width != null) {
width = this.width;
}
if (height != null) {
height = this.height;
}
try {
this.setContent('');
this.img = tng(file, {
colors: colors,
width: width,
height: height,
scale: this.scale,
ascii: this.options.ascii,
speed: this.options.speed,
filename: this.file
});
if (width == null || height == null) {
this.width = this.img.cellmap[0].length;
this.height = this.img.cellmap.length;
}
if (this.img.frames && this.options.animate) {
this.play();
} else {
this.cellmap = this.img.cellmap;
}
} catch (e) {
this.setContent('Image Error: ' + e.message);
this.img = null;
this.cellmap = null;
}
}n/a
stop = function () {
if (!this.img) return;
return this.img.stop();
}...
self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
}
});
gpm.on('error', function() {
self.stop();
});
});
}
}
GpmClient.prototype.__proto__ = EventEmitter.prototype;
...function BigText(options) {
if (!(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}n/a
loadFont = function (filename) {
var self = this
, data
, font;
data = JSON.parse(fs.readFileSync(filename, 'utf8'));
this.ratio.width = data.width;
this.ratio.height = data.height;
function convertLetter(ch, lines) {
var line, i;
while (lines.length > self.ratio.height) {
lines.shift();
lines.pop();
}
lines = lines.map(function(line) {
var chs = line.split('');
chs = chs.map(function(ch) {
return ch === ' ' ? 0 : 1;
});
while (chs.length < self.ratio.width) {
chs.push(0);
}
return chs;
});
while (lines.length < self.ratio.height) {
line = [];
for (i = 0; i < self.ratio.width; i++) {
line.push(0);
}
lines.push(line);
}
return lines;
}
font = Object.keys(data.glyphs).reduce(function(out, ch) {
var lines = data.glyphs[ch].map;
out[ch] = convertLetter(ch, lines);
return out;
}, {});
delete font[' '];
return font;
}n/a
render = function () {
if (this.position.width == null || this._shrinkWidth) {
// if (this.width - this.iwidth < this.ratio.width * this.text.length + 1) {
this.position.width = this.ratio.width * this.text.length + 1;
this._shrinkWidth = true;
// }
}
if (this.position.height == null || this._shrinkHeight) {
// if (this.height - this.iheight < this.ratio.height + 0) {
this.position.height = this.ratio.height + 0;
this._shrinkHeight = true;
// }
}
var coords = this._render();
if (!coords) return;
var lines = this.screen.lines
, left = coords.xi + this.ileft
, top = coords.yi + this.itop
, right = coords.xl - this.iright
, bottom = coords.yl - this.ibottom;
var dattr = this.sattr(this.style)
, bg = dattr & 0x1ff
, fg = (dattr >> 9) & 0x1ff
, flags = (dattr >> 18) & 0x1ff
, attr = (flags << 18) | (bg << 9) | fg;
for (var x = left, i = 0; x < right; x += this.ratio.width, i++) {
var ch = this.text[i];
if (!ch) break;
var map = this.font[ch];
if (!map) continue;
for (var y = top; y < Math.min(bottom, top + this.ratio.height); y++) {
if (!lines[y]) continue;
var mline = map[y - top];
if (!mline) continue;
for (var mx = 0; mx < this.ratio.width; mx++) {
var mcell = mline[mx];
if (mcell == null) break;
if (this.fch && this.fch !== ' ') {
lines[y][x + mx][0] = dattr;
lines[y][x + mx][1] = mcell === 1 ? this.fch : this.ch;
} else {
lines[y][x + mx][0] = mcell === 1 ? attr : dattr;
lines[y][x + mx][1] = mcell === 1 ? ' ' : this.ch;
}
}
lines[y].dirty = true;
}
}
return coords;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...setContent = function (content) {
this.content = '';
this.text = content || '';
}...
height: 'shrink',
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
...function Button(options) {
var self = this;
if (!(this instanceof Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}n/a
press = function () {
this.focus();
this.value = true;
var result = this.emit('press');
delete this.value;
return result;
}n/a
function Checkbox(options) {
var self = this;
if (!(this instanceof Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}n/a
check = function () {
if (this.checked) return;
this.checked = this.value = true;
this.emit('check');
}n/a
render = function () {
this.clearPos(true);
this.setContent('[' + (this.checked ? 'x' : ' ') + '] ' + this.text, true);
return this._render();
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...toggle = function () {
return this.checked
? this.uncheck()
: this.check();
}n/a
uncheck = function () {
if (!this.checked) return;
this.checked = this.value = false;
this.emit('uncheck');
}n/a
RGBToHex = function (r, g, b) {
if (Array.isArray(r)) {
b = r[2], g = r[1], r = r[0];
}
function hex(n) {
n = n.toString(16);
if (n.length < 2) n = '0' + n;
return n;
}
return '#' + hex(r) + hex(g) + hex(b);
}n/a
function blend(attr, attr2, alpha) {
var name, i, c, nc;
var bg = attr & 0x1ff;
if (attr2 != null) {
var bg2 = attr2 & 0x1ff;
if (bg === 0x1ff) bg = 0;
if (bg2 === 0x1ff) bg2 = 0;
bg = exports.mixColors(bg, bg2, alpha);
} else {
if (blend._cache[bg] != null) {
bg = blend._cache[bg];
// } else if (bg < 8) {
// bg += 8;
} else if (bg >= 8 && bg <= 15) {
bg -= 8;
} else {
name = exports.ncolors[bg];
if (name) {
for (i = 0; i < exports.ncolors.length; i++) {
if (name === exports.ncolors[i] && i !== bg) {
c = exports.vcolors[bg];
nc = exports.vcolors[i];
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
blend._cache[bg] = i;
bg = i;
break;
}
}
}
}
}
}
attr &= ~0x1ff;
attr |= bg;
var fg = (attr >> 9) & 0x1ff;
if (attr2 != null) {
var fg2 = (attr2 >> 9) & 0x1ff;
// 0, 7, 188, 231, 251
if (fg === 0x1ff) {
// XXX workaround
fg = 248;
} else {
if (fg === 0x1ff) fg = 7;
if (fg2 === 0x1ff) fg2 = 7;
fg = exports.mixColors(fg, fg2, alpha);
}
} else {
if (blend._cache[fg] != null) {
fg = blend._cache[fg];
// } else if (fg < 8) {
// fg += 8;
} else if (fg >= 8 && fg <= 15) {
fg -= 8;
} else {
name = exports.ncolors[fg];
if (name) {
for (i = 0; i < exports.ncolors.length; i++) {
if (name === exports.ncolors[i] && i !== fg) {
c = exports.vcolors[fg];
nc = exports.vcolors[i];
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
blend._cache[fg] = i;
fg = i;
break;
}
}
}
}
}
}
attr &= ~(0x1ff << 9);
attr |= fg << 9;
return attr;
}n/a
convert = function (color) {
if (typeof color === 'number') {
;
} else if (typeof color === 'string') {
color = color.replace(/[\- ]/g, '');
if (colorNames[color] != null) {
color = colorNames[color];
} else {
color = exports.match(color);
}
} else if (Array.isArray(color)) {
color = exports.match(color);
} else {
color = -1;
}
return color !== -1 ? color : 0x1ff;
}n/a
hexToRGB = function (hex) {
if (hex.length === 4) {
hex = hex[0]
+ hex[1] + hex[1]
+ hex[2] + hex[2]
+ hex[3] + hex[3];
}
var col = parseInt(hex.substring(1), 16)
, r = (col >> 16) & 0xff
, g = (col >> 8) & 0xff
, b = col & 0xff;
return [r, g, b];
}n/a
match = function (r1, g1, b1) {
if (typeof r1 === 'string') {
var hex = r1;
if (hex[0] !== '#') {
return -1;
}
hex = exports.hexToRGB(hex);
r1 = hex[0], g1 = hex[1], b1 = hex[2];
} else if (Array.isArray(r1)) {
b1 = r1[2], g1 = r1[1], r1 = r1[0];
}
var hash = (r1 << 16) | (g1 << 8) | b1;
if (exports._cache[hash] != null) {
return exports._cache[hash];
}
var ldiff = Infinity
, li = -1
, i = 0
, c
, r2
, g2
, b2
, diff;
for (; i < exports.vcolors.length; i++) {
c = exports.vcolors[i];
r2 = c[0];
g2 = c[1];
b2 = c[2];
diff = colorDistance(r1, g1, b1, r2, g2, b2);
if (diff === 0) {
li = i;
break;
}
if (diff < ldiff) {
ldiff = diff;
li = i;
}
}
return exports._cache[hash] = li;
}n/a
mixColors = function (c1, c2, alpha) {
// if (c1 === 0x1ff) return c1;
// if (c2 === 0x1ff) return c1;
if (c1 === 0x1ff) c1 = 0;
if (c2 === 0x1ff) c2 = 0;
if (alpha == null) alpha = 0.5;
c1 = exports.vcolors[c1];
var r1 = c1[0];
var g1 = c1[1];
var b1 = c1[2];
c2 = exports.vcolors[c2];
var r2 = c2[0];
var g2 = c2[1];
var b2 = c2[2];
r1 += (r2 - r1) * alpha | 0;
g1 += (g2 - g1) * alpha | 0;
b1 += (b2 - b1) * alpha | 0;
return exports.match([r1, g1, b1]);
}n/a
reduce = function (color, total) {
if (color >= 16 && total <= 16) {
color = exports.ccolors[color];
} else if (color >= 8 && total <= 8) {
color -= 8;
} else if (color >= 2 && total <= 2) {
color %= 2;
}
return color;
}...
// row).
if (el.position.left + el.width <= width) {
el.position.top = rowOffset;
} else {
// Otherwise we need to start a new row and calculate a new
// `rowOffset` and `rowIndex` (the index of the child on the current
// row).
rowOffset += self.children.slice(rowIndex, i).reduce(function(out, el) {
if (!self.isRendered(el)) return out;
out = Math.max(out, el.lpos.yl - el.lpos.yi);
return out;
}, 0);
rowIndex = i;
el.position.left = 0;
el.position.top = rowOffset;
...function EventEmitter() {
if (!this._events) this._events = {};
}n/a
function EventEmitter() {
if (!this._events) this._events = {};
}n/a
_emit = function (type, args) {
var handler = this._events[type]
, ret;
// if (type !== 'event') {
// this._emit('event', [type.replace(/^element /, '')].concat(args));
// }
if (!handler) {
if (type === 'error') {
throw new args[0];
}
return;
}
if (typeof handler === 'function') {
return handler.apply(this, args);
}
for (var i = 0; i < handler.length; i++) {
if (handler[i].apply(this, args) === false) {
ret = false;
}
}
return ret !== false;
}...
if (!this._events[type]) {
this._events[type] = listener;
} else if (typeof this._events[type] === 'function') {
this._events[type] = [this._events[type], listener];
} else {
this._events[type].push(listener);
}
this._emit('newListener', [type, listener]);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.removeListener = function(type, listener) {
var handler = this._events[type];
if (!handler) return;
...addListener = function (type, listener) {
if (!this._events[type]) {
this._events[type] = listener;
} else if (typeof this._events[type] === 'function') {
this._events[type] = [this._events[type], listener];
} else {
this._events[type].push(listener);
}
this._emit('newListener', [type, listener]);
}n/a
emit = function (type) {
var args = slice.call(arguments, 1)
, params = slice.call(arguments)
, el = this;
this._emit('event', params);
if (this.type === 'screen') {
return this._emit(type, args);
}
if (this._emit(type, args) === false) {
return false;
}
type = 'element ' + type;
args.unshift(this);
// `element` prefix
// params = [type].concat(args);
// no `element` prefix
// params.splice(1, 0, this);
do {
// el._emit('event', params);
if (!el._events[type]) continue;
if (el._emit(type, args) === false) {
return false;
}
} while (el = el.parent);
return true;
}...
}
});
client.on('window size', function(data) {
if (data.command === 'sb') {
client.columns = data.columns;
client.rows = data.rows;
client.emit('resize');
}
});
// Make the client look like a tty:
client.setRawMode = function(mode) {
client.isRaw = mode;
if (!client.writable) return;
...listeners = function (type) {
return typeof this._events[type] === 'function'
? [this._events[type]]
: this._events[type] || [];
}...
var EventEmitter = require('events').EventEmitter;
// NOTE: node <=v0.8.x has no EventEmitter.listenerCount
function listenerCount(stream, event) {
return EventEmitter.listenerCount
? EventEmitter.listenerCount(stream, event)
: stream.listeners(event).length;
}
/**
* accepts a readable Stream instance and makes it emit "keypress" events
*/
function emitKeypressEvents(stream) {
...off = function (type, listener) {
var handler = this._events[type];
if (!handler) return;
if (typeof handler === 'function' || handler.length === 1) {
delete this._events[type];
this._emit('removeListener', [type, listener]);
return;
}
for (var i = 0; i < handler.length; i++) {
if (handler[i] === listener || handler[i].listener === listener) {
handler.splice(i, 1);
this._emit('removeListener', [type, listener]);
return;
}
}
}n/a
on = function (type, listener) {
if (!this._events[type]) {
this._events[type] = listener;
} else if (typeof this._events[type] === 'function') {
this._events[type] = [this._events[type], listener];
} else {
this._events[type].push(listener);
}
this._emit('newListener', [type, listener]);
}...
width: 'shrink',
height: 'shrink',
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
...once = function (type, listener) {
function on() {
this.removeListener(type, on);
return listener.apply(this, arguments);
}
on.listener = listener;
return this.on(type, on);
}n/a
removeAllListeners = function (type) {
if (type) {
delete this._events[type];
} else {
this._events = {};
}
}n/a
removeListener = function (type, listener) {
var handler = this._events[type];
if (!handler) return;
if (typeof handler === 'function' || handler.length === 1) {
delete this._events[type];
this._emit('removeListener', [type, listener]);
return;
}
for (var i = 0; i < handler.length; i++) {
if (handler[i] === listener || handler[i].listener === listener) {
handler.splice(i, 1);
this._emit('removeListener', [type, listener]);
return;
}
}
}...
- __key(name, listener)__ - Bind a keypress listener for a specific key.
- __onceKey(name, listener)__ - Bind a keypress listener for a specific key
once.
- __unkey(name, listener)__ - Remove a keypress listener for a specific key.
- __onScreenEvent(type, handler)__ - Same as`el.on('screen', ...)` except this
will automatically keep track of which listeners are bound to the screen
object. For use with `removeScreenEvent()`, `free()`, and `destroy()`.
- __removeScreenEvent(type, handler)__ - Same as`el.removeListener('screen',
...)` except this will automatically keep track of which listeners are bound
to the screen object. For use with `onScreenEvent()`, `free()`, and
`destroy()`.
- __free()__ - Free up the element. Automatically unbind all events that may
have been bound to the screen object. This prevents memory leaks. For use
with `onScreenEvent()`, `removeScreenEvent()`, and `destroy()`.
- __destroy()__ - Same as the `detach()` method, except this will automatically
...setMaxListeners = function (n) {
this._maxListeners = n;
}n/a
function FileManager(options) {
var self = this;
if (!(this instanceof Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', file));
}
self.refresh();
} else {
self.emit('file', file);
}
});
});
}n/a
pick = function (cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
var self = this
, focused = this.screen.focused === this
, hidden = this.hidden
, onfile
, oncancel;
function resume() {
self.removeListener('file', onfile);
self.removeListener('cancel', oncancel);
if (hidden) {
self.hide();
}
if (!focused) {
self.screen.restoreFocus();
}
self.screen.render();
}
this.on('file', onfile = function(file) {
resume();
return callback(null, file);
});
this.on('cancel', oncancel = function() {
resume();
return callback();
});
this.refresh(cwd, function(err) {
if (err) return callback(err);
if (hidden) {
self.show();
}
if (!focused) {
self.screen.saveFocus();
self.focus();
}
self.screen.render();
});
}n/a
refresh = function (cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
var self = this;
if (cwd) this.cwd = cwd;
else cwd = this.cwd;
return fs.readdir(cwd, function(err, list) {
if (err && err.code === 'ENOENT') {
self.cwd = cwd !== process.env.HOME
? process.env.HOME
: '/';
return self.refresh(callback);
}
if (err) {
if (callback) return callback(err);
return self.emit('error', err, cwd);
}
var dirs = []
, files = [];
list.unshift('..');
list.forEach(function(name) {
var f = path.resolve(cwd, name)
, stat;
try {
stat = fs.lstatSync(f);
} catch (e) {
;
}
if ((stat && stat.isDirectory()) || name === '..') {
dirs.push({
name: name,
text: '{light-blue-fg}' + name + '{/light-blue-fg}/',
dir: true
});
} else if (stat && stat.isSymbolicLink()) {
files.push({
name: name,
text: '{light-cyan-fg}' + name + '{/light-cyan-fg}@',
dir: false
});
} else {
files.push({
name: name,
text: name,
dir: false
});
}
});
dirs = helpers.asort(dirs);
files = helpers.asort(files);
list = dirs.concat(files).map(function(data) {
return data.text;
});
self.setItems(list);
self.select(0);
self.screen.render();
self.emit('refresh');
if (callback) callback();
});
}n/a
reset = function (cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
this.cwd = cwd || this.options.cwd;
this.refresh(callback);
}n/a
function Form(options) {
var self = this;
if (!(this instanceof Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}n/a
_refresh = function () {
// XXX Possibly remove this if statement and refresh on every focus.
// Also potentially only include *visible* focusable elements.
// This would remove the need to check for _selected.visible in previous()
// and next().
if (!this._children) {
var out = [];
this.children.forEach(function fn(el) {
if (el.keyable) out.push(el);
el.children.forEach(fn);
});
this._children = out;
}
}n/a
_visible = function () {
return !!this._children.filter(function(el) {
return el.visible;
}).length;
}n/a
cancel = function () {
this.emit('cancel');
}n/a
focusFirst = function () {
this.resetSelected();
this.focusNext();
}n/a
focusLast = function () {
this.resetSelected();
this.focusPrevious();
}n/a
focusNext = function () {
var next = this.next();
if (next) next.focus();
}n/a
focusPrevious = function () {
var previous = this.previous();
if (previous) previous.focus();
}n/a
next = function () {
this._refresh();
if (!this._visible()) return;
if (!this._selected) {
this._selected = this._children[0];
if (!this._selected.visible) return this.next();
if (this.screen.focused !== this._selected) return this._selected;
}
var i = this._children.indexOf(this._selected);
if (!~i || !this._children[i + 1]) {
this._selected = this._children[0];
if (!this._selected.visible) return this.next();
return this._selected;
}
this._selected = this._children[i + 1];
if (!this._selected.visible) return this.next();
return this._selected;
}n/a
previous = function () {
this._refresh();
if (!this._visible()) return;
if (!this._selected) {
this._selected = this._children[this._children.length - 1];
if (!this._selected.visible) return this.previous();
if (this.screen.focused !== this._selected) return this._selected;
}
var i = this._children.indexOf(this._selected);
if (!~i || !this._children[i - 1]) {
this._selected = this._children[this._children.length - 1];
if (!this._selected.visible) return this.previous();
return this._selected;
}
this._selected = this._children[i - 1];
if (!this._selected.visible) return this.previous();
return this._selected;
}n/a
reset = function () {
this.children.forEach(function fn(el) {
switch (el.type) {
case 'screen':
break;
case 'box':
break;
case 'text':
break;
case 'line':
break;
case 'scrollable-box':
break;
case 'list':
el.select(0);
return;
case 'form':
break;
case 'input':
break;
case 'textbox':
el.clearInput();
return;
case 'textarea':
el.clearInput();
return;
case 'button':
delete el.value;
break;
case 'progress-bar':
el.setProgress(0);
break;
case 'file-manager':
el.refresh(el.options.cwd);
return;
case 'checkbox':
el.uncheck();
return;
case 'radio-set':
break;
case 'radio-button':
el.uncheck();
return;
case 'prompt':
break;
case 'question':
break;
case 'message':
break;
case 'info':
break;
case 'loading':
break;
case 'list-bar':
//el.select(0);
break;
case 'dir-manager':
el.refresh(el.options.cwd);
return;
case 'terminal':
el.write('');
return;
case 'image':
//el.clearImage();
return;
}
el.children.forEach(fn);
});
this.emit('reset');
}n/a
resetSelected = function () {
this._selected = null;
}n/a
submit = function () {
var out = {};
this.children.forEach(function fn(el) {
if (el.value != null) {
var name = el.name || el.type;
if (Array.isArray(out[name])) {
out[name].push(el.value);
} else if (out[name]) {
out[name] = [out[name], el.value];
} else {
out[name] = el.value;
}
}
el.children.forEach(fn);
});
this.emit('submit', out);
return this.submission = out;
}n/a
function GpmClient(options) {
if (!(this instanceof GpmClient)) {
return new GpmClient(options);
}
EventEmitter.call(this);
var pid = process.pid;
// check tty for /dev/tty[n]
var path;
try {
path = fs.readlinkSync('/proc/' + pid + '/fd/0');
} catch (e) {
;
}
var tty = /tty[0-9]+$/.exec(path);
if (tty === null) {
// TODO: should also check for /dev/input/..
}
var vc;
if (tty) {
tty = tty[0];
vc = +/[0-9]+$/.exec(tty)[0];
}
var self = this;
if (tty) {
fs.stat(GPM_SOCKET, function(err, stat) {
if (err || !stat.isSocket()) {
return;
}
var conf = {
eventMask: 0xffff,
defaultMask: GPM_MOVE | GPM_HARD,
minMod: 0,
maxMod: 0xffff,
pid: pid,
vc: vc
};
var gpm = net.createConnection(GPM_SOCKET);
this.gpm = gpm;
gpm.on('connect', function() {
send_config(gpm, conf, function() {
conf.pid = 0;
conf.vc = GPM_REQ_NOPASTE;
//send_config(gpm, conf);
});
});
gpm.on('data', function(packet) {
var evnt = parseEvent(packet);
switch (evnt.type & 15) {
case GPM_MOVE:
if (evnt.dx || evnt.dy) {
self.emit('move', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
if (evnt.wdx || evnt.wdy) {
self.emit('mousewheel',
evnt.buttons, evnt.modifiers,
evnt.x, evnt.y, evnt.wdx, evnt.wdy);
}
break;
case GPM_DRAG:
if (evnt.dx || evnt.dy) {
self.emit('drag', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
if (evnt.wdx || evnt.wdy) {
self.emit('mousewheel',
evnt.buttons, evnt.modifiers,
evnt.x, evnt.y, evnt.wdx, evnt.wdy);
}
break;
case GPM_DOWN:
self.emit('btndown', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
if (evnt.type & GPM_DOUBLE) {
self.emit('dblclick', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
case GPM_UP:
self.emit('btnup', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
if (!(evnt.type & GPM_MFLAG)) {
self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
}
});
gpm.on('error', function() {
self.stop();
});
});
}
}n/a
ButtonName = function (btn) {
if (btn & 4) return 'left';
if (btn & 2) return 'middle';
if (btn & 1) return 'right';
return '';
}n/a
hasCtrlKey = function (mod) {
return (mod & 4) ? true : false;
}n/a
hasMetaKey = function (mod) {
return (mod & 8) ? true : false;
}n/a
hasShiftKey = function (mod) {
return (mod & 1) ? true : false;
}n/a
stop = function () {
if (this.gpm) {
this.gpm.end();
}
delete this.gpm;
}...
self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
}
});
gpm.on('error', function() {
self.stop();
});
});
}
}
GpmClient.prototype.__proto__ = EventEmitter.prototype;
...function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}n/a
asort = function (obj) {
return obj.sort(function(a, b) {
a = a.name.toLowerCase();
b = b.name.toLowerCase();
if (a[0] === '.' && b[0] === '.') {
a = a[1];
b = b[1];
} else {
a = a[0];
b = b[0];
}
return a > b ? 1 : (a < b ? -1 : 0);
});
}n/a
attrToBinary = function (style, element) {
return helpers.Element.prototype.sattr.call(element || {}, style);
}n/a
cleanTags = function (text) {
return helpers.stripTags(text).trim();
}n/a
dropUnicode = function (text) {
if (!text) return '';
return text
.replace(unicode.chars.all, '??')
.replace(unicode.chars.combining, '')
.replace(unicode.chars.surrogate, '?');
}n/a
escape = function (text) {
return text.replace(/[{}]/g, function(ch) {
return ch === '{' ? '{open}' : '{close}';
});
}...
- __merge(a, b)__ - Merge objects `a` and `b` into object `a`.
- __asort(obj)__ - Sort array alphabetically by `name` prop.
- __hsort(obj)__ - Sort array numerically by `index` prop.
- __findFile(start, target)__ - Find a file at `start` directory with name
`target`.
- __escape(text)__ - Escape content's tags to be passed into `el.setContent()`.
Example: `box.setContent('escaped tag: ' + blessed.escape('{bold}{/bold
}'));`
- __parseTags(text)__ - Parse tags into SGR escape codes.
- __generateTags(style, text)__ - Generate text tags based on `style` object.
- __attrToBinary(style, element)__ - Convert `style` attributes to binary
format.
- __stripTags(text)__ - Strip text of tags and SGR sequences.
- __cleanTags(text)__ - Strip text of tags, SGR escape code, and
leading/trailing whitespace.
...findFile = function (start, target) {
return (function read(dir) {
var files, file, stat, out;
if (dir === '/dev' || dir === '/sys'
|| dir === '/proc' || dir === '/net') {
return null;
}
try {
files = fs.readdirSync(dir);
} catch (e) {
files = [];
}
for (var i = 0; i < files.length; i++) {
file = files[i];
if (file === target) {
return (dir === '/' ? '' : dir) + '/' + file;
}
try {
stat = fs.lstatSync((dir === '/' ? '' : dir) + '/' + file);
} catch (e) {
stat = null;
}
if (stat && stat.isDirectory() && !stat.isSymbolicLink()) {
out = read((dir === '/' ? '' : dir) + '/' + file);
if (out) return out;
}
}
return null;
})(start);
}n/a
generateTags = function (style, text) {
var open = ''
, close = '';
Object.keys(style || {}).forEach(function(key) {
var val = style[key];
if (typeof val === 'string') {
val = val.replace(/^light(?!-)/, 'light-');
val = val.replace(/^bright(?!-)/, 'bright-');
open = '{' + val + '-' + key + '}' + open;
close += '{/' + val + '-' + key + '}';
} else {
if (val === true) {
open = '{' + key + '}' + open;
close += '{/' + key + '}';
}
}
});
if (text != null) {
return open + text + close;
}
return {
open: open,
close: close
};
}n/a
hsort = function (obj) {
return obj.sort(function(a, b) {
return b.index - a.index;
});
}n/a
merge = function (a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
return a;
}n/a
parseTags = function (text, screen) {
return helpers.Element.prototype._parseTags.call(
{ parseTags: true, screen: screen || helpers.Screen.global }, text);
}n/a
function sprintf(src) {
var params = Array.prototype.slice.call(arguments, 1)
, rule = /%([\-+# ]{1,4})?(\d+(?:\.\d+)?)?([doxXsc])/g
, i = 0;
return src.replace(rule, function(_, flag, width, type) {
var flags = (flag || '').split('')
, param = params[i] != null ? params[i] : ''
, initial = param
// , width = +width
, opt = {}
, pre = '';
i++;
switch (type) {
case 'd': // signed int
param = (+param).toString(10);
break;
case 'o': // unsigned octal
param = (+param).toString(8);
break;
case 'x': // unsigned hex int
param = (+param).toString(16);
break;
case 'X': // unsigned hex int uppercase
param = (+param).toString(16).toUppercase();
break;
case 's': // string
break;
case 'c': // char
param = isFinite(param)
? String.fromCharCode(param || 0200)
: '';
break;
}
flags.forEach(function(flag) {
switch (flag) {
// left-justify by width
case '-':
opt.left = true;
break;
// always precede numbers with their signs
case '+':
opt.signs = true;
break;
// used with o, x, X - value is preceded with 0, 0x, or 0X respectively.
// used with a, A, e, E, f, F, g, G - forces written output to contain
// a decimal point even if no more digits follow
case '#':
opt.hexpoint = true;
break;
// if no sign is going to be written, black space in front of the value
case ' ':
opt.space = true;
break;
}
});
width = +width.split('.')[0];
// Should this be for opt.left too?
// Example: %2.2X - turns 0 into 00
if (width && !opt.left) {
param = param + '';
while (param.length < width) {
param = '0' + param;
}
}
if (opt.signs) {
if (+initial >= 0) {
pre += '+';
}
}
if (opt.space) {
if (!opt.signs && +initial >= 0) {
pre += ' ';
}
}
if (opt.hexpoint) {
switch (type) {
case 'o': // unsigned octal
pre += '0';
break;
case 'x': // unsigned hex int
pre += '0x';
break;
case 'X': // unsigned hex int uppercase
pre += '0X';
break;
}
}
if (opt.left) {
if (width > (pre.length + param.length)) {
width -= pre.length + param.length;
pre = Array(width + 1).join(' ') + pre;
}
}
return pre + param;
});
}n/a
stripTags = function (text) {
if (!text) return '';
return text
.replace(/{(\/?)([\w\-,;!#]*)}/g, '')
.replace(/\x1b\[[\d;]*m/g, '');
}n/a
function tryRead(file) {
if (Array.isArray(file)) {
for (var i = 0; i < file.length; i++) {
var data = tryRead(file[i]);
if (data) return data;
}
return '';
}
if (!file) return '';
file = path.resolve.apply(path, arguments);
try {
return fs.readFileSync(file, 'utf8');
} catch (e) {
return '';
}
}n/a
function emitKeypressEvents(stream) {
if (stream._keypressDecoder) return;
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
stream._keypressDecoder = new StringDecoder('utf8');
function onData(b) {
if (listenerCount(stream, 'keypress') > 0) {
var r = stream._keypressDecoder.write(b);
if (r) emitKeys(stream, r);
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
stream.on('newListener', onNewListener);
}
}
function onNewListener(event) {
if (event === 'keypress') {
stream.on('data', onData);
stream.removeListener('newListener', onNewListener);
}
}
if (listenerCount(stream, 'keypress') > 0) {
stream.on('data', onData);
} else {
stream.on('newListener', onNewListener);
}
}n/a
function Layout(options) {
if (!(this instanceof Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}...
Here is an example of how to provide a renderer. Note that this is also the
default renderer if none is provided. This renderer will render each child as
though they were `display: inline-block;` in CSS, as if there were a
dynamically sized horizontal grid from left to right.
``` js
var layout = blessed.layout({
parent: screen,
top: 'center',
left: 'center',
width: '50%',
height: '50%',
border: 'line',
style: {
..._renderCoords = function () {
var coords = this._getCoords(true);
var children = this.children;
this.children = [];
this._render();
this.children = children;
return coords;
}n/a
getLast = function (i) {
while (this.children[--i]) {
var el = this.children[i];
if (this.isRendered(el)) return el;
}
}n/a
getLastCoords = function (i) {
var last = this.getLast(i);
if (last) return last.lpos;
}...
return function iterator(el, i) {
// Make our children shrinkable. If they don't have a height, for
// example, calculate it for them.
el.shrink = true;
// Find the previous rendered child's coordinates
var last = self.getLastCoords(i);
// If there is no previously rendered element, we are on the first child.
if (!last) {
el.position.left = 0;
el.position.top = 0;
} else {
// Otherwise, figure out where to place this child. We'll start by
...isRendered = function (el) {
if (!el.lpos) return false;
return (el.lpos.xl - el.lpos.xi) > 0
&& (el.lpos.yl - el.lpos.yi) > 0;
}...
if (el.position.left + el.width <= width) {
el.position.top = rowOffset;
} else {
// Otherwise we need to start a new row and calculate a new
// `rowOffset` and `rowIndex` (the index of the child on the current
// row).
rowOffset += self.children.slice(rowIndex, i).reduce(function(out, el) {
if (!self.isRendered(el)) return out;
out = Math.max(out, el.lpos.yl - el.lpos.yi);
return out;
}, 0);
rowIndex = i;
el.position.left = 0;
el.position.top = rowOffset;
}
...render = function () {
this._emit('prerender');
var coords = this._renderCoords();
if (!coords) {
delete this.lpos;
return;
}
if (coords.xl - coords.xi <= 0) {
coords.xl = Math.max(coords.xl, coords.xi);
return;
}
if (coords.yl - coords.yi <= 0) {
coords.yl = Math.max(coords.yl, coords.yi);
return;
}
this.lpos = coords;
if (this.border) coords.xi++, coords.xl--, coords.yi++, coords.yl--;
if (this.tpadding) {
coords.xi += this.padding.left, coords.xl -= this.padding.right;
coords.yi += this.padding.top, coords.yl -= this.padding.bottom;
}
var iterator = this.renderer(coords);
if (this.border) coords.xi--, coords.xl++, coords.yi--, coords.yl++;
if (this.tpadding) {
coords.xi -= this.padding.left, coords.xl += this.padding.right;
coords.yi -= this.padding.top, coords.yl += this.padding.bottom;
}
this.children.forEach(function(el, i) {
if (el.screen._ci !== -1) {
el.index = el.screen._ci++;
}
var rendered = iterator(el, i);
if (rendered === false) {
delete el.lpos;
return;
}
// if (el.screen._rendering) {
// el._rendering = true;
// }
el.render();
// if (el.screen._rendering) {
// el._rendering = false;
// }
});
this._emit('render', [coords]);
return coords;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...renderer = function (coords) {
var self = this;
// The coordinates of the layout element
var width = coords.xl - coords.xi
, height = coords.yl - coords.yi
, xi = coords.xi
, yi = coords.yi;
// The current row offset in cells (which row are we on?)
var rowOffset = 0;
// The index of the first child in the row
var rowIndex = 0;
var lastRowIndex = 0;
// Figure out the highest width child
if (this.options.layout === 'grid') {
var highWidth = this.children.reduce(function(out, el) {
out = Math.max(out, el.width);
return out;
}, 0);
}
return function iterator(el, i) {
// Make our children shrinkable. If they don't have a height, for
// example, calculate it for them.
el.shrink = true;
// Find the previous rendered child's coordinates
var last = self.getLast(i);
// If there is no previously rendered element, we are on the first child.
if (!last) {
el.position.left = 0;
el.position.top = 0;
} else {
// Otherwise, figure out where to place this child. We'll start by
// setting it's `left`/`x` coordinate to right after the previous
// rendered element. This child will end up directly to the right of it.
el.position.left = last.lpos.xl - xi;
// Make sure the position matches the highest width element
if (self.options.layout === 'grid') {
// Compensate with width:
// el.position.width = el.width + (highWidth - el.width);
// Compensate with position:
el.position.left += highWidth - (last.lpos.xl - last.lpos.xi);
}
// If our child does not overlap the right side of the Layout, set it's
// `top`/`y` to the current `rowOffset` (the coordinate for the current
// row).
if (el.position.left + el.width <= width) {
el.position.top = rowOffset;
} else {
// Otherwise we need to start a new row and calculate a new
// `rowOffset` and `rowIndex` (the index of the child on the current
// row).
rowOffset += self.children.slice(rowIndex, i).reduce(function(out, el) {
if (!self.isRendered(el)) return out;
out = Math.max(out, el.lpos.yl - el.lpos.yi);
return out;
}, 0);
lastRowIndex = rowIndex;
rowIndex = i;
el.position.left = 0;
el.position.top = rowOffset;
}
}
// Make sure the elements on lower rows graviatate up as much as possible
if (self.options.layout === 'inline') {
var above = null;
var abovea = Infinity;
for (var j = lastRowIndex; j < rowIndex; j++) {
var l = self.children[j];
if (!self.isRendered(l)) continue;
var abs = Math.abs(el.position.left - (l.lpos.xi - xi));
// if (abs < abovea && (l.lpos.xl - l.lpos.xi) <= el.width) {
if (abs < abovea) {
above = l;
abovea = abs;
}
}
if (above) {
el.position.top = above.lpos.yl - yi;
}
}
// If our child overflows the Layout, do not render it!
// Disable this feature for now.
if (el.position.top + el.height > height) {
// Returning false tells blessed to ignore this child.
// return false;
}
};
}n/a
function List(options) {
var self = this;
if (!(this instanceof Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(sel ...n/a
add = function (content) {
content = typeof content === 'string' ? content : content.getContent();
var item = this.createItem(content);
item.position.top = this.items.length;
if (!this.screen.autoPadding) {
item.position.top = this.itop + this.items.length;
}
this.ritems.push(content);
this.items.push(item);
this.append(item);
if (this.items.length === 1) {
this.select(0);
}
this.emit('add item');
return item;
}n/a
addItem = function (content) {
content = typeof content === 'string' ? content : content.getContent();
var item = this.createItem(content);
item.position.top = this.items.length;
if (!this.screen.autoPadding) {
item.position.top = this.itop + this.items.length;
}
this.ritems.push(content);
this.items.push(item);
this.append(item);
if (this.items.length === 1) {
this.select(0);
}
this.emit('add item');
return item;
}n/a
appendItem = function (content) {
content = typeof content === 'string' ? content : content.getContent();
var item = this.createItem(content);
item.position.top = this.items.length;
if (!this.screen.autoPadding) {
item.position.top = this.itop + this.items.length;
}
this.ritems.push(content);
this.items.push(item);
this.append(item);
if (this.items.length === 1) {
this.select(0);
}
this.emit('add item');
return item;
}n/a
cancelSelected = function (i) {
if (i != null) this.select(i);
this.emit('action');
this.emit('cancel');
}n/a
clearItems = function () {
return this.setItems([]);
}n/a
createItem = function (content) {
var self = this;
// Note: Could potentially use Button here.
var options = {
screen: this.screen,
content: content,
align: this.align || 'left',
top: 0,
left: 0,
right: (this.scrollbar ? 1 : 0),
tags: this.parseTags,
height: 1,
hoverEffects: this.mouse ? this.style.item.hover : null,
focusEffects: this.mouse ? this.style.item.focus : null,
autoFocus: false
};
if (!this.screen.autoPadding) {
options.top = 1;
options.left = this.ileft;
options.right = this.iright + (this.scrollbar ? 1 : 0);
}
// if (this.shrink) {
// XXX NOTE: Maybe just do this on all shrinkage once autoPadding is default?
if (this.shrink && this.options.normalShrink) {
delete options.right;
options.width = 'shrink';
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options[name] = function() {
var attr = self.items[self.selected] === item && self.interactive
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(item);
return attr;
};
});
if (this.style.transparent) {
options.transparent = true;
}
var item = new Box(options);
if (this.mouse) {
item.on('click', function() {
self.focus();
if (self.items[self.selected] === item) {
self.emit('action', item, self.selected);
self.emit('select', item, self.selected);
return;
}
self.select(item);
self.screen.render();
});
}
this.emit('create item');
return item;
}n/a
down = function (offset) {
this.move(offset || 1);
}...
program.hideCursor();
program.clear();
program.move(1, 1);
program.bg('black');
program.write('Hello world', 'blue fg');
program.setx((program.cols / 2 | 0) - 4);
program.down(5);
program.write('Hi again!');
program.bg('!black');
program.feed();
```
#### Testing
...enterSelected = function (i) {
if (i != null) this.select(i);
this.emit('action', this.items[this.selected], this.selected);
this.emit('select', this.items[this.selected], this.selected);
}n/a
find = function (search, back) {
var start = this.selected + (back ? -1 : 1)
, i;
if (typeof search === 'number') search += '';
if (search && search[0] === '/' && search[search.length - 1] === '/') {
try {
search = new RegExp(search.slice(1, -1));
} catch (e) {
;
}
}
var test = typeof search === 'string'
? function(item) { return !!~item.indexOf(search); }
: (search.test ? search.test.bind(search) : search);
if (typeof test !== 'function') {
if (this.screen.options.debug) {
throw new Error('fuzzyFind(): `test` is not a function.');
}
return this.selected;
}
if (!back) {
for (i = start; i < this.ritems.length; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = 0; i < start; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
} else {
for (i = start; i >= 0; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = this.ritems.length - 1; i > start; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
}
return this.selected;
}n/a
fuzzyFind = function (search, back) {
var start = this.selected + (back ? -1 : 1)
, i;
if (typeof search === 'number') search += '';
if (search && search[0] === '/' && search[search.length - 1] === '/') {
try {
search = new RegExp(search.slice(1, -1));
} catch (e) {
;
}
}
var test = typeof search === 'string'
? function(item) { return !!~item.indexOf(search); }
: (search.test ? search.test.bind(search) : search);
if (typeof test !== 'function') {
if (this.screen.options.debug) {
throw new Error('fuzzyFind(): `test` is not a function.');
}
return this.selected;
}
if (!back) {
for (i = start; i < this.ritems.length; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = 0; i < start; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
} else {
for (i = start; i >= 0; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = this.ritems.length - 1; i > start; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
}
return this.selected;
}n/a
getItem = function (child) {
return this.items[this.getItemIndex(child)];
}n/a
getItemIndex = function (child) {
if (typeof child === 'number') {
return child;
} else if (typeof child === 'string') {
var i = this.ritems.indexOf(child);
if (~i) return i;
for (i = 0; i < this.ritems.length; i++) {
if (helpers.cleanTags(this.ritems[i]) === child) {
return i;
}
}
return -1;
} else {
return this.items.indexOf(child);
}
}n/a
insertItem = function (child, content) {
content = typeof content === 'string' ? content : content.getContent();
var i = this.getItemIndex(child);
if (!~i) return;
if (i >= this.items.length) return this.appendItem(content);
var item = this.createItem(content);
for (var j = i; j < this.items.length; j++) {
this.items[j].position.top++;
}
item.position.top = i + (!this.screen.autoPadding ? 1 : 0);
this.ritems.splice(i, 0, content);
this.items.splice(i, 0, item);
this.append(item);
if (i === this.selected) {
this.select(i + 1);
}
this.emit('insert item');
}n/a
move = function (offset) {
this.select(this.selected + offset);
}...
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
program.bg('red');
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
...pick = function (label, callback) {
if (!callback) {
callback = label;
label = null;
}
if (!this.interactive) {
return callback();
}
var self = this;
var focused = this.screen.focused;
if (focused && focused._done) focused._done('stop');
this.screen.saveFocus();
// XXX Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.focus();
this.show();
this.select(0);
if (label) this.setLabel(label);
this.screen.render();
this.once('action', function(el, selected) {
if (label) self.removeLabel();
self.screen.restoreFocus();
self.hide();
self.screen.render();
if (!el) return callback();
return callback(null, helpers.cleanTags(self.ritems[selected]));
});
}n/a
popItem = function () {
return this.removeItem(this.items.length - 1);
}n/a
pushItem = function (content) {
this.appendItem(content);
return this.items.length;
}n/a
removeItem = function (child) {
var i = this.getItemIndex(child);
if (~i && this.items[i]) {
child = this.items.splice(i, 1)[0];
this.ritems.splice(i, 1);
this.remove(child);
for (var j = i; j < this.items.length; j++) {
this.items[j].position.top--;
}
if (i === this.selected) {
this.select(i - 1);
}
}
this.emit('remove item');
return child;
}n/a
select = function (index) {
if (!this.interactive) {
return;
}
if (!this.items.length) {
this.selected = 0;
this.value = '';
this.scrollTo(0);
return;
}
if (typeof index === 'object') {
index = this.items.indexOf(index);
}
if (index < 0) {
index = 0;
} else if (index >= this.items.length) {
index = this.items.length - 1;
}
if (this.selected === index && this._listInitialized) return;
this._listInitialized = true;
this.selected = index;
this.value = helpers.cleanTags(this.ritems[this.selected]);
if (!this.parent) return;
this.scrollTo(this.selected);
// XXX Move `action` and `select` events here.
this.emit('select item', this.items[this.selected], this.selected);
}n/a
setItem = function (child, content) {
content = typeof content === 'string' ? content : content.getContent();
var i = this.getItemIndex(child);
if (!~i) return;
this.items[i].setContent(content);
this.ritems[i] = content;
}n/a
setItems = function (items) {
var original = this.items.slice()
, selected = this.selected
, sel = this.ritems[this.selected]
, i = 0;
items = items.slice();
this.select(0);
for (; i < items.length; i++) {
if (this.items[i]) {
this.items[i].setContent(items[i]);
} else {
this.add(items[i]);
}
}
for (; i < original.length; i++) {
this.remove(original[i]);
}
this.ritems = items;
// Try to find our old item if it still exists.
sel = items.indexOf(sel);
if (~sel) {
this.select(sel);
} else if (items.length === original.length) {
this.select(selected);
} else {
this.select(Math.min(selected, items.length - 1));
}
this.emit('set items');
}n/a
shiftItem = function () {
return this.removeItem(0);
}n/a
spliceItem = function (child, n) {
var self = this;
var i = this.getItemIndex(child);
if (!~i) return;
var items = Array.prototype.slice.call(arguments, 2);
var removed = [];
while (n--) {
removed.push(this.removeItem(i));
}
items.forEach(function(item) {
self.insertItem(i++, item);
});
return removed;
}n/a
unshiftItem = function (content) {
this.insertItem(0, content);
return this.items.length;
}n/a
up = function (offset) {
this.move(-(offset || 1));
}n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
add = function (item, callback) {
var self = this
, prev = this.items[this.items.length - 1]
, drawn
, cmd
, title
, len;
if (!this.parent) {
drawn = 0;
} else {
drawn = prev ? prev.aleft + prev.width : 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
}
if (typeof item === 'object') {
cmd = item;
if (cmd.prefix == null) cmd.prefix = (this.items.length + 1) + '';
}
if (typeof item === 'string') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item,
callback: callback
};
}
if (typeof item === 'function') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item.name,
callback: item
};
}
if (cmd.keys && cmd.keys[0]) {
cmd.prefix = cmd.keys[0];
}
var t = helpers.generateTags(this.style.prefix || { fg: 'lightblack' });
title = (cmd.prefix != null ? t.open + cmd.prefix + t.close + ':' : '') + cmd.text;
len = ((cmd.prefix != null ? cmd.prefix + ':' : '') + cmd.text).length;
var options = {
screen: this.screen,
top: 0,
left: drawn + 1,
height: 1,
content: title,
width: len + 2,
align: 'center',
autoFocus: false,
tags: true,
mouse: true,
style: helpers.merge({}, this.style.item),
noOverflow: true
};
if (!this.screen.autoPadding) {
options.top += this.itop;
options.left += this.ileft;
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options.style[name] = function() {
var attr = self.items[self.selected] === el
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(el);
return attr;
};
});
var el = new Box(options);
this._[cmd.text] = el;
cmd.element = el;
el._.cmd = cmd;
this.ritems.push(cmd.text);
this.items.push(el);
this.commands.push(cmd);
this.append(el);
if (cmd.callback) {
if (cmd.keys) {
this.screen.key(cmd.keys, function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
}
if (this.items.length === 1) {
this.select(0);
}
// XXX May be affected by new element.options.mouse option.
if (this.mouse) {
el.on('click', function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
this.emit('add item');
}n/a
addItem = function (item, callback) {
var self = this
, prev = this.items[this.items.length - 1]
, drawn
, cmd
, title
, len;
if (!this.parent) {
drawn = 0;
} else {
drawn = prev ? prev.aleft + prev.width : 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
}
if (typeof item === 'object') {
cmd = item;
if (cmd.prefix == null) cmd.prefix = (this.items.length + 1) + '';
}
if (typeof item === 'string') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item,
callback: callback
};
}
if (typeof item === 'function') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item.name,
callback: item
};
}
if (cmd.keys && cmd.keys[0]) {
cmd.prefix = cmd.keys[0];
}
var t = helpers.generateTags(this.style.prefix || { fg: 'lightblack' });
title = (cmd.prefix != null ? t.open + cmd.prefix + t.close + ':' : '') + cmd.text;
len = ((cmd.prefix != null ? cmd.prefix + ':' : '') + cmd.text).length;
var options = {
screen: this.screen,
top: 0,
left: drawn + 1,
height: 1,
content: title,
width: len + 2,
align: 'center',
autoFocus: false,
tags: true,
mouse: true,
style: helpers.merge({}, this.style.item),
noOverflow: true
};
if (!this.screen.autoPadding) {
options.top += this.itop;
options.left += this.ileft;
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options.style[name] = function() {
var attr = self.items[self.selected] === el
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(el);
return attr;
};
});
var el = new Box(options);
this._[cmd.text] = el;
cmd.element = el;
el._.cmd = cmd;
this.ritems.push(cmd.text);
this.items.push(el);
this.commands.push(cmd);
this.append(el);
if (cmd.callback) {
if (cmd.keys) {
this.screen.key(cmd.keys, function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
}
if (this.items.length === 1) {
this.select(0);
}
// XXX May be affected by new element.options.mouse option.
if (this.mouse) {
el.on('click', function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
this.emit('add item');
}n/a
appendItem = function (item, callback) {
var self = this
, prev = this.items[this.items.length - 1]
, drawn
, cmd
, title
, len;
if (!this.parent) {
drawn = 0;
} else {
drawn = prev ? prev.aleft + prev.width : 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
}
if (typeof item === 'object') {
cmd = item;
if (cmd.prefix == null) cmd.prefix = (this.items.length + 1) + '';
}
if (typeof item === 'string') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item,
callback: callback
};
}
if (typeof item === 'function') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item.name,
callback: item
};
}
if (cmd.keys && cmd.keys[0]) {
cmd.prefix = cmd.keys[0];
}
var t = helpers.generateTags(this.style.prefix || { fg: 'lightblack' });
title = (cmd.prefix != null ? t.open + cmd.prefix + t.close + ':' : '') + cmd.text;
len = ((cmd.prefix != null ? cmd.prefix + ':' : '') + cmd.text).length;
var options = {
screen: this.screen,
top: 0,
left: drawn + 1,
height: 1,
content: title,
width: len + 2,
align: 'center',
autoFocus: false,
tags: true,
mouse: true,
style: helpers.merge({}, this.style.item),
noOverflow: true
};
if (!this.screen.autoPadding) {
options.top += this.itop;
options.left += this.ileft;
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options.style[name] = function() {
var attr = self.items[self.selected] === el
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(el);
return attr;
};
});
var el = new Box(options);
this._[cmd.text] = el;
cmd.element = el;
el._.cmd = cmd;
this.ritems.push(cmd.text);
this.items.push(el);
this.commands.push(cmd);
this.append(el);
if (cmd.callback) {
if (cmd.keys) {
this.screen.key(cmd.keys, function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
}
if (this.items.length === 1) {
this.select(0);
}
// XXX May be affected by new element.options.mouse option.
if (this.mouse) {
el.on('click', function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
this.emit('add item');
}n/a
move = function (offset) {
this.select(this.selected + offset);
}...
program.showCursor();
program.normalBuffer();
process.exit(0);
});
program.on('mouse', function(data) {
if (data.action === 'mousemove') {
program.move(data.x, data.y);
program.bg('red');
program.write('x');
program.bg('!red');
}
});
program.alternateBuffer();
...moveLeft = function (offset) {
this.move(-(offset || 1));
}n/a
moveRight = function (offset) {
this.move(offset || 1);
}n/a
removeItem = function (child) {
var i = typeof child !== 'number'
? this.items.indexOf(child)
: child;
if (~i && this.items[i]) {
child = this.items.splice(i, 1)[0];
this.ritems.splice(i, 1);
this.commands.splice(i, 1);
this.remove(child);
if (i === this.selected) {
this.select(i - 1);
}
}
this.emit('remove item');
}n/a
render = function () {
var self = this
, drawn = 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
this.items.forEach(function(el, i) {
if (i < self.leftBase) {
el.hide();
} else {
el.rleft = drawn + 1;
drawn += el.width + 2;
el.show();
}
});
return this._render();
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...select = function (offset) {
if (typeof offset !== 'number') {
offset = this.items.indexOf(offset);
}
if (offset < 0) {
offset = 0;
} else if (offset >= this.items.length) {
offset = this.items.length - 1;
}
if (!this.parent) {
this.emit('select item', this.items[offset], offset);
return;
}
var lpos = this._getCoords();
if (!lpos) return;
var self = this
, width = (lpos.xl - lpos.xi) - this.iwidth
, drawn = 0
, visible = 0
, el;
el = this.items[offset];
if (!el) return;
this.items.forEach(function(el, i) {
if (i < self.leftBase) return;
var lpos = el._getCoords();
if (!lpos) return;
if (lpos.xl - lpos.xi <= 0) return;
drawn += (lpos.xl - lpos.xi) + 2;
if (drawn <= width) visible++;
});
var diff = offset - (this.leftBase + this.leftOffset);
if (offset > this.leftBase + this.leftOffset) {
if (offset > this.leftBase + visible - 1) {
this.leftOffset = 0;
this.leftBase = offset;
} else {
this.leftOffset += diff;
}
} else if (offset < this.leftBase + this.leftOffset) {
diff = -diff;
if (offset < this.leftBase) {
this.leftOffset = 0;
this.leftBase = offset;
} else {
this.leftOffset -= diff;
}
}
// XXX Move `action` and `select` events here.
this.emit('select item', el, offset);
}n/a
selectTab = function (index) {
var item = this.items[index];
if (item) {
if (item._.cmd.callback) {
item._.cmd.callback();
}
this.select(index);
this.screen.render();
}
this.emit('select tab', item, index);
}n/a
setItems = function (commands) {
var self = this;
if (!Array.isArray(commands)) {
commands = Object.keys(commands).reduce(function(obj, key, i) {
var cmd = commands[key]
, cb;
if (typeof cmd === 'function') {
cb = cmd;
cmd = { callback: cb };
}
if (cmd.text == null) cmd.text = key;
if (cmd.prefix == null) cmd.prefix = ++i + '';
if (cmd.text == null && cmd.callback) {
cmd.text = cmd.callback.name;
}
obj.push(cmd);
return obj;
}, []);
}
this.items.forEach(function(el) {
el.detach();
});
this.items = [];
this.ritems = [];
this.commands = [];
commands.forEach(function(cmd) {
self.add(cmd);
});
this.emit('set items');
}n/a
function ListTable(options) {
var self = this;
if (!(this instanceof Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}n/a
_calculateMaxes = function () {
var self = this;
var maxes = [];
if (this.detached) return;
this.rows = this.rows || [];
this.rows.forEach(function(row) {
row.forEach(function(cell, i) {
var clen = self.strWidth(cell);
if (!maxes[i] || maxes[i] < clen) {
maxes[i] = clen;
}
});
});
var total = maxes.reduce(function(total, max) {
return total + max;
}, 0);
total += maxes.length + 1;
// XXX There might be an issue with resizing where on the first resize event
// width appears to be less than total if it's a percentage or left/right
// combination.
if (this.width < total) {
delete this.position.width;
}
if (this.position.width != null) {
var missing = this.width - total;
var w = missing / maxes.length | 0;
var wr = missing % maxes.length;
maxes = maxes.map(function(max, i) {
if (i === maxes.length - 1) {
return max + w + wr;
}
return max + w;
});
} else {
maxes = maxes.map(function(max) {
return max + self.pad;
});
}
return this._maxes = maxes;
}n/a
_select = function (index) {
if (!this.interactive) {
return;
}
if (!this.items.length) {
this.selected = 0;
this.value = '';
this.scrollTo(0);
return;
}
if (typeof index === 'object') {
index = this.items.indexOf(index);
}
if (index < 0) {
index = 0;
} else if (index >= this.items.length) {
index = this.items.length - 1;
}
if (this.selected === index && this._listInitialized) return;
this._listInitialized = true;
this.selected = index;
this.value = helpers.cleanTags(this.ritems[this.selected]);
if (!this.parent) return;
this.scrollTo(this.selected);
// XXX Move `action` and `select` events here.
this.emit('select item', this.items[this.selected], this.selected);
}n/a
render = function () {
var self = this;
var coords = this._render();
if (!coords) return;
this._calculateMaxes();
if (!this._maxes) return coords;
var lines = this.screen.lines
, xi = coords.xi
, yi = coords.yi
, rx
, ry
, i;
var battr = this.sattr(this.style.border);
var height = coords.yl - coords.yi - this.ibottom;
if (!this.border || this.options.noCellBorders) return coords;
// Draw border with correct angles.
ry = 0;
for (i = 0; i < height + 1; i++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.slice(0, -1).forEach(function(max) {
rx += max;
if (!lines[yi + ry][xi + rx + 1]) return;
// center
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u252c'; // '┬'
// XXX If we alter iheight and itop for no borders - nothing should be written here
if (!self.border.top) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
lines[yi + ry].dirty = true;
} else if (ry === height) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2534'; // '┴'
// XXX If we alter iheight and ibottom for no borders - nothing should be written here
if (!self.border.bottom) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
lines[yi + ry].dirty = true;
} else {
// middle
rx++;
}
});
ry += 1;
}
// Draw internal borders.
for (ry = 1; ry < height; ry++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.slice(0, -1).forEach(function(max) {
rx += max;
if (!lines[yi + ry][xi + rx + 1]) return;
if (self.options.fillCellBorders !== false) {
var lbg = lines[yi + ry][xi + rx][0] & 0x1ff;
rx++;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
rx++;
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
lines[yi + ry].dirty = true;
});
}
return coords;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...select = function (i) {
if (i === 0) {
i = 1;
}
if (i <= this.childBase) {
this.setScroll(this.childBase - 1);
}
return this._select(i);
}n/a
setData = function (rows) {
var self = this
, align = this.__align;
if (this.visible && this.lpos) {
this.clearPos();
}
this.clearItems();
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.addItem('');
this.rows.forEach(function(row, i) {
var isHeader = i === 0;
var text = '';
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (isHeader) {
self._header.setContent(text);
} else {
self.addItem(text);
}
});
this._header.setFront();
this.select(0);
}...
- Inherits all from List.
##### Methods:
- Inherits all from List.
- __setRows/setData(rows)__ - Set rows in table. Array of arrays of strings.
``` js
table.setData([
[ 'Animals', 'Foods' ],
[ 'Elephant', 'Apple' ],
[ 'Bird', 'Orange' ]
]);
```
...setRows = function (rows) {
var self = this
, align = this.__align;
if (this.visible && this.lpos) {
this.clearPos();
}
this.clearItems();
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.addItem('');
this.rows.forEach(function(row, i) {
var isHeader = i === 0;
var text = '';
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (isHeader) {
self._header.setContent(text);
} else {
self.addItem(text);
}
});
this._header.setFront();
this.select(0);
}n/a
function Loading(options) {
if (!(this instanceof Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}n/a
load = function (text) {
var self = this;
// XXX Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(text);
if (this._.timer) {
this.stop();
}
this.screen.lockKeys = true;
this._.timer = setInterval(function() {
if (self._.icon.content === '|') {
self._.icon.setContent('/');
} else if (self._.icon.content === '/') {
self._.icon.setContent('-');
} else if (self._.icon.content === '-') {
self._.icon.setContent('\\');
} else if (self._.icon.content === '\\') {
self._.icon.setContent('|');
}
self.screen.render();
}, 200);
}n/a
stop = function () {
this.screen.lockKeys = false;
this.hide();
if (this._.timer) {
clearInterval(this._.timer);
delete this._.timer;
}
this.screen.render();
}...
self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
}
break;
}
});
gpm.on('error', function() {
self.stop();
});
});
}
}
GpmClient.prototype.__proto__ = EventEmitter.prototype;
...function Log(options) {
var self = this;
if (!(this instanceof Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this.on('set content', function() {
if (!self._userScrolled || self.scrollOnInput) {
nextTick(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self.screen.render();
});
}
});
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
..._scroll = function (offset, always) {
if (!this.scrollable) return;
if (this.detached) return;
// Handle scrolling.
var visible = this.height - this.iheight
, base = this.childBase
, d
, p
, t
, b
, max
, emax;
if (this.alwaysScroll || always) {
// Semi-workaround
this.childOffset = offset > 0
? visible - 1 + offset
: offset;
} else {
this.childOffset += offset;
}
if (this.childOffset > visible - 1) {
d = this.childOffset - (visible - 1);
this.childOffset -= d;
this.childBase += d;
} else if (this.childOffset < 0) {
d = this.childOffset;
this.childOffset += -d;
this.childBase += d;
}
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Find max "bottom" value for
// content and descendant elements.
// Scroll the content if necessary.
if (this.childBase === base) {
return this.emit('scroll');
}
// When scrolling text, we want to be able to handle SGR codes as well as line
// feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box.
this.parseContent();
// XXX
// max = this.getScrollHeight() - (this.height - this.iheight);
max = this._clines.length - (this.height - this.iheight);
if (max < 0) max = 0;
emax = this._scrollBottom() - (this.height - this.iheight);
if (emax < 0) emax = 0;
this.childBase = Math.min(this.childBase, Math.max(emax, max));
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Optimize scrolling with CSR + IL/DL.
p = this.lpos;
// Only really need _getCoords() if we want
// to allow nestable scrolling elements...
// or if we **really** want shrinkable
// scrolling elements.
// p = this._getCoords();
if (p && this.childBase !== base && this.screen.cleanSides(this)) {
t = p.yi + this.itop;
b = p.yl - this.ibottom - 1;
d = this.childBase - base;
if (d > 0 && d < visible) {
// scrolled down
this.screen.deleteLine(d, t, t, b);
} else if (d < 0 && -d < visible) {
// scrolled up
d = -d;
this.screen.insertLine(d, t, t, b);
}
}
return this.emit('scroll');
}n/a
add = function () {
var args = Array.prototype.slice.call(arguments);
if (typeof args[0] === 'object') {
args[0] = util.inspect(args[0], true, 20, true);
}
var text = util.format.apply(util, args);
this.emit('log', text);
var ret = this.pushLine(text);
if (this._clines.fake.length > this.scrollback) {
this.shiftLine(0, (this.scrollback / 3) | 0);
}
return ret;
}n/a
log = function () {
var args = Array.prototype.slice.call(arguments);
if (typeof args[0] === 'object') {
args[0] = util.inspect(args[0], true, 20, true);
}
var text = util.format.apply(util, args);
this.emit('log', text);
var ret = this.pushLine(text);
if (this._clines.fake.length > this.scrollback) {
this.shiftLine(0, (this.scrollback / 3) | 0);
}
return ret;
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...scroll = function (offset, always) {
if (offset === 0) return this._scroll(offset, always);
this._userScrolled = true;
var ret = this._scroll(offset, always);
if (this.getScrollPerc() === 100) {
this._userScrolled = false;
}
return ret;
}n/a
function Message(options) {
if (!(this instanceof Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}n/a
display = function (text, time, callback) {
var self = this;
if (typeof time === 'function') {
callback = time;
time = null;
}
if (time == null) time = 3;
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
if (this.scrollable) {
this.screen.saveFocus();
this.focus();
this.scrollTo(0);
}
this.show();
this.setContent(text);
this.screen.render();
if (time === Infinity || time === -1 || time === 0) {
var end = function() {
if (end.done) return;
end.done = true;
if (self.scrollable) {
try {
self.screen.restoreFocus();
} catch (e) {
;
}
}
self.hide();
self.screen.render();
if (callback) callback();
};
setTimeout(function() {
self.onScreenEvent('keypress', function fn(ch, key) {
if (key.name === 'mouse') return;
if (self.scrollable) {
if ((key.name === 'up' || (self.options.vi && key.name === 'k'))
|| (key.name === 'down' || (self.options.vi && key.name === 'j'))
|| (self.options.vi && key.name === 'u' && key.ctrl)
|| (self.options.vi && key.name === 'd' && key.ctrl)
|| (self.options.vi && key.name === 'b' && key.ctrl)
|| (self.options.vi && key.name === 'f' && key.ctrl)
|| (self.options.vi && key.name === 'g' && !key.shift)
|| (self.options.vi && key.name === 'g' && key.shift)) {
return;
}
}
if (self.options.ignoreKeys && ~self.options.ignoreKeys.indexOf(key.name)) {
return;
}
self.removeScreenEvent('keypress', fn);
end();
});
// XXX May be affected by new element.options.mouse option.
if (!self.options.mouse) return;
self.onScreenEvent('mouse', function fn(data) {
if (data.action === 'mousemove') return;
self.removeScreenEvent('mouse', fn);
end();
});
}, 10);
return;
}
setTimeout(function() {
self.hide();
self.screen.render();
if (callback) callback();
}, time * 1000);
}n/a
error = function (text, time, callback) {
return this.display('{red-fg}Error: ' + text + '{/red-fg}', time, callback);
}n/a
log = function (text, time, callback) {
var self = this;
if (typeof time === 'function') {
callback = time;
time = null;
}
if (time == null) time = 3;
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
if (this.scrollable) {
this.screen.saveFocus();
this.focus();
this.scrollTo(0);
}
this.show();
this.setContent(text);
this.screen.render();
if (time === Infinity || time === -1 || time === 0) {
var end = function() {
if (end.done) return;
end.done = true;
if (self.scrollable) {
try {
self.screen.restoreFocus();
} catch (e) {
;
}
}
self.hide();
self.screen.render();
if (callback) callback();
};
setTimeout(function() {
self.onScreenEvent('keypress', function fn(ch, key) {
if (key.name === 'mouse') return;
if (self.scrollable) {
if ((key.name === 'up' || (self.options.vi && key.name === 'k'))
|| (key.name === 'down' || (self.options.vi && key.name === 'j'))
|| (self.options.vi && key.name === 'u' && key.ctrl)
|| (self.options.vi && key.name === 'd' && key.ctrl)
|| (self.options.vi && key.name === 'b' && key.ctrl)
|| (self.options.vi && key.name === 'f' && key.ctrl)
|| (self.options.vi && key.name === 'g' && !key.shift)
|| (self.options.vi && key.name === 'g' && key.shift)) {
return;
}
}
if (self.options.ignoreKeys && ~self.options.ignoreKeys.indexOf(key.name)) {
return;
}
self.removeScreenEvent('keypress', fn);
end();
});
// XXX May be affected by new element.options.mouse option.
if (!self.options.mouse) return;
self.onScreenEvent('mouse', function fn(data) {
if (data.action === 'mousemove') return;
self.removeScreenEvent('mouse', fn);
end();
});
}, 10);
return;
}
setTimeout(function() {
self.hide();
self.screen.render();
if (callback) callback();
}, time * 1000);
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...function Node(options) {
var self = this;
var Screen = require('./screen');
if (!(this instanceof Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}n/a
append = function (element) {
this.insert(element, this.children.length);
}...
hover: {
bg: 'green'
}
}
});
// Append our box to the screen.
screen.append(box);
// Add a png icon to the box
var icon = blessed.image({
parent: box,
top: 0,
left: 0,
type: 'overlay',
...collectAncestors = function (s) {
var out = [];
this.forAncestors(function(el) {
out.push(el);
}, s);
return out;
}n/a
collectDescendants = function (s) {
var out = [];
this.forDescendants(function(el) {
out.push(el);
}, s);
return out;
}n/a
destroy = function () {
this.detach();
this.forDescendants(function(el) {
el.free();
el.destroyed = true;
el.emit('destroy');
}, this);
}...
output: client,
terminal: 'xterm-256color',
fullUnicode: true
});
client.on('close', function() {
if (!screen.destroyed) {
screen.destroy();
}
});
screen.key(['C-c', 'q'], function(ch, key) {
screen.destroy();
});
...detach = function () {
if (this.parent) this.parent.remove(this);
}n/a
emitAncestors = function () {
var args = Array.prototype.slice(arguments)
, iter;
if (typeof args[args.length - 1] === 'function') {
iter = args.pop();
}
return this.forAncestors(function(el) {
if (iter) iter(el);
el.emit.apply(el, args);
}, true);
}n/a
emitDescendants = function () {
var args = Array.prototype.slice(arguments)
, iter;
if (typeof args[args.length - 1] === 'function') {
iter = args.pop();
}
return this.forDescendants(function(el) {
if (iter) iter(el);
el.emit.apply(el, args);
}, true);
}n/a
forAncestors = function (iter, s) {
var el = this;
if (s) iter(this);
while (el = el.parent) {
iter(el);
}
}n/a
forDescendants = function (iter, s) {
if (s) iter(this);
this.children.forEach(function emit(el) {
iter(el);
el.children.forEach(emit);
});
}n/a
free = function () {
return;
}n/a
get = function (name, value) {
if (this.data.hasOwnProperty(name)) {
return this.data[name];
}
return value;
}n/a
hasAncestor = function (target) {
var el = this;
while (el = el.parent) {
if (el === target) return true;
}
return false;
}n/a
hasDescendant = function (target) {
return (function find(el) {
for (var i = 0; i < el.children.length; i++) {
if (el.children[i] === target) {
return true;
}
if (find(el.children[i]) === true) {
return true;
}
}
return false;
})(this);
}n/a
insert = function (element, i) {
var self = this;
if (element.screen && element.screen !== this.screen) {
throw new Error('Cannot switch a node\'s screen.');
}
element.detach();
element.parent = this;
element.screen = this.screen;
if (i === 0) {
this.children.unshift(element);
} else if (i === this.children.length) {
this.children.push(element);
} else {
this.children.splice(i, 0, element);
}
element.emit('reparent', this);
this.emit('adopt', element);
(function emit(el) {
var n = el.detached !== self.detached;
el.detached = self.detached;
if (n) el.emit('attach');
el.children.forEach(emit);
})(element);
if (!this.screen.focused) {
this.screen.focused = element;
}
}n/a
insertAfter = function (element, other) {
var i = this.children.indexOf(other);
if (~i) this.insert(element, i + 1);
}n/a
insertBefore = function (element, other) {
var i = this.children.indexOf(other);
if (~i) this.insert(element, i);
}n/a
prepend = function (element) {
this.insert(element, 0);
}n/a
remove = function (element) {
if (element.parent !== this) return;
var i = this.children.indexOf(element);
if (!~i) return;
element.clearPos();
element.parent = null;
this.children.splice(i, 1);
i = this.screen.clickable.indexOf(element);
if (~i) this.screen.clickable.splice(i, 1);
i = this.screen.keyable.indexOf(element);
if (~i) this.screen.keyable.splice(i, 1);
element.emit('reparent', null);
this.emit('remove', element);
(function emit(el) {
var n = el.detached !== true;
el.detached = true;
if (n) el.emit('detach');
el.children.forEach(emit);
})(element);
if (this.screen.focused === element) {
this.screen.rewindFocus();
}
}n/a
set = function (name, value) {
return this.data[name] = value;
}n/a
function OverlayImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}n/a
clearImage = function (callback) {
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.clearImageSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!this._props) {
if (!callback) return;
return callback(null);
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, [], opt, function(err, success) {
if (!callback) return;
return err
? callback(err)
: callback(null, success);
});
var width = this._props.width + 2
, height = this._props.height + 2
, aleft = this._props.aleft
, atop = this._props.atop;
if (this._drag) {
aleft -= 10;
atop -= 10;
width += 10;
height += 10;
}
var input = '6;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height
+ '\n4;\n3;\n';
delete this.file;
delete this._props;
delete this._lastSize;
ps.stdin.write(input);
ps.stdin.end();
}n/a
clearImageSync = function () {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!this._props) {
return false;
}
var width = this._props.width + 2
, height = this._props.height + 2
, aleft = this._props.aleft
, atop = this._props.atop;
if (this._drag) {
aleft -= 10;
atop -= 10;
width += 10;
height += 10;
}
var input = '6;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height
+ '\n4;\n3;\n';
delete this.file;
delete this._props;
delete this._lastSize;
try {
cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
return true;
}n/a
displayImage = function (callback) {
return this.screen.displayImage(this.file, callback);
}n/a
getPixelRatio = function (callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.getPixelRatioSync());
} catch (e) {
return callback(e);
}
}
// XXX We could cache this, but sometimes it's better
// to recalculate to be pixel perfect.
if (this._ratio && !this._needsRatio) {
return callback(null, this._ratio);
}
return this.termSize(function(err, dimensions) {
if (err) return callback(err);
self._ratio = {
tw: dimensions.width / self.screen.width,
th: dimensions.height / self.screen.height
};
self._needsRatio = false;
return callback(null, self._ratio);
});
}n/a
getPixelRatioSync = function () {
// XXX We could cache this, but sometimes it's better
// to recalculate to be pixel perfect.
if (this._ratio && !this._needsRatio) {
return this._ratio;
}
this._needsRatio = false;
var dimensions = this.termSizeSync();
this._ratio = {
tw: dimensions.width / this.screen.width,
th: dimensions.height / this.screen.height
};
return this._ratio;
}n/a
imageSize = function (callback) {
var img = this.file;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.imageSizeSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!img) {
if (!callback) return;
return callback(new Error('No image.'));
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, [], opt);
var buf = '';
ps.stdout.setEncoding('utf8');
ps.stdout.on('data', function(data) {
buf += data;
});
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function() {
if (!callback) return;
var size = buf.trim().split(/\s+/);
return callback(null, {
raw: buf.trim(),
width: +size[0],
height: +size[1]
});
});
var input = '5;' + img + '\n';
ps.stdin.write(input);
ps.stdin.end();
}n/a
imageSizeSync = function () {
var img = this.file;
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!img) {
throw new Error('No image.');
}
var buf = '';
var input = '5;' + img + '\n';
try {
buf = cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
var size = buf.trim().split(/\s+/);
return {
raw: buf.trim(),
width: +size[0],
height: +size[1]
};
}n/a
renderImage = function (img, ratio, callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.renderImageSync(img, ratio));
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!ratio) {
if (!callback) return;
return callback(new Error('No ratio.'));
}
// clearImage unsets these:
var _file = self.file;
var _lastSize = self._lastSize;
return self.clearImage(function(err) {
if (err) return callback(err);
self.file = _file;
self._lastSize = _lastSize;
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = self.spawn(OverlayImage.w3mdisplay, [], opt, function(err, success) {
if (!callback) return;
return err
? callback(err)
: callback(null, success);
});
var width = self.width * ratio.tw | 0
, height = self.height * ratio.th | 0
, aleft = self.aleft * ratio.tw | 0
, atop = self.atop * ratio.th | 0;
var input = '0;1;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height + ';;;;;'
+ img
+ '\n4;\n3;\n';
self._props = {
aleft: aleft,
atop: atop,
width: width,
height: height
};
ps.stdin.write(input);
ps.stdin.end();
});
}n/a
renderImageSync = function (img, ratio) {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!ratio) {
throw new Error('No ratio.');
}
// clearImage unsets these:
var _file = this.file;
var _lastSize = this._lastSize;
this.clearImageSync();
this.file = _file;
this._lastSize = _lastSize;
var width = this.width * ratio.tw | 0
, height = this.height * ratio.th | 0
, aleft = this.aleft * ratio.tw | 0
, atop = this.atop * ratio.th | 0;
var input = '0;1;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height + ';;;;;'
+ img
+ '\n4;\n3;\n';
this._props = {
aleft: aleft,
atop: atop,
width: width,
height: height
};
try {
cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
return true;
}n/a
setImage = function (img, callback) {
var self = this;
if (this._settingImage) {
this._queue = this._queue || [];
this._queue.push([img, callback]);
return;
}
this._settingImage = true;
var reset = function() {
self._settingImage = false;
self._queue = self._queue || [];
var item = self._queue.shift();
if (item) {
self.setImage(item[0], item[1]);
}
};
if (OverlayImage.hasW3MDisplay === false) {
reset();
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!img) {
reset();
if (!callback) return;
return callback(new Error('No image.'));
}
this.file = img;
return this.getPixelRatio(function(err, ratio) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
return self.renderImage(img, ratio, function(err, success) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
if (self.shrink || self.options.autofit) {
delete self.shrink;
delete self.options.shrink;
self.options.autofit = true;
return self.imageSize(function(err, size) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
if (self._lastSize
&& ratio.tw === self._lastSize.tw
&& ratio.th === self._lastSize.th
&& size.width === self._lastSize.width
&& size.height === self._lastSize.height
&& self.aleft === self._lastSize.aleft
&& self.atop === self._lastSize.atop) {
reset();
if (!callback) return;
return callback(null, success);
}
self._lastSize = {
tw: ratio.tw,
th: ratio.th,
width: size.width,
height: size.height,
aleft: self.aleft,
atop: self.atop
};
self.position.width = size.width / ratio.tw | 0;
self.position.height = size.height / ratio.th | 0;
self._noImage = true;
self.screen.render();
self._noImage = false;
reset();
return self.renderImage(img, ratio, callback);
});
}
reset();
if (!callback) return;
return callback(null, success);
});
});
}n/a
spawn = function (file, args, opt, callback) {
var spawn = require('child_process').spawn
, ps;
opt = opt || {};
ps = spawn(file, args, opt);
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function(code) {
if (!callback) return;
if (code !== 0) return callback(new Error('Exit Code: ' + code));
return callback(null, code === 0);
});
return ps;
}n/a
termSize = function (callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.termSizeSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, ['-test'], opt);
var buf = '';
ps.stdout.setEncoding('utf8');
ps.stdout.on('data', function(data) {
buf += data;
});
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function() {
if (!callback) return;
if (!buf.trim()) {
// Bug: w3mimgdisplay will sometimes
// output nothing. Try again:
return self.termSize(callback);
}
var size = buf.trim().split(/\s+/);
return callback(null, {
raw: buf.trim(),
width: +size[0],
height: +size[1]
});
});
ps.stdin.end();
}n/a
termSizeSync = function (_, recurse) {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
var buf = '';
try {
buf = cp.execFileSync(OverlayImage.w3mdisplay, ['-test'], {
env: process.env,
encoding: 'utf8',
timeout: 1000
});
} catch (e) {
;
}
if (!buf.trim()) {
// Bug: w3mimgdisplay will sometimes
// output nothing. Try again:
recurse = recurse || 0;
if (++recurse === 5) {
throw new Error('Term size not determined.');
}
return this.termSizeSync(_, recurse);
}
var size = buf.trim().split(/\s+/);
return {
raw: buf.trim(),
width: +size[0],
height: +size[1]
};
}n/a
function ProgressBar(options) {
var self = this;
if (!(this instanceof Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}n/a
progress = function (filled) {
this.filled += filled;
if (this.filled < 0) this.filled = 0;
else if (this.filled > 100) this.filled = 100;
if (this.filled === 100) {
this.emit('complete');
}
this.value = this.filled;
}n/a
render = function () {
var ret = this._render();
if (!ret) return;
var xi = ret.xi
, xl = ret.xl
, yi = ret.yi
, yl = ret.yl
, dattr;
if (this.border) xi++, yi++, xl--, yl--;
if (this.orientation === 'horizontal') {
xl = xi + ((xl - xi) * (this.filled / 100)) | 0;
} else if (this.orientation === 'vertical') {
yi = yi + ((yl - yi) - (((yl - yi) * (this.filled / 100)) | 0));
}
dattr = this.sattr(this.style.bar);
this.screen.fillRegion(dattr, this.pch, xi, xl, yi, yl);
if (this.content) {
var line = this.screen.lines[yi];
for (var i = 0; i < this.content.length; i++) {
line[xi + i][1] = this.content[i];
}
line.dirty = true;
}
return ret;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...reset = function () {
this.emit('reset');
this.filled = 0;
this.value = this.filled;
}n/a
setProgress = function (filled) {
this.filled = 0;
this.progress(filled);
}n/a
function Prompt(options) {
if (!(this instanceof Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
input = function (text, value, callback) {
var self = this;
var okay, cancel;
if (!callback) {
callback = value;
value = '';
}
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(' ' + text);
this._.input.value = value;
this.screen.saveFocus();
this._.okay.on('press', okay = function() {
self._.input.submit();
});
this._.cancel.on('press', cancel = function() {
self._.input.cancel();
});
this._.input.readInput(function(err, data) {
self.hide();
self.screen.restoreFocus();
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
});
this.screen.render();
}n/a
readInput = function (text, value, callback) {
var self = this;
var okay, cancel;
if (!callback) {
callback = value;
value = '';
}
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(' ' + text);
this._.input.value = value;
this.screen.saveFocus();
this._.okay.on('press', okay = function() {
self._.input.submit();
});
this._.cancel.on('press', cancel = function() {
self._.input.cancel();
});
this._.input.readInput(function(err, data) {
self.hide();
self.screen.restoreFocus();
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
});
this.screen.render();
}n/a
setInput = function (text, value, callback) {
var self = this;
var okay, cancel;
if (!callback) {
callback = value;
value = '';
}
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(' ' + text);
this._.input.value = value;
this.screen.saveFocus();
this._.okay.on('press', okay = function() {
self._.input.submit();
});
this._.cancel.on('press', cancel = function() {
self._.input.cancel();
});
this._.input.readInput(function(err, data) {
self.hide();
self.screen.restoreFocus();
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
});
this.screen.render();
}n/a
function Question(options) {
if (!(this instanceof Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.okay = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
ask = function (text, callback) {
var self = this;
var press, okay, cancel;
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(' ' + text);
this.onScreenEvent('keypress', press = function(ch, key) {
if (key.name === 'mouse') return;
if (key.name !== 'enter'
&& key.name !== 'escape'
&& key.name !== 'q'
&& key.name !== 'y'
&& key.name !== 'n') {
return;
}
done(null, key.name === 'enter' || key.name === 'y');
});
this._.okay.on('press', okay = function() {
done(null, true);
});
this._.cancel.on('press', cancel = function() {
done(null, false);
});
this.screen.saveFocus();
this.focus();
function done(err, data) {
self.hide();
self.screen.restoreFocus();
self.removeScreenEvent('keypress', press);
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
}
this.screen.render();
}n/a
function RadioButton(options) {
var self = this;
if (!(this instanceof Node)) {
return new RadioButton(options);
}
options = options || {};
Checkbox.call(this, options);
this.on('check', function() {
var el = self;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self) {
return;
}
el.uncheck();
});
});
}n/a
render = function () {
this.clearPos(true);
this.setContent('(' + (this.checked ? '*' : ' ') + ') ' + this.text, true);
return this._render();
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...toggle = function () {
if (this.checked) return;
this.checked = this.value = true;
this.emit('check');
}n/a
function ScrollableBox(options) {
var self = this;
if (!(this instanceof Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll( ...n/a
_recalculateIndex = function () {
var max, emax;
if (this.detached || !this.scrollable) {
return 0;
}
// XXX
// max = this.getScrollHeight() - (this.height - this.iheight);
max = this._clines.length - (this.height - this.iheight);
if (max < 0) max = 0;
emax = this._scrollBottom() - (this.height - this.iheight);
if (emax < 0) emax = 0;
this.childBase = Math.min(this.childBase, Math.max(emax, max));
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
}n/a
_scrollBottom = function () {
if (!this.scrollable) return 0;
// We could just calculate the children, but we can
// optimize for lists by just returning the items.length.
if (this._isList) {
return this.items ? this.items.length : 0;
}
if (this.lpos && this.lpos._scrollBottom) {
return this.lpos._scrollBottom;
}
var bottom = this.children.reduce(function(current, el) {
// el.height alone does not calculate the shrunken height, we need to use
// getCoords. A shrunken box inside a scrollable element will not grow any
// larger than the scrollable element's context regardless of how much
// content is in the shrunken box, unless we do this (call getCoords
// without the scrollable calculation):
// See: $ node test/widget-shrink-fail-2.js
if (!el.detached) {
var lpos = el._getCoords(false, true);
if (lpos) {
return Math.max(current, el.rtop + (lpos.yl - lpos.yi));
}
}
return Math.max(current, el.rtop + el.height);
}, 0);
// XXX Use this? Makes .getScrollHeight() useless!
// if (bottom < this._clines.length) bottom = this._clines.length;
if (this.lpos) this.lpos._scrollBottom = bottom;
return bottom;
}n/a
getScroll = function () {
return this.childBase + this.childOffset;
}n/a
getScrollHeight = function () {
return Math.max(this._clines.length, this._scrollBottom());
}n/a
getScrollPerc = function (s) {
var pos = this.lpos || this._getCoords();
if (!pos) return s ? -1 : 0;
var height = (pos.yl - pos.yi) - this.iheight
, i = this.getScrollHeight()
, p;
if (height < i) {
if (this.alwaysScroll) {
p = this.childBase / (i - height);
} else {
p = (this.childBase + this.childOffset) / (i - 1);
}
return p * 100;
}
return s ? -1 : 0;
}n/a
resetScroll = function () {
if (!this.scrollable) return;
this.childOffset = 0;
this.childBase = 0;
return this.emit('scroll');
}n/a
scroll = function (offset, always) {
if (!this.scrollable) return;
if (this.detached) return;
// Handle scrolling.
var visible = this.height - this.iheight
, base = this.childBase
, d
, p
, t
, b
, max
, emax;
if (this.alwaysScroll || always) {
// Semi-workaround
this.childOffset = offset > 0
? visible - 1 + offset
: offset;
} else {
this.childOffset += offset;
}
if (this.childOffset > visible - 1) {
d = this.childOffset - (visible - 1);
this.childOffset -= d;
this.childBase += d;
} else if (this.childOffset < 0) {
d = this.childOffset;
this.childOffset += -d;
this.childBase += d;
}
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Find max "bottom" value for
// content and descendant elements.
// Scroll the content if necessary.
if (this.childBase === base) {
return this.emit('scroll');
}
// When scrolling text, we want to be able to handle SGR codes as well as line
// feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box.
this.parseContent();
// XXX
// max = this.getScrollHeight() - (this.height - this.iheight);
max = this._clines.length - (this.height - this.iheight);
if (max < 0) max = 0;
emax = this._scrollBottom() - (this.height - this.iheight);
if (emax < 0) emax = 0;
this.childBase = Math.min(this.childBase, Math.max(emax, max));
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Optimize scrolling with CSR + IL/DL.
p = this.lpos;
// Only really need _getCoords() if we want
// to allow nestable scrolling elements...
// or if we **really** want shrinkable
// scrolling elements.
// p = this._getCoords();
if (p && this.childBase !== base && this.screen.cleanSides(this)) {
t = p.yi + this.itop;
b = p.yl - this.ibottom - 1;
d = this.childBase - base;
if (d > 0 && d < visible) {
// scrolled down
this.screen.deleteLine(d, t, t, b);
} else if (d < 0 && -d < visible) {
// scrolled up
d = -d;
this.screen.insertLine(d, t, t, b);
}
}
return this.emit('scroll');
}n/a
scrollTo = function (offset, always) {
// XXX
// At first, this appeared to account for the first new calculation of childBase:
this.scroll(0);
return this.scroll(offset - (this.childBase + this.childOffset), always);
}n/a
setScroll = function (offset, always) {
// XXX
// At first, this appeared to account for the first new calculation of childBase:
this.scroll(0);
return this.scroll(offset - (this.childBase + this.childOffset), always);
}n/a
setScrollPerc = function (i) {
// XXX
// var m = this.getScrollHeight();
var m = Math.max(this._clines.length, this._scrollBottom());
return this.scrollTo((i / 100) * m | 0);
}n/a
function Table(options) {
var self = this;
if (!(this instanceof Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}n/a
_calculateMaxes = function () {
var self = this;
var maxes = [];
if (this.detached) return;
this.rows = this.rows || [];
this.rows.forEach(function(row) {
row.forEach(function(cell, i) {
var clen = self.strWidth(cell);
if (!maxes[i] || maxes[i] < clen) {
maxes[i] = clen;
}
});
});
var total = maxes.reduce(function(total, max) {
return total + max;
}, 0);
total += maxes.length + 1;
// XXX There might be an issue with resizing where on the first resize event
// width appears to be less than total if it's a percentage or left/right
// combination.
if (this.width < total) {
delete this.position.width;
}
if (this.position.width != null) {
var missing = this.width - total;
var w = missing / maxes.length | 0;
var wr = missing % maxes.length;
maxes = maxes.map(function(max, i) {
if (i === maxes.length - 1) {
return max + w + wr;
}
return max + w;
});
} else {
maxes = maxes.map(function(max) {
return max + self.pad;
});
}
return this._maxes = maxes;
}n/a
render = function () {
var self = this;
var coords = this._render();
if (!coords) return;
this._calculateMaxes();
if (!this._maxes) return coords;
var lines = this.screen.lines
, xi = coords.xi
, yi = coords.yi
, rx
, ry
, i;
var dattr = this.sattr(this.style)
, hattr = this.sattr(this.style.header)
, cattr = this.sattr(this.style.cell)
, battr = this.sattr(this.style.border);
var width = coords.xl - coords.xi - this.iright
, height = coords.yl - coords.yi - this.ibottom;
// Apply attributes to header cells and cells.
for (var y = this.itop; y < height; y++) {
if (!lines[yi + y]) break;
for (var x = this.ileft; x < width; x++) {
if (!lines[yi + y][xi + x]) break;
// Check to see if it's not the default attr. Allows for tags:
if (lines[yi + y][xi + x][0] !== dattr) continue;
if (y === this.itop) {
lines[yi + y][xi + x][0] = hattr;
} else {
lines[yi + y][xi + x][0] = cattr;
}
lines[yi + y].dirty = true;
}
}
if (!this.border || this.options.noCellBorders) return coords;
// Draw border with correct angles.
ry = 0;
for (i = 0; i < self.rows.length + 1; i++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.forEach(function(max, i) {
rx += max;
if (i === 0) {
if (!lines[yi + ry][xi + 0]) return;
// left side
if (ry === 0) {
// top
lines[yi + ry][xi + 0][0] = battr;
// lines[yi + ry][xi + 0][1] = '\u250c'; // '┌'
} else if (ry / 2 === self.rows.length) {
// bottom
lines[yi + ry][xi + 0][0] = battr;
// lines[yi + ry][xi + 0][1] = '\u2514'; // '└'
} else {
// middle
lines[yi + ry][xi + 0][0] = battr;
lines[yi + ry][xi + 0][1] = '\u251c'; // '├'
// XXX If we alter iwidth and ileft for no borders - nothing should be written here
if (!self.border.left) {
lines[yi + ry][xi + 0][1] = '\u2500'; // '─'
}
}
lines[yi + ry].dirty = true;
} else if (i === self._maxes.length - 1) {
if (!lines[yi + ry][xi + rx + 1]) return;
// right side
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
// lines[yi + ry][xi + rx][1] = '\u2510'; // '┐'
} else if (ry / 2 === self.rows.length) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
// lines[yi + ry][xi + rx][1] = '\u2518'; // '┘'
} else {
// middle
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2524'; // '┤'
// XXX If we alter iwidth and iright for no borders - nothing should be written here
if (!self.border.right) {
lines[yi + ry][xi + rx][1] = '\u2500'; // '─'
}
}
lines[yi + ry].dirty = true;
return;
}
if (!lines[yi + ry][xi + rx + 1]) return;
// center
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u252c'; // '┬'
// XXX If we alter iheight and itop for no borders - nothing should be written here
if (!self.border.top) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
} else if (ry / 2 === self.rows.length) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2534'; // '┴'
// XXX If we alter iheight and ibottom for no borders - nothing should be written here
if (!self.border.bottom) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
} else {
// middle
if (self.options.fillCellBorders) {
var lbg = (ry <= 2 ? hattr : cattr) & 0x1ff;
rx++;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
rx++;
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry ......
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...setData = function (rows) {
var self = this
, text = ''
, align = this.align;
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.rows.forEach(function(row, i) {
var isFooter = i === self.rows.length - 1;
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (!isFooter) {
text += '\n\n';
}
});
delete this.align;
this.setContent(text);
this.align = align;
}...
- Inherits all from List.
##### Methods:
- Inherits all from List.
- __setRows/setData(rows)__ - Set rows in table. Array of arrays of strings.
``` js
table.setData([
[ 'Animals', 'Foods' ],
[ 'Elephant', 'Apple' ],
[ 'Bird', 'Orange' ]
]);
```
...setRows = function (rows) {
var self = this
, text = ''
, align = this.align;
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.rows.forEach(function(row, i) {
var isFooter = i === self.rows.length - 1;
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (!isFooter) {
text += '\n\n';
}
});
delete this.align;
this.setContent(text);
this.align = align;
}n/a
function Terminal(options) {
if (!(this instanceof Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = false;
Box.call(this, options);
// XXX Workaround for all motion
if (this.screen.program.tmux && this.screen.program.tmuxVersion >= 2) {
this.screen.program.enableMouse();
}
this.handler = options.handler;
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
this.termName = options.terminal
|| options.term
|| process.env.TERM
|| 'xterm';
this.bootstrap();
}n/a
_isMouse = function (buf) {
var s = buf;
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');
}
}
return (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d)
|| /^\x1b\[M([\x00\u0020-\uffff]{3})/.test(s)
|| /^\x1b\[(\d+;\d+;\d+)M/.test(s)
|| /^\x1b\[<(\d+;\d+;\d+)([mM])/.test(s)
|| /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.test(s)
|| /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.test(s)
|| /^\x1b\[(O|I)/.test(s);
}n/a
bootstrap = function () {
var self = this;
var element = {
// window
get document() { return element; },
navigator: { userAgent: 'node.js' },
// document
get defaultView() { return element; },
get documentElement() { return element; },
createElement: function() { return element; },
// element
get ownerDocument() { return element; },
addEventListener: function() {},
removeEventListener: function() {},
getElementsByTagName: function() { return [element]; },
getElementById: function() { return element; },
parentNode: null,
offsetParent: null,
appendChild: function() {},
removeChild: function() {},
setAttribute: function() {},
getAttribute: function() {},
style: {},
focus: function() {},
blur: function() {},
console: console
};
element.parentNode = element;
element.offsetParent = element;
this.term = require('term.js')({
termName: this.termName,
cols: this.width - this.iwidth,
rows: this.height - this.iheight,
context: element,
document: element,
body: element,
parent: element,
cursorBlink: this.cursorBlink,
screenKeys: this.screenKeys
});
this.term.refresh = function() {
self.screen.render();
};
this.term.keyDown = function() {};
this.term.keyPress = function() {};
this.term.open(element);
// Emits key sequences in html-land.
// Technically not necessary here.
// In reality if we wanted to be neat, we would overwrite the keyDown and
// keyPress methods with our own node.js-keys->terminal-keys methods, but
// since all the keys are already coming in as escape sequences, we can just
// send the input directly to the handler/socket (see below).
// this.term.on('data', function(data) {
// self.handler(data);
// });
// Incoming keys and mouse inputs.
// NOTE: Cannot pass mouse events - coordinates will be off!
this.screen.program.input.on('data', this._onData = function(data) {
if (self.screen.focused === self && !self._isMouse(data)) {
self.handler(data);
}
});
this.onScreenEvent('mouse', function(data) {
if (self.screen.focused !== self) return;
if (data.x < self.aleft + self.ileft) return;
if (data.y < self.atop + self.itop) return;
if (data.x > self.aleft - self.ileft + self.width) return;
if (data.y > self.atop - self.itop + self.height) return;
if (self.term.x10Mouse
|| self.term.vt200Mouse
|| self.term.normalMouse
|| self.term.mouseEvents
|| self.term.utfMouse
|| self.term.sgrMouse
|| self.term.urxvtMouse) {
;
} else {
return;
}
var b = data.raw[0]
, x = data.x - self.aleft
, y = data.y - self.atop
, s;
if (self.term.urxvtMouse) {
if (self.screen.program.sgrMouse) {
b += 32;
}
s = '\x1b[' + b + ';' + (x + 32) + ';' + (y + 32) + 'M';
} else if (self.term.sgrMouse) {
if (!self.screen.program.sgrMouse) {
b -= 32;
}
s = '\x1b[<' + b + ';' + x + ';' + y
+ (data.action === 'mousedown' ? 'M' : 'm');
} else {
if (self.screen.program.sgrMouse) {
b += 32;
}
s = '\x1b[M'
+ String.fromCharCode(b)
+ String.fromCharCode(x + 32)
+ String.fromCharCode(y + 32);
}
self.handler(s);
});
this.on('focus', function() {
self.term.focus();
});
this.on('blur', function() {
self.term.blur();
});
this.term.on('title', function(title) {
self.title = title;
self.emit('title', title);
});
this.term.on('passthrough', function(data) {
self.screen.program.flush();
self.screen.program._owrite(data);
});
this.on('resize', function() {
nextTick(function() {
self.term.resize(self.width - self.iwidth, self.height - self.iheight);
});
});
this.once('render', function() {
self.term.resize(self.width - self.iwidth, self.height - self.iheight);
});
this.on('destroy', function() {
self.kill();
self.screen.program.input.removeListener('data', ...n/a
getScroll = function () {
return this.term.ydisp;
}n/a
getScrollHeight = function () {
return this.term.rows - 1;
}n/a
getScrollPerc = function () {
return (this.term.ydisp / this.term.ybase) * 100;
}n/a
kill = function () {
if (this.pty) {
this.pty.destroy();
this.pty.kill();
}
this.term.refresh = function() {};
this.term.write('\x1b[H\x1b[J');
if (this.term._blink) {
clearInterval(this.term._blink);
}
this.term.destroy();
}n/a
render = function () {
var ret = this._render();
if (!ret) return;
this.dattr = this.sattr(this.style);
var xi = ret.xi + this.ileft
, xl = ret.xl - this.iright
, yi = ret.yi + this.itop
, yl = ret.yl - this.ibottom
, cursor;
var scrollback = this.term.lines.length - (yl - yi);
for (var y = Math.max(yi, 0); y < yl; y++) {
var line = this.screen.lines[y];
if (!line || !this.term.lines[scrollback + y - yi]) break;
if (y === yi + this.term.y
&& this.term.cursorState
&& this.screen.focused === this
&& (this.term.ydisp === this.term.ybase || this.term.selectMode)
&& !this.term.cursorHidden) {
cursor = xi + this.term.x;
} else {
cursor = -1;
}
for (var x = Math.max(xi, 0); x < xl; x++) {
if (!line[x] || !this.term.lines[scrollback + y - yi][x - xi]) break;
line[x][0] = this.term.lines[scrollback + y - yi][x - xi][0];
if (x === cursor) {
if (this.cursor === 'line') {
line[x][0] = this.dattr;
line[x][1] = '\u2502';
continue;
} else if (this.cursor === 'underline') {
line[x][0] = this.dattr | (2 << 18);
} else if (this.cursor === 'block' || !this.cursor) {
line[x][0] = this.dattr | (8 << 18);
}
}
line[x][1] = this.term.lines[scrollback + y - yi][x - xi][1];
// default foreground = 257
if (((line[x][0] >> 9) & 0x1ff) === 257) {
line[x][0] &= ~(0x1ff << 9);
line[x][0] |= ((this.dattr >> 9) & 0x1ff) << 9;
}
// default background = 256
if ((line[x][0] & 0x1ff) === 256) {
line[x][0] &= ~0x1ff;
line[x][0] |= this.dattr & 0x1ff;
}
}
line.dirty = true;
}
return ret;
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...resetScroll = function () {
this.term.ydisp = 0;
this.term.ybase = 0;
return this.emit('scroll');
}n/a
screenshot = function (xi, xl, yi, yl) {
xi = 0 + (xi || 0);
if (xl != null) {
xl = 0 + (xl || 0);
} else {
xl = this.term.lines[0].length;
}
yi = 0 + (yi || 0);
if (yl != null) {
yl = 0 + (yl || 0);
} else {
yl = this.term.lines.length;
}
return this.screen.screenshot(xi, xl, yi, yl, this.term);
}n/a
scroll = function (offset) {
this.term.scrollDisp(offset);
return this.emit('scroll');
}n/a
scrollTo = function (offset) {
this.term.ydisp = offset;
return this.emit('scroll');
}n/a
setScroll = function (offset) {
this.term.ydisp = offset;
return this.emit('scroll');
}n/a
setScrollPerc = function (i) {
return this.setScroll((i / 100) * this.term.ybase | 0);
}n/a
write = function (data) {
return this.term.write(data);
}...
var blessed = require('blessed');
var tput = blessed.tput({
terminal: 'xterm-256color',
extended: true
});
process.stdout.write(tput.setaf(4) + 'Hello' + tput.sgr0() + '\n');
```
To play around with it on the command line, it works just like tput:
``` bash
$ tput.js setaf 2
$ tput.js sgr0
...function Textarea(options) {
var self = this;
if (!(this instanceof Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}n/a
_listener = function (ch, key) {
var done = this._done
, value = this.value;
if (key.name === 'return') return;
if (key.name === 'enter') {
ch = '\n';
}
// TODO: Handle directional keys.
if (key.name === 'left' || key.name === 'right'
|| key.name === 'up' || key.name === 'down') {
;
}
if (this.options.keys && key.ctrl && key.name === 'e') {
return this.readEditor();
}
// TODO: Optimize typing by writing directly
// to the screen and screen buffer here.
if (key.name === 'escape') {
done(null, null);
} else if (key.name === 'backspace') {
if (this.value.length) {
if (this.screen.fullUnicode) {
if (unicode.isSurrogate(this.value, this.value.length - 2)) {
// || unicode.isCombining(this.value, this.value.length - 1)) {
this.value = this.value.slice(0, -2);
} else {
this.value = this.value.slice(0, -1);
}
} else {
this.value = this.value.slice(0, -1);
}
}
} else if (ch) {
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
this.value += ch;
}
}
if (this.value !== value) {
this.screen.render();
}
}n/a
_typeScroll = function () {
// XXX Workaround
var height = this.height - this.iheight;
if (this._clines.length - this.childBase > height) {
this.scroll(this._clines.length);
}
}n/a
_updateCursor = function (get) {
if (this.screen.focused !== this) {
return;
}
var lpos = get ? this.lpos : this._getCoords();
if (!lpos) return;
var last = this._clines[this._clines.length - 1]
, program = this.screen.program
, line
, cx
, cy;
// Stop a situation where the textarea begins scrolling
// and the last cline appears to always be empty from the
// _typeScroll `+ '\n'` thing.
// Maybe not necessary anymore?
if (last === '' && this.value[this.value.length - 1] !== '\n') {
last = this._clines[this._clines.length - 2] || '';
}
line = Math.min(
this._clines.length - 1 - (this.childBase || 0),
(lpos.yl - lpos.yi) - this.iheight - 1);
// When calling clearValue() on a full textarea with a border, the first
// argument in the above Math.min call ends up being -2. Make sure we stay
// positive.
line = Math.max(0, line);
cy = lpos.yi + this.itop + line;
cx = lpos.xi + this.ileft + this.strWidth(last);
// XXX Not sure, but this may still sometimes
// cause problems when leaving editor.
if (cy === program.y && cx === program.x) {
return;
}
if (cy === program.y) {
if (cx > program.x) {
program.cuf(cx - program.x);
} else if (cx < program.x) {
program.cub(program.x - cx);
}
} else if (cx === program.x) {
if (cy > program.y) {
program.cud(cy - program.y);
} else if (cy < program.y) {
program.cuu(program.y - cy);
}
} else {
program.cup(cy, cx);
}
}n/a
cancel = function () {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
}n/a
clearInput = function () {
return this.setValue('');
}n/a
clearValue = function () {
return this.setValue('');
}n/a
editor = function (callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
}n/a
getValue = function () {
return this.value;
}n/a
input = function (callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
self._reading = false;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
}n/a
readEditor = function (callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
}n/a
readInput = function (callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
self._reading = false;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
}n/a
render = function () {
this.setValue();
return this._render();
}...
file: __dirname + '/my-program-icon.png',
search: false
});
// If our box is clicked, change the content.
box.on('click', function(data) {
box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}');
screen.render();
});
// If box is focused, handle `enter`/`return` and give us some more content.
box.key('enter', function(ch, key) {
box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n');
box.setLine(1, 'bar');
box.insertLine(1, 'foo');
...setEditor = function (callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
}n/a
setInput = function (callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
self._reading = false;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
}n/a
setValue = function (value) {
if (value == null) {
value = this.value;
}
if (this._value !== value) {
this.value = value;
this._value = value;
this.setContent(this.value);
this._typeScroll();
this._updateCursor();
}
}n/a
submit = function () {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
}n/a
function Textbox(options) {
if (!(this instanceof Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = false;
Textarea.call(this, options);
this.secret = options.secret;
this.censor = options.censor;
}n/a
__olistener = function (ch, key) {
var done = this._done
, value = this.value;
if (key.name === 'return') return;
if (key.name === 'enter') {
ch = '\n';
}
// TODO: Handle directional keys.
if (key.name === 'left' || key.name === 'right'
|| key.name === 'up' || key.name === 'down') {
;
}
if (this.options.keys && key.ctrl && key.name === 'e') {
return this.readEditor();
}
// TODO: Optimize typing by writing directly
// to the screen and screen buffer here.
if (key.name === 'escape') {
done(null, null);
} else if (key.name === 'backspace') {
if (this.value.length) {
if (this.screen.fullUnicode) {
if (unicode.isSurrogate(this.value, this.value.length - 2)) {
// || unicode.isCombining(this.value, this.value.length - 1)) {
this.value = this.value.slice(0, -2);
} else {
this.value = this.value.slice(0, -1);
}
} else {
this.value = this.value.slice(0, -1);
}
}
} else if (ch) {
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
this.value += ch;
}
}
if (this.value !== value) {
this.screen.render();
}
}n/a
_listener = function (ch, key) {
if (key.name === 'enter') {
this._done(null, this.value);
return;
}
return this.__olistener(ch, key);
}n/a
setValue = function (value) {
var visible, val;
if (value == null) {
value = this.value;
}
if (this._value !== value) {
value = value.replace(/\n/g, '');
this.value = value;
this._value = value;
if (this.secret) {
this.setContent('');
} else if (this.censor) {
this.setContent(Array(this.value.length + 1).join('*'));
} else {
visible = -(this.width - this.iwidth - 1);
val = this.value.replace(/\t/g, this.screen.tabc);
this.setContent(val.slice(visible));
}
this._updateCursor();
}
}n/a
submit = function () {
if (!this.__listener) return;
return this.__listener('\r', { name: 'enter' });
}n/a
charWidth = function (str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
// nul
if (point === 0) return 0;
// tab
if (point === 0x09) {
if (!exports.blessed) {
exports.blessed = require('../');
}
return exports.blessed.screen.global
? exports.blessed.screen.global.tabc.length
: 8;
}
// 8-bit control characters (2-width according to unicode??)
if (point < 32 || (point >= 0x7f && point < 0xa0)) {
return 0;
}
// search table of non-spacing characters
// is ucs combining or C0/C1 control character
if (exports.combining[point]) {
return 0;
}
// check for double-wide
// if (point >= 0x1100
// && (point <= 0x115f // Hangul Jamo init. consonants
// || point === 0x2329 || point === 0x232a
// || (point >= 0x2e80 && point <= 0xa4cf
// && point !== 0x303f) // CJK ... Yi
// || (point >= 0xac00 && point <= 0xd7a3) // Hangul Syllables
// || (point >= 0xf900 && point <= 0xfaff) // CJK Compatibility Ideographs
// || (point >= 0xfe10 && point <= 0xfe19) // Vertical forms
// || (point >= 0xfe30 && point <= 0xfe6f) // CJK Compatibility Forms
// || (point >= 0xff00 && point <= 0xff60) // Fullwidth Forms
// || (point >= 0xffe0 && point <= 0xffe6)
// || (point >= 0x20000 && point <= 0x2fffd)
// || (point >= 0x30000 && point <= 0x3fffd))) {
// return 2;
// }
// check for double-wide
if ((0x3000 === point)
|| (0xFF01 <= point && point <= 0xFF60)
|| (0xFFE0 <= point && point <= 0xFFE6)) {
return 2;
}
if ((0x1100 <= point && point <= 0x115F)
|| (0x11A3 <= point && point <= 0x11A7)
|| (0x11FA <= point && point <= 0x11FF)
|| (0x2329 <= point && point <= 0x232A)
|| (0x2E80 <= point && point <= 0x2E99)
|| (0x2E9B <= point && point <= 0x2EF3)
|| (0x2F00 <= point && point <= 0x2FD5)
|| (0x2FF0 <= point && point <= 0x2FFB)
|| (0x3001 <= point && point <= 0x303E)
|| (0x3041 <= point && point <= 0x3096)
|| (0x3099 <= point && point <= 0x30FF)
|| (0x3105 <= point && point <= 0x312D)
|| (0x3131 <= point && point <= 0x318E)
|| (0x3190 <= point && point <= 0x31BA)
|| (0x31C0 <= point && point <= 0x31E3)
|| (0x31F0 <= point && point <= 0x321E)
|| (0x3220 <= point && point <= 0x3247)
|| (0x3250 <= point && point <= 0x32FE)
|| (0x3300 <= point && point <= 0x4DBF)
|| (0x4E00 <= point && point <= 0xA48C)
|| (0xA490 <= point && point <= 0xA4C6)
|| (0xA960 <= point && point <= 0xA97C)
|| (0xAC00 <= point && point <= 0xD7A3)
|| (0xD7B0 <= point && point <= 0xD7C6)
|| (0xD7CB <= point && point <= 0xD7FB)
|| (0xF900 <= point && point <= 0xFAFF)
|| (0xFE10 <= point && point <= 0xFE19)
|| (0xFE30 <= point && point <= 0xFE52)
|| (0xFE54 <= point && point <= 0xFE66)
|| (0xFE68 <= point && point <= 0xFE6B)
|| (0x1B000 <= point && point <= 0x1B001)
|| (0x1F200 <= point && point <= 0x1F202)
|| (0x1F210 <= point && point <= 0x1F23A)
|| (0x1F240 <= point && point <= 0x1F248)
|| (0x1F250 <= point && point <= 0x1F251)
|| (0x20000 <= point && point <= 0x2F73F)
|| (0x2B740 <= point && point <= 0x2FFFD)
|| (0x30000 <= point && point <= 0x3FFFD)) {
return 2;
}
// CJK Ambiguous
// http://www.unicode.org/reports/tr11/
// http://www.unicode.org/reports/tr11/#Ambiguous
if (process.env.NCURSES_CJK_WIDTH) {
if ((0x00A1 === point)
|| (0x00A4 === point)
|| (0x00A7 <= point && point <= 0x00A8)
|| (0x00AA === point)
|| (0x00AD <= point && point <= 0x00AE)
|| (0x00B0 <= point && point <= 0x00B4)
|| (0x00B6 <= point && point <= 0x00BA)
|| (0x00BC <= point && point <= 0x00BF)
|| (0x00C6 === point)
|| (0x00D0 === point)
|| (0x00D7 <= point && point <= 0x00D8)
|| (0x00DE <= point && point <= 0x00E1)
|| (0x00E6 === point)
|| (0x00E8 <= point && point <= 0x ...n/a
codePointAt = function (str, position) {
if (str == null) {
throw TypeError();
}
var string = String(str);
if (string.codePointAt) {
return string.codePointAt(position);
}
var size = string.length;
// `ToInteger`
var index = position ? Number(position) : 0;
if (index !== index) { // better `isNaN`
index = 0;
}
// Account for out-of-bounds indices:
if (index < 0 || index >= size) {
return undefined;
}
// Get the first code unit
var first = string.charCodeAt(index);
var second;
if ( // check if it’s the start of a surrogate pair
first >= 0xD800 && first <= 0xDBFF && // high surrogate
size > index + 1 // there is a next code unit
) {
second = string.charCodeAt(index + 1);
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return first;
}n/a
fromCodePoint = function () {
if (String.fromCodePoint) {
return String.fromCodePoint.apply(String, arguments);
}
var MAX_SIZE = 0x4000;
var codeUnits = [];
var highSurrogate;
var lowSurrogate;
var index = -1;
var length = arguments.length;
if (!length) {
return '';
}
var result = '';
while (++index < length) {
var codePoint = Number(arguments[index]);
if (
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 0x10FFFF || // not a valid Unicode code point
floor(codePoint) !== codePoint // not an integer
) {
throw RangeError('Invalid code point: ' + codePoint);
}
if (codePoint <= 0xFFFF) { // BMP code point
codeUnits.push(codePoint);
} else { // Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 0x10000;
highSurrogate = (codePoint >> 10) + 0xD800;
lowSurrogate = (codePoint % 0x400) + 0xDC00;
codeUnits.push(highSurrogate, lowSurrogate);
}
if (index + 1 === length || codeUnits.length > MAX_SIZE) {
result += stringFromCharCode.apply(null, codeUnits);
codeUnits.length = 0;
}
}
return result;
}n/a
isCombining = function (str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
return exports.combining[point] === true;
}n/a
isSurrogate = function (str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
return point > 0x00ffff;
}n/a
strWidth = function (str) {
var width = 0;
for (var i = 0; i < str.length; i++) {
width += exports.charWidth(str, i);
if (exports.isSurrogate(str, i)) i++;
}
return width;
}n/a
function Video(options) {
var self = this
, shell
, args;
if (!(this instanceof Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}n/a
exists = function (program) {
try {
return !!+cp.execSync('type '
+ program + ' > /dev/null 2> /dev/null'
+ ' && echo 1', { encoding: 'utf8' }).trim();
} catch (e) {
return false;
}
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function BigText(options) {
if (!(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}n/a
function Box(options) {
if (!(this instanceof Node)) {
return new Box(options);
}
options = options || {};
Element.call(this, options);
}n/a
function Button(options) {
var self = this;
if (!(this instanceof Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}n/a
function Checkbox(options) {
var self = this;
if (!(this instanceof Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
function FileManager(options) {
var self = this;
if (!(this instanceof Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', file));
}
self.refresh();
} else {
self.emit('file', file);
}
});
});
}n/a
function Form(options) {
var self = this;
if (!(this instanceof Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}n/a
function Image(options) {
if (!(this instanceof Node)) {
return new Image(options);
}
options = options || {};
options.type = options.itype || options.type || 'ansi';
Box.call(this, options);
if (options.type === 'ansi' && this.type !== 'ansiimage') {
var ANSIImage = require('./ansiimage');
Object.getOwnPropertyNames(ANSIImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ANSIImage.prototype, key));
}, this);
ANSIImage.call(this, options);
return this;
}
if (options.type === 'overlay' && this.type !== 'overlayimage') {
var OverlayImage = require('./overlayimage');
Object.getOwnPropertyNames(OverlayImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(OverlayImage.prototype, key));
}, this);
OverlayImage.call(this, options);
return this;
}
throw new Error('`type` must either be `ansi` or `overlay`.');
}n/a
function Input(options) {
if (!(this instanceof Node)) {
return new Input(options);
}
options = options || {};
Box.call(this, options);
}n/a
function Layout(options) {
if (!(this instanceof Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}n/a
function Line(options) {
if (!(this instanceof Node)) {
return new Line(options);
}
options = options || {};
var orientation = options.orientation || 'vertical';
delete options.orientation;
if (orientation === 'vertical') {
options.width = 1;
} else {
options.height = 1;
}
Box.call(this, options);
this.ch = !options.type || options.type === 'line'
? orientation === 'horizontal' ? '─' : '│'
: options.ch || ' ';
this.border = {
type: 'bg',
__proto__: this
};
this.style.border = this.style;
}n/a
function List(options) {
var self = this;
if (!(this instanceof Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(sel ...n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function ListTable(options) {
var self = this;
if (!(this instanceof Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function Loading(options) {
if (!(this instanceof Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}n/a
function Log(options) {
var self = this;
if (!(this instanceof Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this.on('set content', function() {
if (!self._userScrolled || self.scrollOnInput) {
nextTick(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self.screen.render();
});
}
});
}n/a
function Message(options) {
if (!(this instanceof Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}n/a
function Node(options) {
var self = this;
var Screen = require('./screen');
if (!(this instanceof Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}n/a
function OverlayImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function ProgressBar(options) {
var self = this;
if (!(this instanceof Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}n/a
function Prompt(options) {
if (!(this instanceof Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function Question(options) {
if (!(this instanceof Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.okay = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function RadioButton(options) {
var self = this;
if (!(this instanceof Node)) {
return new RadioButton(options);
}
options = options || {};
Checkbox.call(this, options);
this.on('check', function() {
var el = self;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self) {
return;
}
el.uncheck();
});
});
}n/a
function RadioSet(options) {
if (!(this instanceof Node)) {
return new RadioSet(options);
}
options = options || {};
// Possibly inherit parent's style.
// options.style = this.parent.style;
Box.call(this, options);
}n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}n/a
function ScrollableBox(options) {
var self = this;
if (!(this instanceof Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll( ...n/a
function ScrollableText(options) {
if (!(this instanceof Node)) {
return new ScrollableText(options);
}
options = options || {};
options.alwaysScroll = true;
ScrollableBox.call(this, options);
}n/a
function Table(options) {
var self = this;
if (!(this instanceof Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}n/a
function Terminal(options) {
if (!(this instanceof Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = false;
Box.call(this, options);
// XXX Workaround for all motion
if (this.screen.program.tmux && this.screen.program.tmuxVersion >= 2) {
this.screen.program.enableMouse();
}
this.handler = options.handler;
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
this.termName = options.terminal
|| options.term
|| process.env.TERM
|| 'xterm';
this.bootstrap();
}n/a
function Text(options) {
if (!(this instanceof Node)) {
return new Text(options);
}
options = options || {};
options.shrink = true;
Element.call(this, options);
}n/a
function Textarea(options) {
var self = this;
if (!(this instanceof Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}n/a
function Textbox(options) {
if (!(this instanceof Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = false;
Textarea.call(this, options);
this.secret = options.secret;
this.censor = options.censor;
}n/a
function Video(options) {
var self = this
, shell
, args;
if (!(this instanceof Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function BigText(options) {
if (!(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}n/a
function Box(options) {
if (!(this instanceof Node)) {
return new Box(options);
}
options = options || {};
Element.call(this, options);
}...
var screen = blessed.screen({
smartCSR: true
});
screen.title = 'my window title';
// Create a box perfectly centered horizontally and vertically.
var box = blessed.box({
top: 'center',
left: 'center',
width: '50%',
height: '50%',
content: 'Hello {bold}world{/bold}!',
tags: true,
border: {
...function Button(options) {
var self = this;
if (!(this instanceof Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}n/a
function Checkbox(options) {
var self = this;
if (!(this instanceof Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}n/a
function Element(options) {
var self = this;
if (!(this instanceof Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = require('./scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.fixed = options.fixed;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
| ...n/a
function FileManager(options) {
var self = this;
if (!(this instanceof Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', file));
}
self.refresh();
} else {
self.emit('file', file);
}
});
});
}n/a
function Form(options) {
var self = this;
if (!(this instanceof Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}n/a
function Image(options) {
if (!(this instanceof Node)) {
return new Image(options);
}
options = options || {};
options.type = options.itype || options.type || 'ansi';
Box.call(this, options);
if (options.type === 'ansi' && this.type !== 'ansiimage') {
var ANSIImage = require('./ansiimage');
Object.getOwnPropertyNames(ANSIImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ANSIImage.prototype, key));
}, this);
ANSIImage.call(this, options);
return this;
}
if (options.type === 'overlay' && this.type !== 'overlayimage') {
var OverlayImage = require('./overlayimage');
Object.getOwnPropertyNames(OverlayImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(OverlayImage.prototype, key));
}, this);
OverlayImage.call(this, options);
return this;
}
throw new Error('`type` must either be `ansi` or `overlay`.');
}...
}
});
// Append our box to the screen.
screen.append(box);
// Add a png icon to the box
var icon = blessed.image({
parent: box,
top: 0,
left: 0,
type: 'overlay',
width: 'shrink',
height: 'shrink',
file: __dirname + '/my-program-icon.png',
...function Input(options) {
if (!(this instanceof Node)) {
return new Input(options);
}
options = options || {};
Box.call(this, options);
}n/a
function Layout(options) {
if (!(this instanceof Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}...
Here is an example of how to provide a renderer. Note that this is also the
default renderer if none is provided. This renderer will render each child as
though they were `display: inline-block;` in CSS, as if there were a
dynamically sized horizontal grid from left to right.
``` js
var layout = blessed.layout({
parent: screen,
top: 'center',
left: 'center',
width: '50%',
height: '50%',
border: 'line',
style: {
...function Line(options) {
if (!(this instanceof Node)) {
return new Line(options);
}
options = options || {};
var orientation = options.orientation || 'vertical';
delete options.orientation;
if (orientation === 'vertical') {
options.width = 1;
} else {
options.height = 1;
}
Box.call(this, options);
this.ch = !options.type || options.type === 'line'
? orientation === 'horizontal' ? '─' : '│'
: options.ch || ' ';
this.border = {
type: 'bg',
__proto__: this
};
this.style.border = this.style;
}n/a
function List(options) {
var self = this;
if (!(this instanceof Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(sel ...n/a
function Listbar(options) {
var self = this;
if (!(this instanceof Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}n/a
function ListTable(options) {
var self = this;
if (!(this instanceof Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}n/a
function Loading(options) {
if (!(this instanceof Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}n/a
function Log(options) {
var self = this;
if (!(this instanceof Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this.on('set content', function() {
if (!self._userScrolled || self.scrollOnInput) {
nextTick(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self.screen.render();
});
}
});
}...
left: '45%+1',
...
```
To access the calculated offsets, relative to the parent:
``` js
console.log(box.left);
console.log(box.top);
```
To access the calculated offsets, absolute (relative to the screen):
``` js
console.log(box.aleft);
...function Message(options) {
if (!(this instanceof Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}n/a
function Node(options) {
var self = this;
var Screen = require('./screen');
if (!(this instanceof Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}n/a
function OverlayImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}n/a
function ANSIImage(options) {
var self = this;
if (!(this instanceof Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}n/a
function ProgressBar(options) {
var self = this;
if (!(this instanceof Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}n/a
function Prompt(options) {
if (!(this instanceof Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function Question(options) {
if (!(this instanceof Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.okay = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: 2,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}n/a
function RadioButton(options) {
var self = this;
if (!(this instanceof Node)) {
return new RadioButton(options);
}
options = options || {};
Checkbox.call(this, options);
this.on('check', function() {
var el = self;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self) {
return;
}
el.uncheck();
});
});
}n/a
function RadioSet(options) {
if (!(this instanceof Node)) {
return new RadioSet(options);
}
options = options || {};
// Possibly inherit parent's style.
// options.style = this.parent.style;
Box.call(this, options);
}n/a
function Screen(options) {
var self = this;
if (!(this instanceof Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}...
`blessed.screen` option. This will enable CSR when scrolling text in elements
or when manipulating lines.
``` js
var blessed = require('blessed');
// Create a screen object.
var screen = blessed.screen({
smartCSR: true
});
screen.title = 'my window title';
// Create a box perfectly centered horizontally and vertically.
var box = blessed.box({
...function ScrollableBox(options) {
var self = this;
if (!(this instanceof Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll( ...n/a
function ScrollableText(options) {
if (!(this instanceof Node)) {
return new ScrollableText(options);
}
options = options || {};
options.alwaysScroll = true;
ScrollableBox.call(this, options);
}n/a
function Table(options) {
var self = this;
if (!(this instanceof Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}n/a
function Terminal(options) {
if (!(this instanceof Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = false;
Box.call(this, options);
// XXX Workaround for all motion
if (this.screen.program.tmux && this.screen.program.tmuxVersion >= 2) {
this.screen.program.enableMouse();
}
this.handler = options.handler;
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
this.termName = options.terminal
|| options.term
|| process.env.TERM
|| 'xterm';
this.bootstrap();
}n/a
function Text(options) {
if (!(this instanceof Node)) {
return new Text(options);
}
options = options || {};
options.shrink = true;
Element.call(this, options);
}n/a
function Textarea(options) {
var self = this;
if (!(this instanceof Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}n/a
function Textbox(options) {
if (!(this instanceof Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = false;
Textarea.call(this, options);
this.secret = options.secret;
this.censor = options.censor;
}n/a
function Video(options) {
var self = this
, shell
, args;
if (!(this instanceof Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}n/a