251 lines
6.4 KiB
JavaScript
251 lines
6.4 KiB
JavaScript
![]() |
const _Terminal = require('./terminal');
|
||
|
const _BarElement = require('./generic-bar');
|
||
|
const _options = require('./options');
|
||
|
const _EventEmitter = require('events');
|
||
|
|
||
|
// Progress-Bar constructor
|
||
|
module.exports = class MultiBar extends _EventEmitter{
|
||
|
|
||
|
constructor(options, preset){
|
||
|
super();
|
||
|
|
||
|
// list of bars
|
||
|
this.bars = [];
|
||
|
|
||
|
// parse+store options
|
||
|
this.options = _options.parse(options, preset);
|
||
|
|
||
|
// disable synchronous updates
|
||
|
this.options.synchronousUpdate = false;
|
||
|
|
||
|
// store terminal instance
|
||
|
this.terminal = (this.options.terminal) ? this.options.terminal : new _Terminal(this.options.stream);
|
||
|
|
||
|
// the update timer
|
||
|
this.timer = null;
|
||
|
|
||
|
// progress bar active ?
|
||
|
this.isActive = false;
|
||
|
|
||
|
// update interval
|
||
|
this.schedulingRate = (this.terminal.isTTY() ? this.options.throttleTime : this.options.notTTYSchedule);
|
||
|
|
||
|
// logging output buffer
|
||
|
this.loggingBuffer = [];
|
||
|
|
||
|
// callback used for gracefulExit
|
||
|
this.sigintCallback = null;
|
||
|
}
|
||
|
|
||
|
// add a new bar to the stack
|
||
|
create(total, startValue, payload, barOptions={}){
|
||
|
// create new bar element and merge global options + overrides
|
||
|
// use the same global terminal instance for all instances
|
||
|
const bar = new _BarElement(Object.assign(
|
||
|
{},
|
||
|
|
||
|
// global options
|
||
|
this.options,
|
||
|
|
||
|
// terminal instance
|
||
|
{
|
||
|
terminal: this.terminal
|
||
|
},
|
||
|
|
||
|
// overrides
|
||
|
barOptions,
|
||
|
));
|
||
|
|
||
|
// store bar
|
||
|
this.bars.push(bar);
|
||
|
|
||
|
// progress updates are only visible in TTY mode!
|
||
|
if (this.options.noTTYOutput === false && this.terminal.isTTY() === false){
|
||
|
return bar;
|
||
|
}
|
||
|
|
||
|
// add handler to restore cursor settings (stop the bar) on SIGINT/SIGTERM ?
|
||
|
if (this.sigintCallback === null && this.options.gracefulExit){
|
||
|
this.sigintCallback = this.stop.bind(this);
|
||
|
process.once('SIGINT', this.sigintCallback);
|
||
|
process.once('SIGTERM', this.sigintCallback);
|
||
|
}
|
||
|
|
||
|
// multiprogress already active ?
|
||
|
if (!this.isActive){
|
||
|
// hide the cursor ?
|
||
|
if (this.options.hideCursor === true){
|
||
|
this.terminal.cursor(false);
|
||
|
}
|
||
|
|
||
|
// disable line wrapping ?
|
||
|
if (this.options.linewrap === false){
|
||
|
this.terminal.lineWrapping(false);
|
||
|
}
|
||
|
|
||
|
// initialize update timer
|
||
|
this.timer = setTimeout(this.update.bind(this), this.schedulingRate);
|
||
|
}
|
||
|
|
||
|
// set flag
|
||
|
this.isActive = true;
|
||
|
|
||
|
// start progress bar
|
||
|
bar.start(total, startValue, payload);
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('start');
|
||
|
|
||
|
// return new instance
|
||
|
return bar;
|
||
|
}
|
||
|
|
||
|
// remove a bar from the stack
|
||
|
remove(bar){
|
||
|
// find element
|
||
|
const index = this.bars.indexOf(bar);
|
||
|
|
||
|
// element found ?
|
||
|
if (index < 0){
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// remove element
|
||
|
this.bars.splice(index, 1);
|
||
|
|
||
|
// force update
|
||
|
this.update();
|
||
|
|
||
|
// clear bottom
|
||
|
this.terminal.newline();
|
||
|
this.terminal.clearBottom();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// internal update routine
|
||
|
update(){
|
||
|
// stop timer
|
||
|
if (this.timer){
|
||
|
clearTimeout(this.timer);
|
||
|
this.timer = null;
|
||
|
}
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('update-pre');
|
||
|
|
||
|
// reset cursor
|
||
|
this.terminal.cursorRelativeReset();
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('redraw-pre');
|
||
|
|
||
|
// content within logging buffer ?
|
||
|
if (this.loggingBuffer.length > 0){
|
||
|
this.terminal.clearLine();
|
||
|
|
||
|
// flush logging buffer and write content to terminal
|
||
|
while (this.loggingBuffer.length > 0){
|
||
|
this.terminal.write(this.loggingBuffer.shift(), true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update each bar
|
||
|
for (let i=0; i< this.bars.length; i++){
|
||
|
// add new line ?
|
||
|
if (i > 0){
|
||
|
this.terminal.newline();
|
||
|
}
|
||
|
|
||
|
// render
|
||
|
this.bars[i].render();
|
||
|
}
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('redraw-post');
|
||
|
|
||
|
// add new line in notty mode!
|
||
|
if (this.options.noTTYOutput && this.terminal.isTTY() === false){
|
||
|
this.terminal.newline();
|
||
|
this.terminal.newline();
|
||
|
}
|
||
|
|
||
|
// next update
|
||
|
this.timer = setTimeout(this.update.bind(this), this.schedulingRate);
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('update-post');
|
||
|
|
||
|
// stop if stopOnComplete and all bars stopped
|
||
|
if (this.options.stopOnComplete && !this.bars.find(bar => bar.isActive)) {
|
||
|
this.stop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stop(){
|
||
|
|
||
|
// stop timer
|
||
|
clearTimeout(this.timer);
|
||
|
this.timer = null;
|
||
|
|
||
|
// remove sigint listener
|
||
|
if (this.sigintCallback){
|
||
|
process.removeListener('SIGINT', this.sigintCallback);
|
||
|
process.removeListener('SIGTERM', this.sigintCallback);
|
||
|
this.sigintCallback = null;
|
||
|
}
|
||
|
|
||
|
// set flag
|
||
|
this.isActive = false;
|
||
|
|
||
|
// cursor hidden ?
|
||
|
if (this.options.hideCursor === true){
|
||
|
this.terminal.cursor(true);
|
||
|
}
|
||
|
|
||
|
// re-enable line wrpaping ?
|
||
|
if (this.options.linewrap === false){
|
||
|
this.terminal.lineWrapping(true);
|
||
|
}
|
||
|
|
||
|
// reset cursor
|
||
|
this.terminal.cursorRelativeReset();
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('stop-pre-clear');
|
||
|
|
||
|
// clear line on complete ?
|
||
|
if (this.options.clearOnComplete){
|
||
|
// clear all bars
|
||
|
this.terminal.clearBottom();
|
||
|
|
||
|
// or show final progress ?
|
||
|
}else{
|
||
|
// update each bar
|
||
|
for (let i=0; i< this.bars.length; i++){
|
||
|
// add new line ?
|
||
|
if (i > 0){
|
||
|
this.terminal.newline();
|
||
|
}
|
||
|
|
||
|
// trigger final rendering
|
||
|
this.bars[i].render();
|
||
|
|
||
|
// stop
|
||
|
this.bars[i].stop();
|
||
|
}
|
||
|
|
||
|
// new line on complete
|
||
|
this.terminal.newline();
|
||
|
}
|
||
|
|
||
|
// trigger event
|
||
|
this.emit('stop');
|
||
|
}
|
||
|
|
||
|
log(s){
|
||
|
// push content into logging buffer
|
||
|
this.loggingBuffer.push(s);
|
||
|
}
|
||
|
}
|