beautiful-text 0.0.1-security → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of beautiful-text might be problematic. Click here for more details.

@@ -0,0 +1,837 @@
1
+ /*
2
+
3
+ Terminal Kit
4
+
5
+
6
+ Copyright (c) 2009 - 2022 Cédric Ronvel
7
+
8
+
9
+ The MIT License (MIT)
10
+
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+
14
+ of this software and associated documentation files (the "Software"), to deal
15
+
16
+ in the Software without restriction, including without limitation the rights
17
+
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+
20
+ copies of the Software, and to permit persons to whom the Software is
21
+
22
+ furnished to do so, subject to the following conditions:
23
+
24
+
25
+ The above copyright notice and this permission notice shall be included in all
26
+
27
+ copies or substantial portions of the Software.
28
+
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+
38
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41
+
42
+ SOFTWARE.
43
+
44
+ */
45
+
46
+
47
+ "use strict" ;
48
+
49
+
50
+
51
+
52
+ //var string = require( 'string-kit' ) ;
53
+
54
+
55
+
56
+
57
+ /*
58
+
59
+ progressBar( options )
60
+
61
+ * options `object` of options, all of them are OPTIONAL, where:
62
+
63
+ * width: `number` the total width of the progress bar, default to the max available width
64
+
65
+ * percent: `boolean` if true, it shows the progress in percent alongside with the progress bar
66
+
67
+ * eta: `boolean` if true, it shows the Estimated Time of Arrival alongside with the progress bar
68
+
69
+ * items `number` the number of items, turns the 'item mode' on
70
+
71
+ * title `string` the title of the current progress bar, turns the 'title mode' on
72
+
73
+ * barStyle `function` the style of the progress bar items, default to `term.cyan`
74
+
75
+ * barBracketStyle `function` the style of the progress bar bracket character, default to options.barStyle if given
76
+
77
+ or `term.blue`
78
+
79
+ * percentStyle `function` the style of percent value string, default to `term.yellow`
80
+
81
+ * etaStyle `function` the style of the ETA display, default to `term.bold`
82
+
83
+ * itemStyle `function` the style of the item display, default to `term.dim`
84
+
85
+ * titleStyle `function` the style of the title display, default to `term.bold`
86
+
87
+ * itemSize `number` the size of the item status, default to 33% of width
88
+
89
+ * titleSize `number` the size of the title, default to 33% of width or title.length depending on context
90
+
91
+ * barChar `string` the char used for the bar, default to '='
92
+
93
+ * barHeadChar `string` the char used for the bar, default to '>'
94
+
95
+ * maxRefreshTime `number` the maximum time between two refresh in ms, default to 500ms
96
+
97
+ * minRefreshTime `number` the minimum time between two refresh in ms, default to 100ms
98
+
99
+ * inline `boolean` (default: false) if true it is not locked in place, i.e. it redraws itself on the current line
100
+
101
+ * syncMode `boolean` true if it should work in sync mode
102
+
103
+ * y `integer` if set, display the progressBar on that line y-coord
104
+
105
+ * x `integer` if set and the 'y' option is set, display the progressBar starting on that x-coord
106
+
107
+ */
108
+
109
+ module.exports = function progressBar_( options ) {
110
+
111
+ if ( ! options || typeof options !== 'object' ) { options = {} ; }
112
+
113
+
114
+ var controller = {} , progress , ready = false , pause = false ,
115
+
116
+ maxItems , itemsDone = 0 , itemsStarted = [] , itemFiller ,
117
+
118
+ title , titleFiller ,
119
+
120
+ width , y , startX , endX , oldWidth ,
121
+
122
+ wheel , wheelCounter = 0 , itemRollCounter = 0 ,
123
+
124
+ updateCount = 0 , progressUpdateCount = 0 ,
125
+
126
+ lastUpdateTime , lastRedrawTime ,
127
+
128
+ startingTime , redrawTimer ,
129
+
130
+ etaStartingTime , lastEta , etaFiller ;
131
+
132
+
133
+ etaStartingTime = startingTime = ( new Date() ).getTime() ;
134
+
135
+
136
+ wheel = [ '|' , '/' , '-' , '\\' ] ;
137
+
138
+
139
+ options.syncMode = !! options.syncMode ;
140
+
141
+
142
+ width = options.width || this.width - 1 ;
143
+
144
+
145
+ if ( ! options.barBracketStyle ) {
146
+
147
+ if ( options.barStyle ) { options.barBracketStyle = options.barStyle ; }
148
+
149
+ else { options.barBracketStyle = this.blue ; }
150
+
151
+ }
152
+
153
+
154
+ if ( ! options.barStyle ) { options.barStyle = this.cyan ; }
155
+
156
+ if ( ! options.percentStyle ) { options.percentStyle = this.yellow ; }
157
+
158
+ if ( ! options.etaStyle ) { options.etaStyle = this.bold ; }
159
+
160
+ if ( ! options.itemStyle ) { options.itemStyle = this.dim ; }
161
+
162
+ if ( ! options.titleStyle ) { options.titleStyle = this.bold ; }
163
+
164
+
165
+ if ( ! options.barChar ) { options.barChar = '=' ; }
166
+
167
+ else { options.barChar = options.barChar[ 0 ] ; }
168
+
169
+
170
+ if ( ! options.barHeadChar ) { options.barHeadChar = '>' ; }
171
+
172
+ else { options.barHeadChar = options.barHeadChar[ 0 ] ; }
173
+
174
+
175
+ if ( typeof options.maxRefreshTime !== 'number' ) { options.maxRefreshTime = 500 ; }
176
+
177
+ if ( typeof options.minRefreshTime !== 'number' ) { options.minRefreshTime = 100 ; }
178
+
179
+
180
+ if ( typeof options.items === 'number' ) { maxItems = options.items ; }
181
+
182
+ if ( maxItems && typeof options.itemSize !== 'number' ) { options.itemSize = Math.round( width / 3 ) ; }
183
+
184
+
185
+ itemFiller = ' '.repeat( options.itemSize ) ;
186
+
187
+
188
+
189
+ if ( options.title && typeof options.title === 'string' ) {
190
+
191
+ title = options.title ;
192
+
193
+
194
+ if ( typeof options.titleSize !== 'number' ) {
195
+
196
+ options.titleSize = Math.round( Math.min( options.title.length + 1 , width / 3 ) ) ;
197
+
198
+ }
199
+
200
+ }
201
+
202
+
203
+ titleFiller = ' '.repeat( options.titleSize ) ;
204
+
205
+
206
+
207
+ etaFiller = ' ' ; // 11 chars
208
+
209
+
210
+ // This is a naive ETA for instance...
211
+
212
+ var etaString = updated => {
213
+
214
+ var eta = '' , elapsedTime , elapsedEtaTime , remainingTime ,
215
+
216
+ averageUpdateDelay , averageUpdateProgress , lastUpdateElapsedTime , fakeProgress ;
217
+
218
+
219
+ if ( progress >= 1 ) {
220
+
221
+ eta = ' done' ;
222
+
223
+ }
224
+
225
+ else if ( progress > 0 ) {
226
+
227
+ elapsedTime = ( new Date() ).getTime() - startingTime ;
228
+
229
+ elapsedEtaTime = ( new Date() ).getTime() - etaStartingTime ;
230
+
231
+
232
+ if ( ! updated && progressUpdateCount > 1 ) {
233
+
234
+ lastUpdateElapsedTime = ( new Date() ).getTime() - lastUpdateTime ;
235
+
236
+ averageUpdateDelay = elapsedEtaTime / progressUpdateCount ;
237
+
238
+ averageUpdateProgress = progress / progressUpdateCount ;
239
+
240
+
241
+ //console.log( '\n' , elapsedEtaTime , lastUpdateElapsedTime , averageUpdateDelay , averageUpdateProgress , '\n' ) ;
242
+
243
+
244
+ // Do not update ETA if it's not an update, except if update time is too long
245
+
246
+ if ( lastUpdateElapsedTime < averageUpdateDelay ) {
247
+
248
+ fakeProgress = progress + averageUpdateProgress * lastUpdateElapsedTime / averageUpdateDelay ;
249
+
250
+ }
251
+
252
+ else {
253
+
254
+ fakeProgress = progress + averageUpdateProgress ;
255
+
256
+ }
257
+
258
+
259
+ if ( fakeProgress > 0.99 ) { fakeProgress = 0.99 ; }
260
+
261
+ }
262
+
263
+ else {
264
+
265
+ fakeProgress = progress ;
266
+
267
+ }
268
+
269
+
270
+ remainingTime = elapsedEtaTime * ( ( 1 - fakeProgress ) / fakeProgress ) / 1000 ;
271
+
272
+
273
+ eta = ' in ' ;
274
+
275
+
276
+ if ( remainingTime < 10 ) { eta += Math.round( remainingTime * 10 ) / 10 + 's' ; }
277
+
278
+ else if ( remainingTime < 120 ) { eta += Math.round( remainingTime ) + 's' ; }
279
+
280
+ else if ( remainingTime < 7200 ) { eta += Math.round( remainingTime / 60 ) + 'min' ; }
281
+
282
+ else if ( remainingTime < 172800 ) { eta += Math.round( remainingTime / 3600 ) + 'hours' ; }
283
+
284
+ else if ( remainingTime < 31536000 ) { eta += Math.round( remainingTime / 86400 ) + 'days' ; }
285
+
286
+ else { eta = 'few years' ; }
287
+
288
+ }
289
+
290
+ else {
291
+
292
+ etaStartingTime = ( new Date() ).getTime() ;
293
+
294
+ }
295
+
296
+
297
+ eta = ( eta + etaFiller ).slice( 0 , etaFiller.length ) ;
298
+
299
+ lastEta = eta ;
300
+
301
+
302
+ return eta ;
303
+
304
+ } ;
305
+
306
+
307
+
308
+
309
+ var redraw = updated => {
310
+
311
+ var time , itemIndex , itemName = itemFiller , titleName = titleFiller ,
312
+
313
+ innerBarSize , progressSize , voidSize ,
314
+
315
+ progressBar = '' , voidBar = '' , percent = '' , eta = '' ;
316
+
317
+
318
+ if ( ! ready || pause ) { return ; }
319
+
320
+
321
+ time = ( new Date() ).getTime() ;
322
+
323
+
324
+ // If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
325
+
326
+ if ( ( ! progress || progress < 1 ) && lastRedrawTime && time < lastRedrawTime + options.minRefreshTime ) {
327
+
328
+ if ( ! options.syncMode ) {
329
+
330
+ if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
331
+
332
+ redrawTimer = setTimeout( redraw.bind( this , updated ) , lastRedrawTime + options.minRefreshTime - time ) ;
333
+
334
+ }
335
+
336
+ return ;
337
+
338
+ }
339
+
340
+
341
+
342
+ this.saveCursor() ;
343
+
344
+
345
+ // If 'y' is null, we are in the blind mode, we haven't get the cursor location
346
+
347
+ if ( y === null ) { this.column( startX ) ; }
348
+
349
+ else { this.moveTo( startX , y ) ; }
350
+
351
+
352
+ //this.noFormat( Math.floor( progress * 100 ) + '%' ) ;
353
+
354
+
355
+ innerBarSize = width - 2 ;
356
+
357
+
358
+ if ( options.percent ) {
359
+
360
+ innerBarSize -= 4 ;
361
+
362
+ percent = ( ' ' + Math.round( ( progress || 0 ) * 100 ) + '%' ).slice( -4 ) ;
363
+
364
+ }
365
+
366
+
367
+ if ( options.eta ) {
368
+
369
+ eta = etaString( updated ) ;
370
+
371
+ innerBarSize -= eta.length ;
372
+
373
+ }
374
+
375
+
376
+ innerBarSize -= options.itemSize || 0 ;
377
+
378
+ if ( maxItems ) {
379
+
380
+ if ( ! itemsStarted.length ) {
381
+
382
+ itemName = '' ;
383
+
384
+ }
385
+
386
+ else if ( itemsStarted.length === 1 ) {
387
+
388
+ itemName = ' ' + itemsStarted[ 0 ] ;
389
+
390
+ }
391
+
392
+ else {
393
+
394
+ itemIndex = ( itemRollCounter ++ ) % itemsStarted.length ;
395
+
396
+ itemName = ' [' + ( itemIndex + 1 ) + '/' + itemsStarted.length + '] ' + itemsStarted[ itemIndex ] ;
397
+
398
+ }
399
+
400
+
401
+ if ( itemName.length > itemFiller.length ) { itemName = itemName.slice( 0 , itemFiller.length - 1 ) + '…' ; }
402
+
403
+ else if ( itemName.length < itemFiller.length ) { itemName = ( itemName + itemFiller ).slice( 0 , itemFiller.length ) ; }
404
+
405
+ }
406
+
407
+
408
+ innerBarSize -= options.titleSize || 0 ;
409
+
410
+ if ( title ) {
411
+
412
+ titleName = title ;
413
+
414
+
415
+ if ( titleName.length >= titleFiller.length ) { titleName = titleName.slice( 0 , titleFiller.length - 2 ) + '… ' ; }
416
+
417
+ else { titleName = ( titleName + titleFiller ).slice( 0 , titleFiller.length ) ; }
418
+
419
+ }
420
+
421
+
422
+ progressSize = progress === undefined ? 1 : Math.round( innerBarSize * Math.max( Math.min( progress , 1 ) , 0 ) ) ;
423
+
424
+ voidSize = innerBarSize - progressSize ;
425
+
426
+
427
+ /*
428
+
429
+ console.log( "Size:" , width ,
430
+
431
+ voidSize , innerBarSize , progressSize , eta.length , title.length , itemName.length ,
432
+
433
+ voidSize + progressSize + eta.length + title.length + itemName.length
434
+
435
+ ) ;
436
+
437
+ //*/
438
+
439
+
440
+ if ( progressSize ) {
441
+
442
+ if ( progress === undefined ) {
443
+
444
+ progressBar = wheel[ ++ wheelCounter % wheel.length ] ;
445
+
446
+ }
447
+
448
+ else {
449
+
450
+ progressBar += options.barChar.repeat( progressSize - 1 ) ;
451
+
452
+ progressBar += options.barHeadChar ;
453
+
454
+ }
455
+
456
+ }
457
+
458
+
459
+ voidBar += ' '.repeat( voidSize ) ;
460
+
461
+
462
+ options.titleStyle( titleName ) ;
463
+
464
+
465
+ if ( percent ) { options.percentStyle( percent ) ; }
466
+
467
+
468
+ if ( progress === undefined ) { this( ' ' ) ; }
469
+
470
+ else { options.barBracketStyle( '[' ) ; }
471
+
472
+
473
+ options.barStyle( progressBar ) ;
474
+
475
+ this( voidBar ) ;
476
+
477
+
478
+ if ( progress === undefined ) { this( ' ' ) ; /*this( '+' ) ;*/ }
479
+
480
+ else { options.barBracketStyle( ']' ) ; }
481
+
482
+
483
+ options.etaStyle( eta ) ;
484
+
485
+ //this( '*' ) ;
486
+
487
+ options.itemStyle( itemName ) ;
488
+
489
+ //this( '&' ) ;
490
+
491
+
492
+ this.restoreCursor() ;
493
+
494
+
495
+ if ( ! options.syncMode ) {
496
+
497
+ if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
498
+
499
+ if ( ! progress || progress < 1 ) { redrawTimer = setTimeout( redraw , options.maxRefreshTime ) ; }
500
+
501
+ }
502
+
503
+
504
+ lastRedrawTime = time ;
505
+
506
+ } ;
507
+
508
+
509
+
510
+ if ( options.syncMode || options.inline || options.y ) {
511
+
512
+ oldWidth = width ;
513
+
514
+
515
+ if ( options.y ) {
516
+
517
+ startX = + options.x || 1 ;
518
+
519
+ y = + options.y || 1 ;
520
+
521
+ }
522
+
523
+ else {
524
+
525
+ startX = 1 ;
526
+
527
+ y = null ;
528
+
529
+ }
530
+
531
+
532
+ endX = Math.min( startX + width , this.width ) ;
533
+
534
+ width = endX - startX ;
535
+
536
+
537
+ if ( width !== oldWidth ) {
538
+
539
+ // Should resize all part here
540
+
541
+ if ( options.titleSize ) { options.titleSize = Math.floor( options.titleSize * width / oldWidth ) ; }
542
+
543
+ if ( options.itemSize ) { options.itemSize = Math.floor( options.itemSize * width / oldWidth ) ; }
544
+
545
+ }
546
+
547
+
548
+ ready = true ;
549
+
550
+ redraw() ;
551
+
552
+ }
553
+
554
+ else {
555
+
556
+ // Get the cursor location before getting started
557
+
558
+ this.getCursorLocation( ( error , x_ , y_ ) => {
559
+
560
+ if ( error ) {
561
+
562
+ // Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
563
+
564
+ // So we just move to the last line and create a new line.
565
+
566
+ //cleanup( error ) ; return ;
567
+
568
+ this.row.eraseLineAfter( this.height )( '\n' ) ;
569
+
570
+ x_ = 1 ;
571
+
572
+ y_ = this.height ;
573
+
574
+ }
575
+
576
+
577
+ var oldWidth_ = width ;
578
+
579
+
580
+ startX = x_ ;
581
+
582
+ endX = Math.min( x_ + width , this.width ) ;
583
+
584
+ y = y_ ;
585
+
586
+ width = endX - startX ;
587
+
588
+
589
+ if ( width !== oldWidth_ ) {
590
+
591
+ // Should resize all part here
592
+
593
+ if ( options.titleSize ) { options.titleSize = Math.floor( options.titleSize * width / oldWidth_ ) ; }
594
+
595
+ if ( options.itemSize ) { options.itemSize = Math.floor( options.itemSize * width / oldWidth_ ) ; }
596
+
597
+ }
598
+
599
+
600
+ ready = true ;
601
+
602
+ redraw() ;
603
+
604
+ } ) ;
605
+
606
+ }
607
+
608
+
609
+ controller.startItem = name => {
610
+
611
+ itemsStarted.push( name ) ;
612
+
613
+
614
+ // No need to redraw NOW if there are other items running.
615
+
616
+ // Let the timer do the job.
617
+
618
+ if ( itemsStarted.length === 1 ) {
619
+
620
+ // If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
621
+
622
+ if ( progress >= 1 ) { redraw() ; return ; }
623
+
624
+
625
+ if ( options.syncMode ) {
626
+
627
+ redraw() ;
628
+
629
+ }
630
+
631
+ else {
632
+
633
+ // Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
634
+
635
+ // if multiple synchronous update are performed, redraw will be called once
636
+
637
+ if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
638
+
639
+ redrawTimer = setTimeout( redraw , 0 ) ;
640
+
641
+ }
642
+
643
+ }
644
+
645
+ } ;
646
+
647
+
648
+ controller.itemDone = name => {
649
+
650
+ var index ;
651
+
652
+
653
+ itemsDone ++ ;
654
+
655
+
656
+ if ( maxItems ) { progress = itemsDone / maxItems ; }
657
+
658
+ else { progress = undefined ; }
659
+
660
+
661
+ lastUpdateTime = ( new Date() ).getTime() ;
662
+
663
+ updateCount ++ ;
664
+
665
+ progressUpdateCount ++ ;
666
+
667
+
668
+ index = itemsStarted.indexOf( name ) ;
669
+
670
+ if ( index >= 0 ) { itemsStarted.splice( index , 1 ) ; }
671
+
672
+
673
+ // If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
674
+
675
+ if ( progress >= 1 ) { redraw( true ) ; return ; }
676
+
677
+
678
+ if ( options.syncMode ) {
679
+
680
+ redraw() ;
681
+
682
+ }
683
+
684
+ else {
685
+
686
+ // Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
687
+
688
+ // if multiple synchronous update are performed, redraw will be called once
689
+
690
+ if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
691
+
692
+ redrawTimer = setTimeout( redraw.bind( this , true ) , 0 ) ;
693
+
694
+ }
695
+
696
+ } ;
697
+
698
+
699
+ controller.update = toUpdate => {
700
+
701
+ if ( ! toUpdate ) { toUpdate = {} ; }
702
+
703
+ else if ( typeof toUpdate === 'number' ) { toUpdate = { progress: toUpdate } ; }
704
+
705
+
706
+ if ( 'progress' in toUpdate ) {
707
+
708
+ if ( typeof toUpdate.progress !== 'number' ) {
709
+
710
+ progress = undefined ;
711
+
712
+ }
713
+
714
+ else {
715
+
716
+ // Not sure if it is a good thing to let the user set progress to a value that is lesser than the current one
717
+
718
+ progress = toUpdate.progress ;
719
+
720
+
721
+ if ( progress > 1 ) { progress = 1 ; }
722
+
723
+ else if ( progress < 0 ) { progress = 0 ; }
724
+
725
+
726
+ if ( progress > 0 ) { progressUpdateCount ++ ; }
727
+
728
+
729
+ lastUpdateTime = ( new Date() ).getTime() ;
730
+
731
+ updateCount ++ ;
732
+
733
+ }
734
+
735
+ }
736
+
737
+
738
+ if ( typeof toUpdate.items === 'number' ) {
739
+
740
+ maxItems = toUpdate.items ;
741
+
742
+ if ( maxItems ) { progress = itemsDone / maxItems ; }
743
+
744
+
745
+ if ( typeof options.itemSize !== 'number' ) {
746
+
747
+ options.itemSize = Math.round( width / 3 ) ;
748
+
749
+ itemFiller = ' '.repeat( options.itemSize ) ;
750
+
751
+ }
752
+
753
+ }
754
+
755
+
756
+ if ( typeof toUpdate.title === 'string' ) {
757
+
758
+ title = toUpdate.title ;
759
+
760
+
761
+ if ( typeof options.titleSize !== 'number' ) {
762
+
763
+ options.titleSize = Math.round( width / 3 ) ;
764
+
765
+ titleFiller = ' '.repeat( options.titleSize ) ;
766
+
767
+ }
768
+
769
+ }
770
+
771
+
772
+ // If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
773
+
774
+ if ( progress >= 1 ) { redraw( true ) ; return ; }
775
+
776
+
777
+ if ( options.syncMode ) {
778
+
779
+ redraw() ;
780
+
781
+ }
782
+
783
+ else {
784
+
785
+ // Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
786
+
787
+ // if multiple synchronous update are performed, redraw will be called once
788
+
789
+ if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
790
+
791
+ redrawTimer = setTimeout( redraw.bind( this , true ) , 0 ) ;
792
+
793
+ }
794
+
795
+ } ;
796
+
797
+
798
+ controller.pause = controller.stop = () => {
799
+
800
+ pause = true ;
801
+
802
+ } ;
803
+
804
+
805
+ controller.resume = () => {
806
+
807
+ if ( pause ) {
808
+
809
+ pause = false ;
810
+
811
+ redraw() ;
812
+
813
+ }
814
+
815
+ } ;
816
+
817
+
818
+ controller.reset = () => {
819
+
820
+ etaStartingTime = startingTime = ( new Date() ).getTime() ;
821
+
822
+ itemsDone = 0 ;
823
+
824
+ progress = undefined ;
825
+
826
+ itemsStarted.length = 0 ;
827
+
828
+ wheelCounter = itemRollCounter = updateCount = progressUpdateCount = 0 ;
829
+
830
+ redraw() ;
831
+
832
+ } ;
833
+
834
+
835
+ return controller ;
836
+
837
+ } ;