Spaces:
Sleeping
Sleeping
| const _ETA = require('./eta'); | |
| const _Terminal = require('./terminal'); | |
| const _formatter = require('./formatter'); | |
| const _options = require('./options'); | |
| const _EventEmitter = require('events'); | |
| // Progress-Bar constructor | |
| module.exports = class GenericBar extends _EventEmitter{ | |
| constructor(options){ | |
| super(); | |
| // store options and assign derived ones (instance specific) | |
| this.options = _options.assignDerivedOptions(options); | |
| // store terminal instance | |
| this.terminal = (this.options.terminal) ? this.options.terminal : new _Terminal(this.options.stream); | |
| // the current bar value | |
| this.value = 0; | |
| // bar start value (used for progress calculation) | |
| this.startValue = 0; | |
| // the end value of the bar | |
| this.total = 100; | |
| // last drawn string - only render on change! | |
| this.lastDrawnString = null; | |
| // start time (used for eta calculation) | |
| this.startTime = null; | |
| // stop time (used for duration calculation) | |
| this.stopTime = null; | |
| // last update time | |
| this.lastRedraw = Date.now(); | |
| // default eta calculator (will be re-create on start) | |
| this.eta = new _ETA(this.options.etaBufferLength, 0, 0); | |
| // payload data | |
| this.payload = {}; | |
| // progress bar active ? | |
| this.isActive = false; | |
| // use default formatter or custom one ? | |
| this.formatter = (typeof this.options.format === 'function') ? this.options.format : _formatter; | |
| } | |
| // internal render function | |
| render(forceRendering=false){ | |
| // formatter params | |
| const params = { | |
| progress: this.getProgress(), | |
| eta: this.eta.getTime(), | |
| startTime: this.startTime, | |
| stopTime: this.stopTime, | |
| total: this.total, | |
| value: this.value, | |
| maxWidth: this.terminal.getWidth() | |
| }; | |
| // automatic eta update ? (long running processes) | |
| if (this.options.etaAsynchronousUpdate){ | |
| this.updateETA(); | |
| } | |
| // format string | |
| const s = this.formatter(this.options, params, this.payload); | |
| const forceRedraw = forceRendering || this.options.forceRedraw | |
| // force redraw in notty-mode! | |
| || (this.options.noTTYOutput && !this.terminal.isTTY()); | |
| // string changed ? only trigger redraw on change! | |
| if (forceRedraw || this.lastDrawnString != s){ | |
| // trigger event | |
| this.emit('redraw-pre'); | |
| // set cursor to start of line | |
| this.terminal.cursorTo(0, null); | |
| // write output | |
| this.terminal.write(s); | |
| // clear to the right from cursor | |
| this.terminal.clearRight(); | |
| // store string | |
| this.lastDrawnString = s; | |
| // set last redraw time | |
| this.lastRedraw = Date.now(); | |
| // trigger event | |
| this.emit('redraw-post'); | |
| } | |
| } | |
| // start the progress bar | |
| start(total, startValue, payload){ | |
| // set initial values | |
| this.value = startValue || 0; | |
| this.total = (typeof total !== 'undefined' && total >= 0) ? total : 100; | |
| // set start value for progress calculation | |
| this.startValue = (startValue || 0); | |
| // store payload (optional) | |
| this.payload = payload || {}; | |
| // store start time for duration+eta calculation | |
| this.startTime = Date.now(); | |
| // reset stop time for 're-start' scenario (used for duration calculation) | |
| this.stopTime = null; | |
| // reset string line buffer (redraw detection) | |
| this.lastDrawnString = ''; | |
| // initialize eta buffer | |
| this.eta = new _ETA(this.options.etaBufferLength, this.startTime, this.value); | |
| // set flag | |
| this.isActive = true; | |
| // start event | |
| this.emit('start', total, startValue); | |
| } | |
| // stop the bar | |
| stop(){ | |
| // set flag | |
| this.isActive = false; | |
| // store stop timestamp to get total duration | |
| this.stopTime = Date.now(); | |
| // stop event | |
| this.emit('stop', this.total, this.value); | |
| } | |
| // update the bar value | |
| // update(value, payload) | |
| // update(payload) | |
| update(arg0, arg1 = {}){ | |
| // value set ? | |
| // update(value, [payload]); | |
| if (typeof arg0 === 'number') { | |
| // update value | |
| this.value = arg0; | |
| // add new value; recalculate eta | |
| this.eta.update(Date.now(), arg0, this.total); | |
| } | |
| // extract payload | |
| // update(value, payload) | |
| // update(payload) | |
| const payloadData = ((typeof arg0 === 'object') ? arg0 : arg1) || {}; | |
| // update event (before stop() is called) | |
| this.emit('update', this.total, this.value); | |
| // merge payload | |
| for (const key in payloadData){ | |
| this.payload[key] = payloadData[key]; | |
| } | |
| // limit reached ? autostop set ? | |
| if (this.value >= this.getTotal() && this.options.stopOnComplete) { | |
| this.stop(); | |
| } | |
| } | |
| // calculate the actual progress value | |
| getProgress(){ | |
| // calculate the normalized current progress | |
| let progress = (this.value/this.total); | |
| // use relative progress calculation ? range between startValue and total is then used as 100% | |
| // startValue (offset) is ignored for calculations | |
| if (this.options.progressCalculationRelative){ | |
| progress = (this.value-this.startValue)/(this.total-this.startValue); | |
| } | |
| // handle NaN Errors caused by total=0. Set to complete in this case | |
| if (isNaN(progress)){ | |
| progress = (this.options && this.options.emptyOnZero) ? 0.0 : 1.0; | |
| } | |
| // limiter | |
| progress = Math.min(Math.max(progress, 0.0), 1.0); | |
| return progress; | |
| } | |
| // update the bar value | |
| // increment(delta, payload) | |
| // increment(payload) | |
| increment(arg0 = 1, arg1 = {}){ | |
| // increment([payload]) => step=1 | |
| // handle the use case when `step` is omitted but payload is passed | |
| if (typeof arg0 === 'object') { | |
| this.update(this.value + 1, arg0); | |
| // increment([step=1], [payload={}]) | |
| }else{ | |
| this.update(this.value + arg0, arg1); | |
| } | |
| } | |
| // get the total (limit) value | |
| getTotal(){ | |
| return this.total; | |
| } | |
| // set the total (limit) value | |
| setTotal(total){ | |
| if (typeof total !== 'undefined' && total >= 0){ | |
| this.total = total; | |
| } | |
| } | |
| // force eta calculation update (long running processes) | |
| updateETA(){ | |
| // add new value; recalculate eta | |
| this.eta.update(Date.now(), this.value, this.total); | |
| } | |
| } | |