@locr-company/js-progress@1.1.5Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
latest
locr-company/js-progressA class for handling progress
This package works with Node.js, DenoIt is unknown whether this package works with Cloudflare Workers, Bun, Browsers




JSR Score
100%
Published
2 years ago (1.1.5)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351type DateInterval = { y: number; m: number; d: number; h: number; i: number; s: number; f: number; }; type EventOptions = { updateIntervalMSThreshold?: number; }; type EventInternalData = { lastTimeEventFired?: Date; }; /** * A class for handling progress */ export default class Progress { public static readonly DEFAULT_TO_STRING_FORMAT = 'progress => ${Counter}/${TotalCount} (${PercentageCompleted}%)' + '; elapsed: ${ElapsedTime}' + '; ete: ${EstimatedTimeEnroute}' + '; eta: ${EstimatedTimeOfArrival}'; /** * @ignore */ private _counter = 0; /** * @ignore */ private _events: { [key: string]: { callback: (progress: Progress) => void; options: EventOptions; internalData: EventInternalData; }[]; } = {}; /** * @ignore */ private _locale: string | null = null; /** * @ignore */ private _startTime: Date; /** * @ignore */ private _totalCount: number | null = null; /** * @ignore */ private _unit: string | null = null; constructor(totalCount: number | null = null, locale: string | null = null, unit: string | null = null) { this._locale = locale; this._startTime = new Date(); this._totalCount = totalCount; this._unit = unit; } /** * Gets the current counter value */ public get Counter(): number { return this._counter; } /** * Gets the elapsed time since the progress was started */ public get ElapsedTime(): DateInterval { const now = new Date(); const diffTotalMilliseconds = now.getTime() - this._startTime.getTime(); const diffTotalSeconds = diffTotalMilliseconds / 1_000; const milliseconds = diffTotalMilliseconds % 1_000; const diffTotalMinutes = diffTotalSeconds / 60; const seconds = Math.floor(diffTotalSeconds % 60); const diffTotalHours = diffTotalMinutes / 60; const minutes = Math.floor(diffTotalMinutes % 60); const diffTotalDays = diffTotalHours / 24; const hours = Math.floor(diffTotalHours % 24); const years = Math.floor(diffTotalDays / 365); const days = Math.floor(diffTotalDays % 365); return { y: years, m: 0, d: days, h: hours, i: minutes, s: seconds, f: milliseconds, }; } /** * Gets the percentage of the progress that has been completed */ public get PercentageCompleted(): number | null { if (this._totalCount === null) { return null; } return this._counter / this._totalCount * 100; } /** * Gets the start time of the progress */ public get StartTime(): Date { return this._startTime; } /** * Gets the total count of the progress */ public get TotalCount(): number | null { return this._totalCount; } /** * Calculatees the estimated time enroute */ public calculateEstimatedTimeEnroute(): DateInterval | null { if (this._totalCount === null || this._counter === 0) { return null; } const elapsedTime = this.ElapsedTime; const totalElapsedSeconds = elapsedTime.s + elapsedTime.i * 60 + elapsedTime.h * 3_600 + elapsedTime.d * 86_400 + //elapsedTime.m * 2_592_000 + elapsedTime.y * 31_536_000; let yearsRemaining = 0; let daysRemaining = 0; let hoursRemaining = 0; let minutesRemaining = 0; let secondsRemaining = totalElapsedSeconds / this._counter * (this._totalCount - this._counter); if (secondsRemaining >= 60) { minutesRemaining = Math.floor(secondsRemaining / 60); secondsRemaining = secondsRemaining % 60; } if (minutesRemaining >= 60) { hoursRemaining = Math.floor(minutesRemaining / 60); minutesRemaining = minutesRemaining % 60; } if (hoursRemaining >= 24) { daysRemaining = Math.floor(hoursRemaining / 24); hoursRemaining = hoursRemaining % 24; } if (daysRemaining >= 365) { yearsRemaining = Math.floor(daysRemaining / 365); daysRemaining = daysRemaining % 365; } secondsRemaining = Math.floor(secondsRemaining); return { y: yearsRemaining, m: 0, d: daysRemaining, h: hoursRemaining, i: minutesRemaining, s: secondsRemaining, f: 0, }; } /** * Calculates the estimated time of arrival */ public calculateEstimatedTimeOfArrival(): Date | null { const ete = this.calculateEstimatedTimeEnroute(); if (ete === null) { return null; } const now = new Date(); now.setTime(now.getTime() + ete.h * 60 * 60 * 1_000 + ete.i * 60 * 1_000 + ete.s * 1_000); return now; } /** * @ignore */ private formatValue(value: number, locale: string | null = null): string { const options: { minimumFractionDigits?: number; maximumFractionDigits?: number; } = {}; let unitExt = ''; if (this._unit) { if (this._unit == 'byte') { const byteUnits = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; let index = 0; while (value >= 1024 && index < byteUnits.length - 1) { value /= 1024; index++; } options.maximumFractionDigits = index === 0 ? 0 : 2; value = Progress.round(value, options.maximumFractionDigits); unitExt = ` ${byteUnits[index]}`; } } let valueString = value.toFixed(options.maximumFractionDigits ?? 0); if (locale) { if (options.maximumFractionDigits) { options.minimumFractionDigits = options.maximumFractionDigits; } valueString = new Intl.NumberFormat(locale, options).format(value); } return valueString + unitExt; } /** * Increments the counter by 1 */ public incrementCounter(): void { this._counter++; this.raiseEvent('change'); } /** * Adds an event listener * * ```typescript * const progress = new Progress(); * progress.on('change', (progress) => { * console.log(progress.toFormattedString()); * }); * progress.incrementCounter(); // fires the 'change' event * ``` */ public on(eventName: string, callback: (progress: Progress) => void, options: EventOptions = {}): void { if (!this._events[eventName]) { this._events[eventName] = []; } this._events[eventName].push({ callback, options, internalData: {} }); } /** * @ignore */ private raiseEvent(eventName: string): void { if (this._events[eventName]) { this._events[eventName].forEach((evt) => { if (typeof evt.options.updateIntervalMSThreshold === 'number' && evt.internalData.lastTimeEventFired) { const now = new Date(); if ( now.getTime() - evt.internalData.lastTimeEventFired.getTime() < evt.options.updateIntervalMSThreshold ) { return; } } evt.internalData.lastTimeEventFired = new Date(); evt.callback(this); }); } } /** * @ignore */ private static round(number: number, precision = 0): number { const factor = Math.pow(10, precision); return Math.round(number * factor) / factor; } /** * Sets the counter to a specific value */ public setCounter(value: number): void { if (value < 0) { throw new Error('Counter must be greater than or equal to 0'); } this._counter = value; this.raiseEvent('change'); } /** * Sets the total count of the progress */ public setTotalCount(value: number): void { if (value < 0) { throw new Error('Total count must be greater than or equal to 0'); } this._totalCount = value; this.raiseEvent('change'); } /** * Converts the progress to a formatted string */ public toFormattedString(format: string = Progress.DEFAULT_TO_STRING_FORMAT): string { const elapsedTime = this.ElapsedTime; const ete = this.calculateEstimatedTimeEnroute(); const eta = this.calculateEstimatedTimeOfArrival(); let formattedETA = 'N/A'; if (eta) { const paddedNumber = (num: number) => String(num).padStart(2, '0'); const etaYear = eta.getFullYear(); const etaMonth = paddedNumber(eta.getMonth() + 1); // Monat beginnt bei 0 const etaDay = paddedNumber(eta.getDate()); const etaHour = paddedNumber(eta.getHours()); const etaMinute = paddedNumber(eta.getMinutes()); const etaSecond = paddedNumber(eta.getSeconds()); formattedETA = `${etaYear}-${etaMonth}-${etaDay} ${etaHour}:${etaMinute}:${etaSecond}`; } let elapsedTimeReplacement = String(elapsedTime.h).padStart(2, '0'); elapsedTimeReplacement += `:${String(elapsedTime.i).padStart(2, '0')}`; elapsedTimeReplacement += `:${String(elapsedTime.s).padStart(2, '0')}`; let estimatedTimeEnrouteReplacement = 'N/A'; if (ete) { estimatedTimeEnrouteReplacement = String(ete.h).padStart(2, '0'); estimatedTimeEnrouteReplacement += `:${String(ete.i).padStart(2, '0')}`; estimatedTimeEnrouteReplacement += `:${String(ete.s).padStart(2, '0')}`; } const replacements: Record<string, string> = { '${Counter}': this.formatValue(this._counter, this._locale), '${ElapsedTime}': elapsedTimeReplacement, '${EstimatedTimeEnroute}': estimatedTimeEnrouteReplacement, '${EstimatedTimeOfArrival}': formattedETA, '${PercentageCompleted}': this.PercentageCompleted !== null ? this.PercentageCompleted.toFixed(2) : 'N/A', '${TotalCount}': this._totalCount ? this.formatValue(this._totalCount, this._locale) : '-', }; for (const key in replacements) { format = format.replace(key, replacements[key]); } return format; } }