This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
5.7 KiB
225 lines
5.7 KiB
"use strict" |
|
var hasUnicode = require("has-unicode") |
|
var ansi = require("ansi") |
|
var align = { |
|
center: require("lodash.pad"), |
|
left: require("lodash.padend"), |
|
right: require("lodash.padstart") |
|
} |
|
var defaultStream = process.stderr |
|
function isTTY() { |
|
return process.stderr.isTTY |
|
} |
|
function getWritableTTYColumns() { |
|
// Writing to the final column wraps the line |
|
// We have to use stdout here, because Node's magic SIGWINCH handler only |
|
// updates process.stdout, not process.stderr |
|
return process.stdout.columns - 1 |
|
} |
|
|
|
var ProgressBar = module.exports = function (options, cursor) { |
|
if (! options) options = {} |
|
if (! cursor && options.write) { |
|
cursor = options |
|
options = {} |
|
} |
|
if (! cursor) { |
|
cursor = ansi(defaultStream) |
|
} |
|
this.cursor = cursor |
|
this.showing = false |
|
this.theme = options.theme || (hasUnicode() ? ProgressBar.unicode : ProgressBar.ascii) |
|
this.template = options.template || [ |
|
{type: "name", separated: true, length: 25}, |
|
{type: "spinner", separated: true}, |
|
{type: "startgroup"}, |
|
{type: "completionbar"}, |
|
{type: "endgroup"} |
|
] |
|
this.updatefreq = options.maxUpdateFrequency == null ? 50 : options.maxUpdateFrequency |
|
this.lastName = "" |
|
this.lastCompleted = 0 |
|
this.spun = 0 |
|
this.last = new Date(0) |
|
|
|
var self = this |
|
this._handleSizeChange = function () { |
|
if (!self.showing) return |
|
self.hide() |
|
self.show() |
|
} |
|
} |
|
ProgressBar.prototype = {} |
|
|
|
ProgressBar.unicode = { |
|
startgroup: "╢", |
|
endgroup: "╟", |
|
complete: "█", |
|
incomplete: "░", |
|
spinner: "▀▐▄▌", |
|
subsection: "→" |
|
} |
|
|
|
ProgressBar.ascii = { |
|
startgroup: "|", |
|
endgroup: "|", |
|
complete: "#", |
|
incomplete: "-", |
|
spinner: "-\\|/", |
|
subsection: "->" |
|
} |
|
|
|
ProgressBar.prototype.setTheme = function(theme) { |
|
this.theme = theme |
|
} |
|
|
|
ProgressBar.prototype.setTemplate = function(template) { |
|
this.template = template |
|
} |
|
|
|
ProgressBar.prototype._enableResizeEvents = function() { |
|
process.stdout.on('resize', this._handleSizeChange) |
|
} |
|
|
|
ProgressBar.prototype._disableResizeEvents = function() { |
|
process.stdout.removeListener('resize', this._handleSizeChange) |
|
} |
|
|
|
ProgressBar.prototype.disable = function() { |
|
this.hide() |
|
this.disabled = true |
|
} |
|
|
|
ProgressBar.prototype.enable = function() { |
|
this.disabled = false |
|
this.show() |
|
} |
|
|
|
ProgressBar.prototype.hide = function() { |
|
if (!isTTY()) return |
|
if (this.disabled) return |
|
this.cursor.show() |
|
if (this.showing) this.cursor.up(1) |
|
this.cursor.horizontalAbsolute(0).eraseLine() |
|
this.showing = false |
|
} |
|
|
|
var repeat = function (str, count) { |
|
var out = "" |
|
for (var ii=0; ii<count; ++ii) out += str |
|
return out |
|
} |
|
|
|
ProgressBar.prototype.pulse = function(name) { |
|
++ this.spun |
|
if (! this.showing) return |
|
if (this.disabled) return |
|
|
|
var baseName = this.lastName |
|
name = name |
|
? ( baseName |
|
? baseName + " " + this.theme.subsection + " " + name |
|
: null ) |
|
: baseName |
|
this.show(name) |
|
this.lastName = baseName |
|
} |
|
|
|
ProgressBar.prototype.show = function(name, completed) { |
|
name = this.lastName = name || this.lastName |
|
completed = this.lastCompleted = completed || this.lastCompleted |
|
|
|
if (!isTTY()) return |
|
if (this.disabled) return |
|
if (! this.spun && ! completed) return |
|
if (this.tryAgain) return |
|
var self = this |
|
|
|
if (this.showing && new Date() - this.last < this.updatefreq) { |
|
this.tryAgain = setTimeout(function () { |
|
self.tryAgain = null |
|
if (self.disabled) return |
|
if (! self.spun && ! completed) return |
|
drawBar() |
|
}, this.updatefreq - (new Date() - this.last)) |
|
return |
|
} |
|
|
|
return drawBar() |
|
|
|
function drawBar() { |
|
var values = { |
|
name: name, |
|
spinner: self.spun, |
|
completed: completed |
|
} |
|
|
|
self.last = new Date() |
|
|
|
var statusline = self.renderTemplate(self.theme, self.template, values) |
|
|
|
if (self.showing) self.cursor.up(1) |
|
self.cursor |
|
.hide() |
|
.horizontalAbsolute(0) |
|
.write(statusline.substr(0, getWritableTTYColumns()) + "\n") |
|
.show() |
|
|
|
self.showing = true |
|
} |
|
} |
|
|
|
ProgressBar.prototype.renderTemplate = function (theme, template, values) { |
|
values.startgroup = theme.startgroup |
|
values.endgroup = theme.endgroup |
|
values.spinner = values.spinner |
|
? theme.spinner.substr(values.spinner % theme.spinner.length,1) |
|
: "" |
|
|
|
var output = {prebar: "", postbar: ""} |
|
var status = "prebar" |
|
var self = this |
|
template.forEach(function(T) { |
|
if (typeof T === "string") { |
|
output[status] += T |
|
return |
|
} |
|
if (T.type === "completionbar") { |
|
status = "postbar" |
|
return |
|
} |
|
if (!values.hasOwnProperty(T.type)) throw new Error("Unknown template value '"+T.type+"'") |
|
var value = self.renderValue(T, values[T.type]) |
|
if (value === "") return |
|
var sofar = output[status].length |
|
var lastChar = sofar ? output[status][sofar-1] : null |
|
if (T.separated && sofar && lastChar !== " ") { |
|
output[status] += " " |
|
} |
|
output[status] += value |
|
if (T.separated) output[status] += " " |
|
}) |
|
|
|
var bar = "" |
|
if (status === "postbar") { |
|
var nonBarLen = output.prebar.length + output.postbar.length |
|
|
|
var barLen = getWritableTTYColumns() - nonBarLen |
|
var sofar = Math.round(barLen * Math.max(0,Math.min(1,values.completed||0))) |
|
var rest = barLen - sofar |
|
bar = repeat(theme.complete, sofar) |
|
+ repeat(theme.incomplete, rest) |
|
} |
|
|
|
return output.prebar + bar + output.postbar |
|
} |
|
ProgressBar.prototype.renderValue = function (template, value) { |
|
if (value == null || value === "") return "" |
|
var maxLength = template.maxLength || template.length |
|
var minLength = template.minLength || template.length |
|
var alignWith = align[template.align] || align.left |
|
// if (maxLength) value = value.substr(-1 * maxLength) |
|
if (maxLength) value = value.substr(0, maxLength) |
|
if (minLength) value = alignWith(value, minLength) |
|
return value |
|
}
|
|
|