Source: Progress.js

  1. const EventEmitter = require('events').EventEmitter
  2. , Rx = require('rxjs')
  3. , Observable = Rx.Observable
  4. , fromEvent = Rx.fromEvent
  5. , symbolProgress = Symbol('progress');
  6. /**
  7. * A class used to report progress of any kind. Supports events through
  8. * EventEmitter, own callbacks or being used as an Observable.
  9. *
  10. * @template T
  11. */
  12. class Progress extends EventEmitter {
  13. /**
  14. * @param {callbackHandler<T>} handler a callback to be invoked for every
  15. * progress reported. If not provided, you may subscribe to the events of
  16. * this class or use the provided Observable.
  17. */
  18. constructor(handler = void 0) {
  19. super();
  20. this.handler = handler;
  21. /** @type {T} */
  22. this.last = void 0;
  23. };
  24. /**
  25. * Report the current progress, which is then broadcast to all listeners
  26. * and passed to this handler.
  27. *
  28. * @param {T} progress the item that represents the progress
  29. * @returns {Progress} this
  30. */
  31. reportProgress(progress) {
  32. this.last = progress;
  33. setTimeout(() => {
  34. this.emit(symbolProgress, progress);
  35. if (this.handler instanceof Function) {
  36. this.handler(progress);
  37. }
  38. }, 0);
  39. return this;
  40. };
  41. /**
  42. * Returns an Rx.Observable that will emit events whenever they
  43. * are reported to its subscribers.
  44. *
  45. * @type {Observable<T>}
  46. */
  47. get observable() {
  48. return fromEvent(this, symbolProgress);
  49. };
  50. };
  51. /**
  52. * A simple class to report numeric progress within a range
  53. * (typically [0, 1]), but any other positive range is good, too.
  54. */
  55. class ProgressNumeric extends Progress {
  56. /**
  57. * @param {Number} progressMin the lowest possible value for the progress
  58. * @param {Number} progressMax the largest possible value for the progress
  59. * @param {callbackHandler<number>} handler a callback to be invoked for every
  60. * progress reported. If not provided, you may subscribe to the events of
  61. * this class or use the provided Observable.
  62. */
  63. constructor(progressMin = 0, progressMax = 1, handler = void 0) {
  64. super(handler);
  65. const t = '[object Number]', v = x => Object.prototype.toString.call(x);
  66. if (v(progressMin) !== t || v(progressMax) !== t
  67. || isNaN(progressMin) || isNaN(progressMax)) {
  68. throw new Error('Both progressMin and progressMax must be numeric.');
  69. }
  70. if (progressMin < 0 || progressMax < progressMin) {
  71. throw new Error('Both progressMin and progressMax must be positive and progressMax must be greater than progressMin.');
  72. }
  73. this.progressMin = progressMin;
  74. this.progressMax = progressMax;
  75. this.progressRange = progressMax - progressMin;
  76. };
  77. /**
  78. * @type {Number}
  79. */
  80. get percent() {
  81. return this.last === void 0 ? 0 :
  82. (this.last - this.progressMin) / this.progressRange;
  83. };
  84. /**
  85. * @override
  86. * @inheritdoc
  87. * @param {Number} progress the numeric progress
  88. * @returns {this}
  89. */
  90. reportProgress(progress) {
  91. if (isNaN(progress) || progress < this.progressMin || progress > this.progressMax) {
  92. throw new Error(`The value "${progress}" is out of range.`);
  93. }
  94. return super.reportProgress(progress);
  95. };
  96. };
  97. module.exports = Object.freeze({
  98. Progress,
  99. ProgressNumeric,
  100. symbolProgress
  101. });