tty-progressbar 0.15.1 → 0.18.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +63 -0
- data/LICENSE.txt +1 -1
- data/README.md +495 -125
- data/lib/tty-progressbar.rb +2 -2
- data/lib/tty/progressbar.rb +169 -68
- data/lib/tty/progressbar/configuration.rb +121 -27
- data/lib/tty/progressbar/converter.rb +16 -19
- data/lib/tty/progressbar/formats.rb +120 -0
- data/lib/tty/progressbar/formatter.rb +33 -38
- data/lib/tty/progressbar/formatter/bar.rb +74 -27
- data/lib/tty/progressbar/formatter/byte_rate.rb +6 -20
- data/lib/tty/progressbar/formatter/current.rb +4 -19
- data/lib/tty/progressbar/formatter/current_byte.rb +9 -17
- data/lib/tty/progressbar/formatter/elapsed.rb +9 -18
- data/lib/tty/progressbar/formatter/estimated.rb +18 -20
- data/lib/tty/progressbar/formatter/estimated_time.rb +39 -0
- data/lib/tty/progressbar/formatter/mean_byte.rb +6 -20
- data/lib/tty/progressbar/formatter/mean_rate.rb +6 -20
- data/lib/tty/progressbar/formatter/percent.rb +10 -16
- data/lib/tty/progressbar/formatter/rate.rb +5 -19
- data/lib/tty/progressbar/formatter/total.rb +10 -16
- data/lib/tty/progressbar/formatter/total_byte.rb +14 -18
- data/lib/tty/progressbar/formatters.rb +53 -0
- data/lib/tty/progressbar/meter.rb +2 -2
- data/lib/tty/progressbar/multi.rb +61 -21
- data/lib/tty/progressbar/pipeline.rb +13 -6
- data/lib/tty/progressbar/timer.rb +89 -0
- data/lib/tty/progressbar/version.rb +3 -1
- metadata +56 -164
- data/.codeclimate.yml +0 -11
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/.travis.yml +0 -26
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -14
- data/Rakefile +0 -8
- data/appveyor.yml +0 -23
- data/examples/color.rb +0 -18
- data/examples/failure.rb +0 -12
- data/examples/iterator.rb +0 -5
- data/examples/lazy.rb +0 -6
- data/examples/multi/main_bar.rb +0 -13
- data/examples/multi/simple.rb +0 -13
- data/examples/multi/width.rb +0 -13
- data/examples/simple.rb +0 -7
- data/examples/slow_process.rb +0 -29
- data/examples/speed.rb +0 -11
- data/examples/threaded.rb +0 -14
- data/examples/tokens.rb +0 -12
- data/examples/unicode.rb +0 -7
- data/spec/spec_helper.rb +0 -51
- data/spec/unit/advance_spec.rb +0 -25
- data/spec/unit/clear_spec.rb +0 -17
- data/spec/unit/complete_spec.rb +0 -16
- data/spec/unit/converter/to_bytes_spec.rb +0 -47
- data/spec/unit/converter/to_seconds_spec.rb +0 -15
- data/spec/unit/converter/to_time_spec.rb +0 -19
- data/spec/unit/custom_formatter_spec.rb +0 -26
- data/spec/unit/custom_token_spec.rb +0 -14
- data/spec/unit/events_spec.rb +0 -33
- data/spec/unit/finish_spec.rb +0 -15
- data/spec/unit/formatter/bar_spec.rb +0 -16
- data/spec/unit/formatter/byte_rate_spec.rb +0 -32
- data/spec/unit/formatter/current_byte_spec.rb +0 -16
- data/spec/unit/formatter/current_spec.rb +0 -14
- data/spec/unit/formatter/elapsed_spec.rb +0 -58
- data/spec/unit/formatter/estimated_spec.rb +0 -27
- data/spec/unit/formatter/mean_byte_spec.rb +0 -32
- data/spec/unit/formatter/mean_rate_spec.rb +0 -31
- data/spec/unit/formatter/percent_spec.rb +0 -16
- data/spec/unit/formatter/rate_spec.rb +0 -31
- data/spec/unit/formatter/total_byte_spec.rb +0 -16
- data/spec/unit/formatter/total_spec.rb +0 -16
- data/spec/unit/frequency_spec.rb +0 -27
- data/spec/unit/head_spec.rb +0 -32
- data/spec/unit/hide_cursor_spec.rb +0 -27
- data/spec/unit/inspect_spec.rb +0 -11
- data/spec/unit/iterate_spec.rb +0 -79
- data/spec/unit/log_spec.rb +0 -29
- data/spec/unit/meter_spec.rb +0 -70
- data/spec/unit/multi/advance_spec.rb +0 -123
- data/spec/unit/multi/events_spec.rb +0 -115
- data/spec/unit/multi/finish_spec.rb +0 -41
- data/spec/unit/multi/line_inset_spec.rb +0 -65
- data/spec/unit/multi/register_spec.rb +0 -35
- data/spec/unit/multi/reset_spec.rb +0 -28
- data/spec/unit/multi/stop_spec.rb +0 -15
- data/spec/unit/multi/width_spec.rb +0 -118
- data/spec/unit/new_spec.rb +0 -76
- data/spec/unit/pipeline_spec.rb +0 -19
- data/spec/unit/ratio_spec.rb +0 -31
- data/spec/unit/render_spec.rb +0 -25
- data/spec/unit/reset_spec.rb +0 -31
- data/spec/unit/resize_spec.rb +0 -35
- data/spec/unit/set_current_spec.rb +0 -43
- data/spec/unit/start_spec.rb +0 -14
- data/spec/unit/stop_spec.rb +0 -19
- data/spec/unit/update_spec.rb +0 -22
- data/spec/unit/width_spec.rb +0 -86
- data/tasks/console.rake +0 -9
- data/tasks/coverage.rake +0 -9
- data/tasks/spec.rake +0 -27
- data/tty-progressbar.gemspec +0 -30
data/lib/tty-progressbar.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative "tty/progressbar"
|
2
|
+
require_relative "tty/progressbar/multi"
|
data/lib/tty/progressbar.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
|
10
|
-
|
11
|
-
require_relative
|
12
|
-
require_relative
|
13
|
-
require_relative
|
3
|
+
require "io/console"
|
4
|
+
require "forwardable"
|
5
|
+
require "monitor"
|
6
|
+
require "tty-cursor"
|
7
|
+
require "tty-screen"
|
8
|
+
require "strings-ansi"
|
9
|
+
require "unicode/display_width"
|
10
|
+
|
11
|
+
require_relative "progressbar/timer"
|
12
|
+
require_relative "progressbar/configuration"
|
13
|
+
require_relative "progressbar/formatters"
|
14
|
+
require_relative "progressbar/meter"
|
15
|
+
require_relative "progressbar/version"
|
14
16
|
|
15
17
|
module TTY
|
16
18
|
# Used for creating terminal progress bar
|
@@ -20,7 +22,9 @@ module TTY
|
|
20
22
|
extend Forwardable
|
21
23
|
include MonitorMixin
|
22
24
|
|
23
|
-
ECMA_CSI = "\e["
|
25
|
+
ECMA_CSI = "\e["
|
26
|
+
|
27
|
+
NEWLINE = "\n"
|
24
28
|
|
25
29
|
CURSOR_LOCK = Monitor.new
|
26
30
|
|
@@ -28,17 +32,15 @@ module TTY
|
|
28
32
|
|
29
33
|
attr_reader :current
|
30
34
|
|
31
|
-
attr_reader :start_at
|
32
|
-
|
33
35
|
attr_reader :row
|
34
36
|
|
35
|
-
def_delegators :@configuration, :total, :width, :
|
36
|
-
:
|
37
|
-
:
|
37
|
+
def_delegators :@configuration, :total, :width, :complete, :incomplete,
|
38
|
+
:head, :clear_head, :hide_cursor, :clear, :output,
|
39
|
+
:frequency, :interval, :inset, :width=, :unknown, :bar_format
|
38
40
|
|
39
41
|
def_delegators :@meter, :rate, :mean_rate
|
40
42
|
|
41
|
-
|
43
|
+
def_delegators :@timer, :elapsed_time, :start_time
|
42
44
|
|
43
45
|
# Determine terminal width
|
44
46
|
#
|
@@ -58,11 +60,17 @@ module TTY
|
|
58
60
|
#
|
59
61
|
# @api public
|
60
62
|
def self.display_columns(value)
|
61
|
-
Unicode::DisplayWidth.of(value)
|
63
|
+
Unicode::DisplayWidth.of(Strings::ANSI.sanitize(value))
|
62
64
|
end
|
63
65
|
|
64
66
|
# Create progress bar
|
65
67
|
#
|
68
|
+
# @example
|
69
|
+
# bar = TTY::Progressbar.new
|
70
|
+
# bar.configure do |config|
|
71
|
+
# config.total = 20
|
72
|
+
# end
|
73
|
+
#
|
66
74
|
# @param [String] format
|
67
75
|
# the tokenized string that displays the output
|
68
76
|
#
|
@@ -70,20 +78,29 @@ module TTY
|
|
70
78
|
# @option options [Numeric] :total
|
71
79
|
# the total number of steps to completion
|
72
80
|
# @option options [Numeric] :width
|
73
|
-
# the maximum width for the
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
+
# the maximum width for the progress bar except all formatting tokens
|
82
|
+
# @option option [String] :complete
|
83
|
+
# the complete character in progress animation
|
84
|
+
# @option options [String] :incomplete
|
85
|
+
# the incomplete character in progress animation
|
86
|
+
# @option options [String] :head
|
87
|
+
# the head character, defaults to complete
|
88
|
+
# @option options [String] :unknown
|
89
|
+
# the unknown character for indeterminate progress animation
|
90
|
+
# @option options [Boolean] :bar_format
|
91
|
+
# the preconfigured bar format name, defaults to :classic
|
81
92
|
# @option options [Object] :output
|
82
|
-
# the object that responds to print call
|
93
|
+
# the object that responds to print call, defaults to stderr
|
83
94
|
# @option options [Number] :frequency
|
84
|
-
# the frequency with which to display
|
95
|
+
# the frequency with which to display a progress bar per second
|
85
96
|
# @option options [Number] :interval
|
86
|
-
# the
|
97
|
+
# the time interval for sampling of speed measurement, defaults to 1 second
|
98
|
+
# @option options [Boolean] :hide_cursor
|
99
|
+
# whether or not to hide the cursor, defaults to false
|
100
|
+
# @option options [Boolean] :clear
|
101
|
+
# whether or not to clear the progress line, defaults to false
|
102
|
+
# @option options [Boolean] :clear_head
|
103
|
+
# whether or not to replace head character with complete, defaults to false
|
87
104
|
#
|
88
105
|
# @api public
|
89
106
|
def initialize(format, options = {})
|
@@ -96,34 +113,52 @@ module TTY
|
|
96
113
|
@configuration = TTY::ProgressBar::Configuration.new(options)
|
97
114
|
yield @configuration if block_given?
|
98
115
|
|
99
|
-
@
|
100
|
-
@meter
|
116
|
+
@formatters = TTY::ProgressBar::Formatters.new
|
117
|
+
@meter = TTY::ProgressBar::Meter.new(interval)
|
118
|
+
@timer = TTY::ProgressBar::Timer.new
|
101
119
|
@callbacks = Hash.new { |h, k| h[k] = [] }
|
102
120
|
|
103
|
-
@
|
121
|
+
@formatters.load(self)
|
104
122
|
reset
|
105
123
|
|
106
124
|
@first_render = true
|
107
|
-
@multibar
|
108
|
-
@row
|
125
|
+
@multibar = nil
|
126
|
+
@row = nil
|
109
127
|
end
|
110
128
|
|
111
129
|
# Reset progress to default configuration
|
112
130
|
#
|
113
131
|
# @api public
|
114
132
|
def reset
|
115
|
-
@width = 0 if
|
133
|
+
@width = 0 if indeterminate?
|
116
134
|
@render_period = frequency == 0 ? 0 : 1.0 / frequency
|
117
135
|
@current = 0
|
136
|
+
@unknown = 0
|
118
137
|
@last_render_time = Time.now
|
119
138
|
@last_render_width = 0
|
120
139
|
@done = false
|
121
140
|
@stopped = false
|
122
|
-
@
|
123
|
-
@started = false
|
141
|
+
@paused = false
|
124
142
|
@tokens = {}
|
125
143
|
|
126
144
|
@meter.clear
|
145
|
+
@timer.reset
|
146
|
+
end
|
147
|
+
|
148
|
+
# Access instance configuration
|
149
|
+
#
|
150
|
+
# @api public
|
151
|
+
def configure
|
152
|
+
yield @configuration
|
153
|
+
end
|
154
|
+
|
155
|
+
# Check if progress can be determined or not
|
156
|
+
#
|
157
|
+
# @return [Boolean]
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
def indeterminate?
|
161
|
+
total.nil?
|
127
162
|
end
|
128
163
|
|
129
164
|
# Attach this bar to multi bar
|
@@ -136,13 +171,26 @@ module TTY
|
|
136
171
|
@multibar = multibar
|
137
172
|
end
|
138
173
|
|
174
|
+
# Use custom token formatter
|
175
|
+
#
|
176
|
+
# @param [Object] formatter_class
|
177
|
+
# the formatter class to add to formatting pipeline
|
178
|
+
#
|
179
|
+
# @api public
|
180
|
+
def use(formatter_class)
|
181
|
+
unless formatter_class.is_a?(Class)
|
182
|
+
raise ArgumentError, "Formatter needs to be a class"
|
183
|
+
end
|
184
|
+
|
185
|
+
@formatters.use(formatter_class.new(self))
|
186
|
+
end
|
187
|
+
|
139
188
|
# Start progression by drawing bar and setting time
|
140
189
|
#
|
141
190
|
# @api public
|
142
191
|
def start
|
143
192
|
synchronize do
|
144
|
-
@
|
145
|
-
@start_at = Time.now
|
193
|
+
@timer.start
|
146
194
|
@meter.start
|
147
195
|
end
|
148
196
|
|
@@ -162,17 +210,21 @@ module TTY
|
|
162
210
|
if progress.respond_to?(:to_hash)
|
163
211
|
tokens, progress = progress, 1
|
164
212
|
end
|
165
|
-
@
|
166
|
-
@current
|
167
|
-
|
213
|
+
@timer.start
|
214
|
+
@current += progress
|
215
|
+
# When progress is unknown increase by 2% up to max 200%, after
|
216
|
+
# that reset back to 0%
|
217
|
+
@unknown += 2 if indeterminate?
|
218
|
+
@unknown = 0 if @unknown > 199
|
219
|
+
@tokens = tokens
|
168
220
|
@meter.sample(Time.now, progress)
|
169
221
|
|
170
|
-
if !
|
222
|
+
if !indeterminate? && @current >= total
|
171
223
|
finish && return
|
172
224
|
end
|
173
225
|
|
174
|
-
|
175
|
-
|
226
|
+
return if (Time.now - @last_render_time) < @render_period
|
227
|
+
|
176
228
|
render
|
177
229
|
end
|
178
230
|
end
|
@@ -189,7 +241,7 @@ module TTY
|
|
189
241
|
# be convenient in "unsure number of iterations" situations
|
190
242
|
# (like downloading in chunks, when server may eventually send
|
191
243
|
# more chunks than predicted), but be careful to not pass infinite
|
192
|
-
# enumerators without
|
244
|
+
# enumerators without previously doing `.take(some_finite_number)`
|
193
245
|
# on them.
|
194
246
|
#
|
195
247
|
# @example
|
@@ -256,12 +308,20 @@ module TTY
|
|
256
308
|
|
257
309
|
# Ratio of completed over total steps
|
258
310
|
#
|
311
|
+
# When the total is unknown the progress ratio oscillates
|
312
|
+
# by going up from 0 to 1 and then down from 1 to 0 and
|
313
|
+
# up again to infinity.
|
314
|
+
#
|
259
315
|
# @return [Float]
|
260
316
|
#
|
261
317
|
# @api public
|
262
318
|
def ratio
|
263
319
|
synchronize do
|
264
|
-
proportion =
|
320
|
+
proportion = if total
|
321
|
+
total > 0 ? (@current.to_f / total) : 0
|
322
|
+
else
|
323
|
+
(@unknown > 100 ? 200 - @unknown : @unknown).to_f / 100
|
324
|
+
end
|
265
325
|
[[proportion, 0].max, 1].min
|
266
326
|
end
|
267
327
|
end
|
@@ -271,7 +331,9 @@ module TTY
|
|
271
331
|
# @api private
|
272
332
|
def render
|
273
333
|
return if done?
|
274
|
-
|
334
|
+
|
335
|
+
if hide_cursor && @last_render_width == 0 &&
|
336
|
+
(indeterminate? || @current < total)
|
275
337
|
write(TTY::Cursor.hide)
|
276
338
|
end
|
277
339
|
|
@@ -280,7 +342,7 @@ module TTY
|
|
280
342
|
update(inset: self.class.display_columns(characters_in))
|
281
343
|
end
|
282
344
|
|
283
|
-
formatted = @
|
345
|
+
formatted = @formatters.decorate(@format)
|
284
346
|
@tokens.each do |token, val|
|
285
347
|
formatted = formatted.gsub(":#{token}", val)
|
286
348
|
end
|
@@ -303,7 +365,7 @@ module TTY
|
|
303
365
|
if @first_render
|
304
366
|
@row = @multibar.next_row
|
305
367
|
yield if block_given?
|
306
|
-
output.print
|
368
|
+
output.print NEWLINE
|
307
369
|
@first_render = false
|
308
370
|
else
|
309
371
|
lines_up = (@multibar.rows + 1) - @row
|
@@ -342,6 +404,7 @@ module TTY
|
|
342
404
|
# @api public
|
343
405
|
def resize(new_width = nil)
|
344
406
|
return if done?
|
407
|
+
|
345
408
|
synchronize do
|
346
409
|
clear_line
|
347
410
|
if new_width
|
@@ -355,12 +418,14 @@ module TTY
|
|
355
418
|
# @api public
|
356
419
|
def finish
|
357
420
|
return if done?
|
358
|
-
|
421
|
+
|
422
|
+
@current = total unless indeterminate?
|
359
423
|
render
|
360
|
-
clear ? clear_line : write(
|
424
|
+
clear ? clear_line : write(NEWLINE, false)
|
361
425
|
ensure
|
362
426
|
@meter.clear
|
363
427
|
@done = true
|
428
|
+
@timer.stop
|
364
429
|
|
365
430
|
# reenable cursor if it is turned off
|
366
431
|
if hide_cursor && @last_render_width != 0
|
@@ -370,31 +435,57 @@ module TTY
|
|
370
435
|
emit(:done)
|
371
436
|
end
|
372
437
|
|
438
|
+
# Resume rendering when bar is done, stopped or paused
|
439
|
+
#
|
440
|
+
# @api public
|
441
|
+
def resume
|
442
|
+
synchronize do
|
443
|
+
@done = false
|
444
|
+
@stopped = false
|
445
|
+
@paused = false
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
373
449
|
# Stop and cancel the progress at the current position
|
374
450
|
#
|
375
451
|
# @api public
|
376
452
|
def stop
|
377
|
-
# reenable cursor if it is turned off
|
378
|
-
if hide_cursor && @last_render_width != 0
|
379
|
-
write(TTY::Cursor.show, false)
|
380
|
-
end
|
381
453
|
return if done?
|
454
|
+
|
382
455
|
render
|
383
|
-
clear ? clear_line : write(
|
456
|
+
clear ? clear_line : write(NEWLINE, false)
|
384
457
|
ensure
|
385
458
|
@meter.clear
|
386
459
|
@stopped = true
|
460
|
+
@timer.stop
|
461
|
+
|
462
|
+
# reenable cursor if it is turned off
|
463
|
+
if hide_cursor && @last_render_width != 0
|
464
|
+
write(TTY::Cursor.show, false)
|
465
|
+
end
|
466
|
+
|
387
467
|
emit(:stopped)
|
388
468
|
end
|
389
469
|
|
470
|
+
# Pause the progress at the current position
|
471
|
+
#
|
472
|
+
# @api public
|
473
|
+
def pause
|
474
|
+
synchronize do
|
475
|
+
@paused = true
|
476
|
+
@timer.stop
|
477
|
+
emit(:paused)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
390
481
|
# Clear current line
|
391
482
|
#
|
392
483
|
# @api public
|
393
484
|
def clear_line
|
394
|
-
output.print(ECMA_CSI
|
485
|
+
output.print("#{ECMA_CSI}0m#{TTY::Cursor.clear_line}")
|
395
486
|
end
|
396
487
|
|
397
|
-
# Check if progress is
|
488
|
+
# Check if progress is finished
|
398
489
|
#
|
399
490
|
# @return [Boolean]
|
400
491
|
# true when progress finished, false otherwise
|
@@ -413,13 +504,22 @@ module TTY
|
|
413
504
|
@stopped
|
414
505
|
end
|
415
506
|
|
416
|
-
# Check if progress is
|
507
|
+
# Check if progress is paused
|
508
|
+
#
|
509
|
+
# @return [Boolean]
|
510
|
+
#
|
511
|
+
# @api public
|
512
|
+
def paused?
|
513
|
+
@paused
|
514
|
+
end
|
515
|
+
|
516
|
+
# Check if progress is finished, stopped or paused
|
417
517
|
#
|
418
518
|
# @return [Boolean]
|
419
519
|
#
|
420
520
|
# @api public
|
421
521
|
def done?
|
422
|
-
@done || @stopped
|
522
|
+
@done || @stopped || @paused
|
423
523
|
end
|
424
524
|
|
425
525
|
# Register callback with this bar
|
@@ -444,14 +544,14 @@ module TTY
|
|
444
544
|
#
|
445
545
|
# @api public
|
446
546
|
def log(message)
|
447
|
-
sanitized_message = message.gsub(/\r|\n/,
|
547
|
+
sanitized_message = message.gsub(/\r|\n/, " ")
|
448
548
|
if done?
|
449
|
-
write(sanitized_message
|
549
|
+
write("#{sanitized_message}#{NEWLINE}", false)
|
450
550
|
return
|
451
551
|
end
|
452
552
|
sanitized_message = padout(sanitized_message)
|
453
553
|
|
454
|
-
write(sanitized_message
|
554
|
+
write("#{sanitized_message}#{NEWLINE}", true)
|
455
555
|
render
|
456
556
|
end
|
457
557
|
|
@@ -471,13 +571,14 @@ module TTY
|
|
471
571
|
# @api public
|
472
572
|
def inspect
|
473
573
|
"#<#{self.class.name} " \
|
474
|
-
"@format=\"#{format}\", " \
|
574
|
+
"@format=\"#{@format}\", " \
|
475
575
|
"@current=\"#{@current}\", " \
|
476
576
|
"@total=\"#{total}\", " \
|
477
577
|
"@width=\"#{width}\", " \
|
478
578
|
"@complete=\"#{complete}\", " \
|
479
579
|
"@head=\"#{head}\", " \
|
480
580
|
"@incomplete=\"#{incomplete}\", " \
|
581
|
+
"@unknown=\"#{unknown}\", " \
|
481
582
|
"@interval=\"#{interval}\">"
|
482
583
|
end
|
483
584
|
|
@@ -491,7 +592,7 @@ module TTY
|
|
491
592
|
|
492
593
|
if @last_render_width > message_length
|
493
594
|
remaining_width = @last_render_width - message_length
|
494
|
-
message +=
|
595
|
+
message += " " * remaining_width
|
495
596
|
end
|
496
597
|
message
|
497
598
|
end
|
@@ -504,7 +605,7 @@ module TTY
|
|
504
605
|
# @api private
|
505
606
|
def emit(name, *args)
|
506
607
|
@callbacks[name].each do |callback|
|
507
|
-
callback.
|
608
|
+
callback.(*args)
|
508
609
|
end
|
509
610
|
end
|
510
611
|
|