ncurses
Version:
An ncurses binding for node.js
1,014 lines (963 loc) • 35.8 kB
JavaScript
var nc = require('..');
var origColors;
function backupColors() {
if (!nc.hasColors)
return;
if (!origColors) {
origColors = new Array(nc.maxColorPairs);
for (var i=1; i<nc.maxColorPairs-1; i++)
origColors[i] = [nc.colorFg(nc.colorPair(i)), nc.colorBg(nc.colorPair(i))];
} else {
for (var i=1; i<nc.maxColorPairs-1; i++) {
origColors[i][0] = nc.colorFg(nc.colorPair(i));
origColors[i][1] = nc.colorBg(nc.colorPair(i));
}
}
}
function restoreColors() {
if (!nc.hasColors)
return;
for (var i=1; i<nc.maxColorPairs-1; i++)
nc.colorPair(i, origColors[i][0], origColors[i][1]);
}
function times(str, num) {
var ret = '';
for (var i=0; i<num; i++)
ret += str;
return ret;
}
function pad(str, which, amount, padchar) {
padchar = padchar || ' ';
amount -= str.length;
if (amount > 0) {
var nbefore = 0, nafter = 0;
if (which !== 'left') {
if (which === 'center') {
nbefore = Math.floor(amount/2);
nafter = amount - nbefore;
} else
nbefore = amount;
}
str = times(padchar, nbefore) + str + times(padchar, nafter);
}
return str;
}
var Popup = function(height, width, position, title) {
var win, pos;
pos = position || 'center';
pos = parsePosition(height, width, pos);
win = new nc.Window(height, width, pos[0], pos[1]);
if (title)
win.frame(title);
else if (title !== null)
win.frame();
return win;
};
exports.MessageBox = function(text, options, cb) {
backupColors();
var win, lines = text.split('\n'), btnStartCol, selection,
width = 0, height, buttons, textAlign,
style = {
colors: {
fg: undefined,
bg: undefined,
button: {
fg: undefined,
bg: undefined
},
hl: {
fg: undefined,
bg: undefined
}
}
}, fnDraw;
if (!cb && typeof options === 'function')
cb = options;
if (typeof options !== 'object')
options = {};
buttons = (options.buttons && !Array.isArray(options.buttons) ? [options.buttons] : options.buttons);
textAlign = options.textAlign || 'center';
selection = options.default || 0;
height = lines.length+2+(buttons ? 2 : 0);
extend(true, style, options.style);
// Fit to the content
for (var i=0,len=lines.length; i<len; i++)
if (lines[i].length > width)
width = lines[i].length;
if (buttons) {
if (buttons.length === 1)
selection = 0;
else if (typeof selection === 'string')
selection = buttons.indexOf(selection);
if (buttons.join(' ').length > width)
width = buttons.join(' ').length;
btnStartCol = Math.floor((width-buttons.join(' ').length)/2)+2;
}
width += 4;
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
win = Popup(height, width, options.pos, options.title);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
if (!style.colors.button)
style.colors.button = {};
style.colors.button.fg = parseColor(style.colors.button.fg, style.colors.fg);
style.colors.button.bg = parseColor(style.colors.button.bg, style.colors.bg);
if (!style.colors.hl)
style.colors.hl = {};
style.colors.hl.fg = parseColor(style.colors.hl.fg, style.colors.bg);
style.colors.hl.bg = parseColor(style.colors.hl.bg, style.colors.fg);
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
nc.colorPair(nc.maxColorPairs-2, style.colors.button.fg, style.colors.button.bg);
nc.colorPair(nc.maxColorPairs-3, style.colors.hl.fg, style.colors.hl.bg);
win.attrset(nc.colorPair(nc.maxColorPairs-1));
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
}
win.on('inputChar', function(c, i) {
if ((i === 9 || i === nc.keys.LEFT || i === nc.keys.RIGHT
|| i === nc.keys.UP || i === nc.keys.DOWN)
&& Array.isArray(buttons) && buttons.length > 1) {
var col = btnStartCol+buttons.slice(0, selection).join(' ').length+(selection > 0 ? 2 : 0);
if (nc.hasColors)
win.chgat(height-2, col, buttons[selection].length, nc.attrs.NORMAL, nc.maxColorPairs-2);
else
win.chgat(height-2, col, buttons[selection].length, nc.attrs.NORMAL);
if (i === 9 || i === nc.keys.RIGHT || i === nc.keys.UP)
selection++;
else
selection--;
if (selection === buttons.length)
selection = 0;
else if (selection < 0)
selection = buttons.length-1;
col = btnStartCol+buttons.slice(0, selection).join(' ').length+(selection > 0 ? 2 : 0);
if (nc.hasColors)
win.chgat(height-2, col, buttons[selection].length, nc.attrs.NORMAL, nc.maxColorPairs-3);
else
win.chgat(height-2, col, buttons[selection].length, nc.attrs.REVERSE);
win.refresh();
return;
} else if (i === nc.keys.ESC || (i === nc.keys.NEWLINE && !buttons))
selection = undefined;
else if (i === nc.keys.NEWLINE && buttons)
selection = buttons[selection];
else
return;
win.close();
restoreColors();
if (cb)
process.nextTick(function(){ cb(selection); });
//process.removeListener('SIGWINCH', fnDraw);
});
}
var ln = 1;
for (var i=0,len=lines.length; i<len; i++,ln++) {
lines[i] = pad(lines[i], textAlign, width-4);
win.addstr(ln, 2, lines[i]);
}
if (buttons) {
var col = btnStartCol+buttons.slice(0, selection).join(' ').length+(selection > 0 ? 2 : 0);
win.hline(ln++, 1, width-2);
win.addstr(ln, 2, pad(buttons.join(' '), 'center', width-4));
if (nc.hasColors) {
for (var i=0; i<buttons.length; i++) {
win.chgat(ln, col, buttons[i].length, nc.attrs.NORMAL, nc.maxColorPairs-(i === 0 ? 3 : 2));
col += buttons[i].length + 2;
}
} else
win.chgat(ln++, col, buttons[selection].length, nc.attrs.REVERSE);
}
win.refresh();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
exports.InputBox = function(text, options, cb) {
backupColors();
var win, lines = text.split('\n'), buffer = "", old_curs = nc.showCursor,
width = 0, height = lines.length+4, textAlign,
style = {
colors: {
fg: undefined,
bg: undefined,
input: {
fg: undefined,
bg: undefined
}
}
}, fnDraw;
if (!cb && typeof options === 'function')
cb = options;
if (typeof options !== 'object')
options = {};
textAlign = options.textAlign || 'center';
extend(true, style, options.style);
// Fit to the content
for (var i=0,len=lines.length; i<len; i++)
if (lines[i].length > width)
width = lines[i].length;
width += 4;
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
win = Popup(height, width, options.pos);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
if (!style.colors.input)
style.colors.input = {};
style.colors.input.fg = parseColor(style.colors.input.fg, style.colors.bg);
style.colors.input.bg = parseColor(style.colors.input.bg, style.colors.fg);
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
nc.colorPair(nc.maxColorPairs-2, style.colors.input.fg, style.colors.input.bg);
win.attrset(nc.colorPair(nc.maxColorPairs-1));
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
}
win.on('inputChar', function(c, i) {
if (i === nc.keys.ESC)
buffer = undefined;
else if (i !== nc.keys.NEWLINE) {
if (i === nc.keys.BACKSPACE && win.curx > 1) {
var prev_x = win.curx-1;
win.delch(height-2, prev_x);
buffer = buffer.substring(0, prev_x-1) + buffer.substr(prev_x);
win.insch(height-2, width-2, 32);
win.cursor(height-2, prev_x);
} else if (i === nc.keys.DEL && win.curx > 0 && win.curx < width-1) {
var prev_x = win.curx;
win.delch(height-2, win.curx);
buffer = buffer.substring(0, win.curx-1) + buffer.substr(win.curx);
win.insch(height-2, width-2, 32);
win.cursor(height-2, prev_x);
} else if (i === nc.keys.LEFT && win.curx > 1)
win.cursor(height-2, win.curx-1);
else if (i === nc.keys.RIGHT && win.curx < buffer.length+1)
win.cursor(height-2, win.curx+1);
else if (i === nc.keys.END)
win.cursor(height-2, buffer.length+1);
else if (i === nc.keys.HOME)
win.cursor(height-2, 1);
else if (i >= 32 && i <= 126 && win.curx < width-1) {
win.echochar(i);
buffer += c;
}
if (nc.hasColors) {
var x = win.curx;
win.chgat(height-2, 1, width-2, nc.attrs.NORMAL, nc.maxColorPairs-1);
win.chgat(height-2, 1, buffer.length, nc.attrs.NORMAL, nc.maxColorPairs-2);
win.cursor(height-2, x);
}
win.refresh();
return;
}
nc.showCursor = old_curs;
win.close();
restoreColors();
if (cb)
process.nextTick(function(){ cb(buffer); });
//process.removeListener('SIGWINCH', fnDraw);
});
}
var ln = 1;
for (var i=0,len=lines.length; i<len; i++,ln++) {
lines[i] = pad(lines[i], textAlign, width-4);
win.addstr(ln, 2, lines[i]);
}
win.hline(ln++, 1, width-2);
win.cursor(height-2, 1);
nc.showCursor = true;
if (options.title)
win.frame(options.title);
else
win.frame();
nc.redraw();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
var months = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'],
dow = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
numdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
exports.Calendar = function(options, cb) {
backupColors();
var win, width = 24, height = 11, highlight, firstDow, maxday,
style = {
colors: {
fg: undefined,
bg: undefined,
hl: {
fg: undefined,
bg: undefined
},
today: {
fg: undefined,
bg: undefined
}
}
}, fnDraw;
if (!cb && typeof options === 'function')
cb = options;
if (typeof options !== 'object')
options = {};
highlight = options.initialDate || (new Date);
extend(true, style, options.style);
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
win = Popup(height, width, options.pos);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
if (!style.colors.hl)
style.colors.hl = {};
style.colors.hl.fg = parseColor(style.colors.hl.fg, style.colors.bg);
style.colors.hl.bg = parseColor(style.colors.hl.bg, style.colors.fg);
if (!style.colors.today)
style.colors.today = {};
style.colors.today.fg = parseColor(style.colors.today.fg, style.colors.bg);
style.colors.today.bg = parseColor(style.colors.today.bg, style.colors.fg);
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
nc.colorPair(nc.maxColorPairs-2, style.colors.hl.fg, style.colors.hl.bg);
nc.colorPair(nc.maxColorPairs-3, style.colors.today.fg, style.colors.today.bg);
win.attrset(nc.colorPair(nc.maxColorPairs-1));
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
}
win.on('inputChar', function(c, i) {
if (i === nc.keys.ESC)
highlight = undefined;
else if (i !== nc.keys.NEWLINE) {
var dir = (i === nc.keys.LEFT || i === nc.keys.UP || i === nc.keys.PPAGE ? -1 : 1);
if (i === nc.keys.LEFT || i === nc.keys.RIGHT)
highlight = new Date(+highlight + (86400000*dir));
else if (i === nc.keys.UP || i === nc.keys.DOWN)
highlight = new Date(+highlight + (604800000*dir));
else if (i === nc.keys.PPAGE || i === nc.keys.NPAGE) {
var year = highlight.getFullYear(), month = highlight.getMonth()+dir,
day = highlight.getDate();
if (month < 0) {
month = 11;
year--;
} else if (month > 11) {
month = 0;
year++;
}
maxday = numdays[month];
if (month === 1 && year % 4 === 0)
maxday++; // leap year
if (day > maxday)
day = maxday;
highlight = new Date((month+1) + '/' + day + '/' + year);
} else
return;
if (dir !== 0)
fnDraw();
else
win.refresh();
return;
}
win.close();
restoreColors();
if (cb)
process.nextTick(function(){ cb(highlight); });
//process.removeListener('SIGWINCH', fnDraw);
});
}
var ln = 1;
win.erase();
maxday = numdays[highlight.getMonth()];
if (highlight.getMonth() === 1 && highlight.getFullYear() % 4 === 0)
maxday++; // leap year
win.addstr(ln, 2, months[highlight.getMonth()] + ' ' + highlight.getDate());
win.addstr(ln++, width-6, ''+highlight.getFullYear());
win.addstr(ln++, 2, dow.join(' '));
ln++;
firstDow = new Date((highlight.getMonth()+1) + '/1/' + highlight.getFullYear()).getDay();
var today = new Date;
for (var pos=(firstDow*3)+2,day=1,curday=highlight.getDate(); ln<height-1; ln++,pos=2) {
while (pos < 23 && day <= maxday) {
win.addstr(ln, pos, pad(''+(day), 'right', 2, '0'));
if (today.getFullYear() === highlight.getFullYear()
&& today.getMonth() === highlight.getMonth()
&& today.getDate() === day) {
if (nc.hasColors)
win.chgat(ln, pos, 2, nc.attrs.NORMAL, nc.maxColorPairs-3);
else
win.chgat(ln, pos, 2, nc.attrs.REVERSE);
}
if (day++ === curday) {
if (nc.hasColors)
win.chgat(ln, pos, 2, nc.attrs.NORMAL, nc.maxColorPairs-2);
else
win.chgat(ln, pos, 2, nc.attrs.REVERSE);
}
pos += 3;
}
}
if (options.title)
win.frame(options.title);
else
win.frame();
win.refresh();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
exports.Viewer = function(text, options, cb) {
backupColors();
var win, lines = text.split('\n'), numlines = lines.length,
width = 2, height = 2,
style = {
colors: {
fg: undefined,
bg: undefined,
}
}, top = 1, textAreaHeight, curline = 0, curcol = 0, fnDraw,
maxlinewidth = lines.reduce(function(a,b){return Math.max(a,b.length)},0);
if (!cb && typeof options === 'function')
cb = options;
if (typeof options !== 'object')
options = {};
extend(true, style, options.style);
if (options.height)
height = options.height;
if (options.width)
width = options.width;
if (style.linecount)
top = 2;
textAreaHeight = height - 1 - top;
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
win = Popup(height, width, options.pos, options.title);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
win.attrset(nc.colorPair(nc.maxColorPairs-1));
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
}
win.on('inputChar', function(c, i) {
var old_x = win.curx, old_y = win.cury;
if (i !== nc.keys.ESC) {
var dir = (i === nc.keys.LEFT || i === nc.keys.UP || i === nc.keys.PPAGE ? -1 : 1);
if (i === nc.keys.HOME) {
curcol = 0;
fnDraw();
return;
} else if (i === nc.keys.END) {
if (maxlinewidth > (width-2)) {
curcol = maxlinewidth - (width-2);
fnDraw();
return;
}
} else if ((i === nc.keys.LEFT && curcol+dir >= 0) || (i === nc.keys.RIGHT && curcol+(width-2)+dir <= maxlinewidth)) {
curcol += dir;
for (var i=0,curs=top,ln=curline; i<Math.min(numlines, textAreaHeight); i++,ln++,curs++) {
win.cursor(curs, 1);
win.clrtoeol();
win.print(lines[ln].substring(curcol, curcol+(width-2)));
}
} else if ((i === nc.keys.UP && curline+dir >= 0) || (i === nc.keys.DOWN && curline+textAreaHeight+dir <= numlines)) {
curline += dir;
var which = curline;
win.cursor(top, 1);
if (dir < 0)
win.insertln();
else {
win.deleteln();
win.cursor(win.height-2, 1);
win.clrtoeol();
which = curline+textAreaHeight-1;
}
win.print(lines[which].substring(curcol, curcol+(width-2)));
} else if (i === nc.keys.PPAGE || i === nc.keys.NPAGE) {
var numscroll = textAreaHeight;
win.cursor(top, 1);
if (dir < 0) {
if (curline - numscroll < 0)
numscroll = curline;
if (numscroll > 0) {
win.insdelln(numscroll);
for (var i=top,len=i+numscroll,ln=curline-numscroll; i<=numscroll+(style.linecount ? 1 : 0); i++,ln++) {
win.cursor(i, 1);
win.clrtoeol();
win.print(lines[ln].substring(curcol, curcol+(width-2)));
}
}
} else {
if ((curline + textAreaHeight) + numscroll > numlines)
numscroll = numlines - (curline + textAreaHeight);
if (numscroll > 0) {
win.insdelln(-numscroll);
for (var len=height-1,i=len-numscroll,ln=curline+textAreaHeight; i<len; i++,ln++) {
win.cursor(i, 1);
win.clrtoeol();
win.print(lines[ln].substring(curcol, curcol+(width-2)));
}
}
}
if (numscroll > 0)
curline += numscroll*dir;
} else
return;
if (style.linecount) {
win.cursor(1, 1);
win.clrtoeol();
win.centertext(1, 'Lines '+ (curline+1) + '-' + (curline+textAreaHeight)
+ ' of ' + numlines + ' ('
+ Math.ceil(((curline+textAreaHeight)/numlines)*100)
+ '%)'
);
}
if (options.title)
win.frame(options.title);
else
win.frame();
win.refresh();
return;
}
win.close();
restoreColors();
if (cb)
process.nextTick(function(){ cb(); });
//process.removeListener('SIGWINCH', fnDraw);
});
}
if (style.linecount) {
win.cursor(1, 1);
win.clrtoeol();
win.centertext(1, 'Lines '+ (curline+1) + '-' + (curline+textAreaHeight)
+ ' of ' + numlines + ' ('
+ Math.ceil(((curline+textAreaHeight)/numlines)*100)
+ '%)'
);
}
for (var i=0,curs=top,ln=curline,len=Math.min(numlines, textAreaHeight); i<len; i++,curs++,ln++) {
win.cursor(curs, 1);
win.clrtoeol();
win.print(lines[ln].substring(curcol, curcol+(width-2)));
}
if (options.title)
win.frame(options.title);
else
win.frame();
win.refresh();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
exports.ListBox = function(items, options, cb, cbHover, cbSelect) {
backupColors();
var values;
if (!Array.isArray(items)) {
values = Object.keys(items).map(function(x){return items[x]});
items = Object.keys(items);
}
var win, numitems = items.length, highlight = 0, itemAreaHeight, itemAreaWidth,
maxitemwidth = items.reduce(function(a,b){return Math.max(a,b.length)},0),
width, height, top = 1, curwndline = 1, topline = 0, selection, old_curs = nc.showCursor,
style = {
colors: {
fg: undefined,
bg: undefined,
hl: {
fg: undefined,
bg: undefined
},
sel: {
fg: undefined,
bg: undefined
}
}
}, autoColors = [], fnDraw;
if (!cb && typeof options === 'function')
cb = options;
if (typeof options !== 'object')
options = {};
width = options.width || (maxitemwidth+2);
if (options.title && options.title.length > width)
width = options.title.length;
height = options.height || 3;
itemAreaHeight = height-2;
itemAreaWidth = width-2;
extend(true, style, options.style);
if (options.multi)
selection = {};
nc.showCursor = false;
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
win = Popup(height, width, options.pos);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
if (!style.colors.hl)
style.colors.hl = {};
style.colors.hl.fg = parseColor(style.colors.hl.fg, style.colors.bg);
style.colors.hl.bg = parseColor(style.colors.hl.bg, style.colors.fg);
if (!style.colors.sel)
style.colors.sel = {};
style.colors.sel.fg = parseColor(style.colors.sel.fg, style.colors.bg);
style.colors.sel.bg = parseColor(style.colors.sel.bg, style.colors.fg);
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
nc.colorPair(nc.maxColorPairs-2, style.colors.hl.fg, style.colors.hl.bg);
nc.colorPair(nc.maxColorPairs-3, style.colors.sel.fg, style.colors.sel.bg);
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
var attrs = nc.colorPair(nc.maxColorPairs-1);
if (style.bold === true)
attrs |= nc.attrs.BOLD;
if (style.underline === true)
attrs |= nc.attrs.UNDERLINE;
win.attrset(attrs);
}
win.on('inputChar', function(c, i) {
if (i === nc.keys.ESC)
highlight = undefined;
else if (i === nc.keys.NEWLINE)
highlight = (Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight]);
else if (i === nc.keys.SPACE && options.multi) {
if (selection[highlight]) {
if (cbSelect)
process.nextTick(function(){cbSelect(Array.isArray(selection[highlight]) ? selection[highlight][0] : selection[highlight], false)});
delete selection[highlight];
} else {
if (cbSelect)
process.nextTick(function(){cbSelect(Array.isArray(selection[highlight]) ? selection[highlight][0] : selection[highlight], true)});
selection[highlight] = true;
}
fnDraw();
return;
} else {
var dir = (i === nc.keys.LEFT || i === nc.keys.UP || i === nc.keys.PPAGE ? -1 : 1);
if (i === nc.keys.HOME) {
if (highlight !== 0 && cbHover)
process.nextTick(function(){cbHover(Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight])});
win.chgat(curwndline, 1, itemAreaWidth, nc.attrs.NORMAL);
topline = 0;
curwndline = 1;
highlight = 0;
} else if (i === nc.keys.END) {
var old_hl = highlight;
win.chgat(curwndline, 1, itemAreaWidth, nc.attrs.NORMAL);
if (numitems-itemAreaHeight < 0)
topline = 0;
else
topline = numitems-itemAreaHeight;
highlight = numitems-1;
curwndline = (highlight-topline)+1;
if (highlight !== old_hl && cbHover)
process.nextTick(function(){cbHover(Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight])});
} else if ((i === nc.keys.UP && highlight+dir >= 0) || (i === nc.keys.DOWN && highlight+dir < numitems)) {
highlight += dir;
if (cbHover)
process.nextTick(function(){cbHover(Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight])});
win.chgat(curwndline, 1, itemAreaWidth, nc.attrs.NORMAL);
if (curwndline + dir >= top && curwndline + dir < height - 1)
curwndline += dir;
else {
var which = highlight;
topline += dir;
}
} else if (i === nc.keys.PPAGE || i === nc.keys.NPAGE) {
win.chgat(curwndline, 1, itemAreaWidth, nc.attrs.NORMAL);
var numscroll = itemAreaHeight, old_hl = highlight;
highlight += numscroll*dir;
if (highlight >= numitems) {
highlight = numitems-1;
topline = (numitems-itemAreaHeight < 0 ? 0 : numitems-itemAreaHeight);
curwndline = (highlight-topline)+1;
} else if (highlight < 0) {
highlight = 0;
topline = 0;
curwndline = 1;
} else {
topline += numscroll*dir;
if (topline < 0)
topline = 0;
else if (topline > (numitems-itemAreaHeight) && topline <= (numitems-1))
topline = numitems-itemAreaHeight;
curwndline = (highlight-topline)+1;
}
if (old_hl !== highlight && cbHover)
process.nextTick(function(){cbHover(Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight])});
} else
return;
fnDraw();
return;
}
if (options.multi) {
selection = Object.keys(selection).map(function(x) {
var val = items[x];
if (Array.isArray(val))
val = val[0];
if (values)
val = values[x];
return val;
});
if (!selection.length)
selection = undefined;
} else {
selection = highlight;
if (values)
selection = values[items.indexOf(selection)];
}
nc.showCursor = old_curs;
win.close();
restoreColors();
if (cb)
process.nextTick(function(){ cb(selection); });
//process.removeListener('SIGWINCH', fnDraw);
});
}
for (var i=0,curs=top,ln=topline,txt,len=Math.min(numitems, itemAreaHeight); i<len; i++,curs++,ln++) {
txt = (Array.isArray(items[ln]) ? items[ln][0] : items[ln]);
win.cursor(curs, 1);
win.clrtoeol();
win.addstr(txt, Math.min(txt.length, width-2));
if (ln === highlight || (options.multi && selection[ln])) {
if (nc.hasColors) {
if (options.multi && selection[ln])
win.chgat(curs, 1, -1, nc.attrs.NORMAL, nc.maxColorPairs-3);
if (ln === highlight)
win.chgat(curs, 1, -1, nc.attrs.NORMAL, nc.maxColorPairs-2);
} else
win.chgat(curs, 1, -1, nc.attrs.REVERSE);
} else if (nc.hasColors && Array.isArray(items[ln]) && items[ln].length === 2
&& typeof items[ln][1] === 'object') {
var fg = style.colors.fg, bg = style.colors.bg;
if (typeof items[ln][1].fg !== 'undefined')
fg = parseColor(items[ln][1].fg);
if (typeof items[ln][1].bg !== 'undefined')
bg = parseColor(items[ln][1].bg);
var found = false, pair = 1;
for (var c=0; c<autoColors.length; c++) {
if (autoColors[c].fg === fg && autoColors[c].bg === bg) {
found = true;
pair = (nc.maxColorPairs-4)-c;
break;
}
}
if (!found && autoColors.length < nc.maxColorPairs) {
autoColors.push({fg: fg, bg: bg});
pair = (nc.maxColorPairs-4)-(autoColors.length-1);
nc.colorPair(pair, fg, bg);
}
win.chgat(curs, 1, -1, nc.attrs.NORMAL, pair);
}
}
if (numitems && cbHover)
process.nextTick(function(){cbHover(Array.isArray(items[highlight]) ? items[highlight][0] : items[highlight])});
if (options.title)
win.frame(options.title);
else
win.frame();
win.refresh();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
exports.Marquee = function(text, options) {
backupColors();
var self = this, win, dir, width, delay, idx, textlen,
style = {
colors: {
fg: undefined,
bg: undefined
}
}, fnDraw, fnAnimate;
if (typeof options !== 'object')
options = {};
delay = options.delay || 250; // ms
dir = options.dir || 'left';
width = options.width || nc.cols;
if (dir === 'left') {
text = times(' ', width) + text + ' ';
idx = 0;
} else {
text = times(' ', width) + text;
idx = text.length-1;
}
textlen = text.length;
this._tmr = undefined;
extend(true, style, options.style);
fnDraw = function() {
var isRedraw = (win ? true : false);
if (isRedraw) {
//win.move(boxY, boxX);
//nc.redraw();
} else {
self.win = win = Popup(1, width, options.pos, null);
if (nc.hasColors) {
if (!style.colors)
style.colors = {};
style.colors.fg = parseColor(style.colors.fg, nc.colorFg(nc.colorPair(0)));
style.colors.bg = parseColor(style.colors.bg, nc.colorBg(nc.colorPair(0)));
nc.colorPair(nc.maxColorPairs-1, style.colors.fg, style.colors.bg);
win.bkgd = ' '|nc.colorPair(nc.maxColorPairs-1);
var attrs = nc.colorPair(nc.maxColorPairs-1);
if (style.bold === true)
attrs |= nc.attrs.BOLD;
if (style.underline === true)
attrs |= nc.attrs.UNDERLINE;
win.attrset(attrs);
}
/*win.on('inputChar', function(c, i) {
clearInterval(self._tmr);
win.close();
//process.removeListener('SIGWINCH', fnDraw);
});*/
self._tmr = setInterval(fnAnimate, delay);
}
win.refresh();
};
fnAnimate = function() {
if (dir === 'left') {
if (idx++ === textlen)
idx = 0;
win.addstr(0, 0, text.substring(idx, idx+width));
} else {
if (idx-- === 0)
idx = textlen-1;
win.addstr(0, 0, text.substring(idx));
}
win.refresh();
};
fnDraw();
//process.on('SIGWINCH', fnDraw);
};
exports.Marquee.prototype.stop = function() {
if (this._tmr) {
clearInterval(this._tmr);
this.win.close();
restoreColors();
this._tmr = undefined;
}
};
function parseColor(color, def) {
var ret = undefined;
if (typeof color === 'string') {
if (typeof nc.colors[color.toUpperCase()] !== 'undefined')
ret = nc.colors[color.toUpperCase()];
else {
try {
ret = parseInt(color);
} catch (err) {}
}
} else if (typeof color === 'number')
ret = color;
else if (typeof def !== 'undefined')
ret = def;
return ret;
}
function parsePosition(height, width, pos) {
var boxY = 0, boxX = 0;
if (typeof pos === 'string') {
if (pos === 'top' || pos === 'topleft' || pos === 'topright')
boxY = 0;
else if (pos === 'bottom' || pos === 'bottomleft' || pos === 'bottomright')
boxY = nc.lines-height;
else
boxY = Math.floor(nc.lines/2)-Math.floor(height/2);
if (pos === 'left' || pos === 'topleft' || pos === 'bottomleft')
boxX = 0;
else if (pos === 'right' || pos === 'topright' || pos === 'bottomright')
boxX = nc.cols-width;
else
boxX = Math.floor(nc.cols/2)-Math.floor(width/2);
} else if (Array.isArray(pos)) {
boxY = pos[0];
boxX = pos[1];
} else if (typeof pos === 'number')
boxY = boxX = Math.floor(pos);
return [boxY, boxX];
}
/**
* Adopted from jquery's extend method. Under the terms of MIT License.
*
* http://code.jquery.com/jquery-1.4.2.js
*
* Modified by Brian White to use Array.isArray instead of the custom isArray method
*/
function extend() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
// Handle a deep copy situation
if (typeof target === "boolean") {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== "object" && !typeof target === 'function')
target = {};
var isPlainObject = function(obj) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval)
return false;
var has_own_constructor = hasOwnProperty.call(obj, "constructor");
var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");
// Not own constructor property must be Object
if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
return false;
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var last_key;
for (key in obj)
last_key = key;
return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key);
};
for (; i < length; i++) {
// Only deal with non-null/undefined values
if ((options = arguments[i]) !== null) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
// Prevent never-ending loop
if (target === copy)
continue;
// Recurse if we're merging object literal values or arrays
if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};
// Never move original objects, clone them
target[name] = extend(deep, clone, copy);
// Don't bring in undefined values
} else if (typeof copy !== "undefined")
target[name] = copy;
}
}
}
// Return the modified object
return target;
};