ruby2js 3.2.0 → 3.3.4
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.
- checksums.yaml +4 -4
- data/README.md +45 -5
- data/lib/ruby2js.rb +14 -2
- data/lib/ruby2js/converter.rb +11 -3
- data/lib/ruby2js/converter/args.rb +1 -1
- data/lib/ruby2js/converter/block.rb +2 -2
- data/lib/ruby2js/converter/class.rb +4 -3
- data/lib/ruby2js/converter/class2.rb +38 -9
- data/lib/ruby2js/converter/cvar.rb +1 -1
- data/lib/ruby2js/converter/cvasgn.rb +1 -1
- data/lib/ruby2js/converter/def.rb +2 -2
- data/lib/ruby2js/converter/for.rb +7 -0
- data/lib/ruby2js/converter/hash.rb +17 -3
- data/lib/ruby2js/converter/if.rb +13 -2
- data/lib/ruby2js/converter/import.rb +38 -0
- data/lib/ruby2js/converter/logical.rb +46 -1
- data/lib/ruby2js/converter/opasgn.rb +5 -2
- data/lib/ruby2js/converter/regexp.rb +27 -2
- data/lib/ruby2js/converter/return.rb +1 -1
- data/lib/ruby2js/converter/send.rb +53 -27
- data/lib/ruby2js/converter/super.rb +15 -9
- data/lib/ruby2js/converter/xnode.rb +89 -0
- data/lib/ruby2js/es2021.rb +5 -0
- data/lib/ruby2js/es2021/strict.rb +3 -0
- data/lib/ruby2js/filter/cjs.rb +2 -2
- data/lib/ruby2js/filter/esm.rb +72 -0
- data/lib/ruby2js/filter/functions.rb +142 -26
- data/lib/ruby2js/filter/matchAll.rb +49 -0
- data/lib/ruby2js/filter/node.rb +18 -10
- data/lib/ruby2js/filter/nokogiri.rb +13 -13
- data/lib/ruby2js/filter/react.rb +190 -30
- data/lib/ruby2js/filter/require.rb +1 -4
- data/lib/ruby2js/filter/rubyjs.rb +4 -4
- data/lib/ruby2js/filter/vue.rb +15 -15
- data/lib/ruby2js/filter/wunderbar.rb +63 -0
- data/lib/ruby2js/serializer.rb +25 -11
- data/lib/ruby2js/version.rb +2 -2
- data/ruby2js.gemspec +3 -12
- metadata +25 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Ruby2JS
|
|
2
|
+
class Converter
|
|
3
|
+
|
|
4
|
+
# (xnode str hash)
|
|
5
|
+
|
|
6
|
+
# NOTE: xnode is a synthetic
|
|
7
|
+
|
|
8
|
+
handle :xnode do |nodename, *args|
|
|
9
|
+
attrs = {}
|
|
10
|
+
children = []
|
|
11
|
+
|
|
12
|
+
args.each do |arg|
|
|
13
|
+
if arg.type == :hash
|
|
14
|
+
arg.children.each do |pair|
|
|
15
|
+
name = pair.children[0].children[0]
|
|
16
|
+
|
|
17
|
+
if defined? Ruby2JS::Filter::React
|
|
18
|
+
name = :className if name == :class
|
|
19
|
+
name = :htmlFor if name == :for
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if [:class, :className].include? name and attrs[name]
|
|
23
|
+
if attrs[name].type == :str and pair.children[1].type == :str
|
|
24
|
+
attrs[name] = s(:str, pair.children[1].children[0] + ' ' +
|
|
25
|
+
attrs[name].children[0])
|
|
26
|
+
else
|
|
27
|
+
attrs[name] = s(:send, s(:send, attrs[name], :+,
|
|
28
|
+
s(:str, ' ')), :+, pair.children[1])
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
attrs[name] = pair.children[1]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
elsif arg.type == :begin
|
|
35
|
+
children += arg.children
|
|
36
|
+
else
|
|
37
|
+
children << arg
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
put '<'
|
|
42
|
+
put nodename
|
|
43
|
+
|
|
44
|
+
attrs.each do |name, value|
|
|
45
|
+
put ' '
|
|
46
|
+
put name
|
|
47
|
+
put '='
|
|
48
|
+
if value.type == :str
|
|
49
|
+
parse value
|
|
50
|
+
else
|
|
51
|
+
put '{'
|
|
52
|
+
parse value
|
|
53
|
+
put '}'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if children.empty?
|
|
58
|
+
put '/>'
|
|
59
|
+
else
|
|
60
|
+
put '>'
|
|
61
|
+
put @nl unless children.length == 1 and children.first.type != :xnode
|
|
62
|
+
|
|
63
|
+
children.each_with_index do |child, index|
|
|
64
|
+
put @nl unless index == 0
|
|
65
|
+
if child.type == :str
|
|
66
|
+
put child.children.first
|
|
67
|
+
elsif child.type == :xnode
|
|
68
|
+
parse child
|
|
69
|
+
else
|
|
70
|
+
begin
|
|
71
|
+
jsx, @jsx = @jsx, true
|
|
72
|
+
put '{'
|
|
73
|
+
parse child
|
|
74
|
+
put '}'
|
|
75
|
+
ensure
|
|
76
|
+
@jsx = jsx
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
put @nl unless children.length == 1 and children.first.type != :xnode
|
|
82
|
+
|
|
83
|
+
put '</'
|
|
84
|
+
put nodename
|
|
85
|
+
put ">"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/ruby2js/filter/cjs.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Ruby2JS
|
|
|
24
24
|
*assign.children[1..-1]
|
|
25
25
|
])
|
|
26
26
|
|
|
27
|
-
elsif
|
|
27
|
+
elsif \
|
|
28
28
|
node.children[2].type == :send and
|
|
29
29
|
node.children[2].children[0..1] == [nil, :async] and
|
|
30
30
|
node.children[2].children[2].type == :def
|
|
@@ -38,7 +38,7 @@ module Ruby2JS
|
|
|
38
38
|
*process_all(fn.children[1..-1])))
|
|
39
39
|
])
|
|
40
40
|
|
|
41
|
-
elsif
|
|
41
|
+
elsif \
|
|
42
42
|
node.children[2].type == :send and
|
|
43
43
|
node.children[2].children[0..1] == [nil, :default]
|
|
44
44
|
then
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'ruby2js'
|
|
2
|
+
|
|
3
|
+
module Ruby2JS
|
|
4
|
+
module Filter
|
|
5
|
+
module ESM
|
|
6
|
+
include SEXP
|
|
7
|
+
|
|
8
|
+
def initialize(*args)
|
|
9
|
+
@esm_include = nil
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def process(node)
|
|
14
|
+
return super if @esm_include
|
|
15
|
+
@esm_include = Set.new
|
|
16
|
+
@esm_exclude = Set.new
|
|
17
|
+
@esm_export = nil
|
|
18
|
+
result = super
|
|
19
|
+
|
|
20
|
+
esm_walk(result)
|
|
21
|
+
|
|
22
|
+
inventory = (@esm_include - @esm_exclude).to_a.sort
|
|
23
|
+
|
|
24
|
+
if inventory.empty? and not @esm_export
|
|
25
|
+
result
|
|
26
|
+
else
|
|
27
|
+
list = inventory.map do |name|
|
|
28
|
+
if name == "React" and defined? Ruby2JS::Filter::React
|
|
29
|
+
s(:import, "#{name.downcase}", s(:const, nil, name))
|
|
30
|
+
elsif not %w(JSON Object).include? name
|
|
31
|
+
s(:import, "./#{name.downcase}.js", s(:const, nil, name))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
list.push result
|
|
36
|
+
|
|
37
|
+
if @esm_export
|
|
38
|
+
list.push s(:export, :default, s(:const, nil, @esm_export))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
s(:begin, *list.compact)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# gather constants
|
|
46
|
+
def esm_walk(node)
|
|
47
|
+
# extract ivars and cvars
|
|
48
|
+
if node.type == :const and node.children.first == nil
|
|
49
|
+
@esm_include << node.children.last.to_s
|
|
50
|
+
elsif node.type == :xnode
|
|
51
|
+
name = node.children.first
|
|
52
|
+
@esm_include << name unless name.empty? or name =~ /^[a-z]/
|
|
53
|
+
elsif node.type == :casgn and node.children.first == nil
|
|
54
|
+
@esm_exclude << node.children[1].to_s
|
|
55
|
+
elsif node.type == :class and node.children.first.type == :const
|
|
56
|
+
if node.children.first.children.first == nil
|
|
57
|
+
name = node.children.first.children.last.to_s
|
|
58
|
+
@esm_exclude << name
|
|
59
|
+
@esm_export ||= name
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# recurse
|
|
64
|
+
node.children.each do |child|
|
|
65
|
+
esm_walk(child) if Parser::AST::Node === child
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
DEFAULTS.push ESM
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'ruby2js'
|
|
2
|
+
require 'regexp_parser'
|
|
2
3
|
|
|
3
4
|
module Ruby2JS
|
|
4
5
|
module Filter
|
|
@@ -15,6 +16,11 @@ module Ruby2JS
|
|
|
15
16
|
gvar: :gvasgn
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
def initialize(*args)
|
|
20
|
+
@jsx = false
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
def on_send(node)
|
|
19
25
|
target, method, *args = node.children
|
|
20
26
|
return super if excluded?(method)
|
|
@@ -35,6 +41,78 @@ module Ruby2JS
|
|
|
35
41
|
elsif method == :keys and args.length == 0 and node.is_method?
|
|
36
42
|
process S(:send, s(:const, nil, :Object), :keys, target)
|
|
37
43
|
|
|
44
|
+
elsif method == :[]= and args.length == 3 and
|
|
45
|
+
args[0].type == :regexp and args[1].type == :int
|
|
46
|
+
index = args[1].children.first
|
|
47
|
+
|
|
48
|
+
parts = Regexp::Parser.parse(args[0].children.first.children.first)
|
|
49
|
+
split = parts.index do |part|
|
|
50
|
+
part.type == :group and part.number == index
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return super unless split
|
|
54
|
+
|
|
55
|
+
rewritten = parts[split].to_s
|
|
56
|
+
|
|
57
|
+
dstr = [args.last]
|
|
58
|
+
if rewritten == '()'
|
|
59
|
+
index -= 1
|
|
60
|
+
rewritten = ''
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
parts = parts.to_a
|
|
64
|
+
|
|
65
|
+
pre = ''
|
|
66
|
+
if parts.first.type == :anchor
|
|
67
|
+
pre = parts.shift.to_s
|
|
68
|
+
split -= 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if split > 0
|
|
72
|
+
if split == 1 and parts.first.type == :group
|
|
73
|
+
rewritten = parts.first.to_s + rewritten
|
|
74
|
+
else
|
|
75
|
+
rewritten = '(' + parts[0 .. split - 1].join + ')' + rewritten
|
|
76
|
+
index += 1
|
|
77
|
+
end
|
|
78
|
+
dstr.unshift s(:send, s(:lvar, :match), :[], s(:int, 0))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
post = ''
|
|
83
|
+
if parts.last.type == :anchor
|
|
84
|
+
post = parts.pop.to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if split + 1 < parts.length
|
|
88
|
+
if split + 2 == parts.length and parts.last.type == :group
|
|
89
|
+
rewritten += parts.last.to_s
|
|
90
|
+
else
|
|
91
|
+
rewritten += '(' + parts[split + 1 .. -1].join + ')'
|
|
92
|
+
end
|
|
93
|
+
dstr << s(:send, s(:lvar, :match), :[], s(:int, index))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
rewritten = pre + rewritten + post
|
|
97
|
+
|
|
98
|
+
regex = process s(:regexp, s(:str, rewritten), args[0].children.last)
|
|
99
|
+
block = s(:block,
|
|
100
|
+
s(:send, target, :replace, regex),
|
|
101
|
+
s(:args, s(:arg, :match)),
|
|
102
|
+
process(s(:dstr, *dstr)))
|
|
103
|
+
|
|
104
|
+
if VAR_TO_ASSIGN.keys.include? target.type
|
|
105
|
+
S(VAR_TO_ASSIGN[target.type], target.children.first, block)
|
|
106
|
+
elsif target.type == :send
|
|
107
|
+
if target.children[0] == nil
|
|
108
|
+
S(:lvasgn, target.children[1], block)
|
|
109
|
+
else
|
|
110
|
+
S(:send, target.children[0], :"#{target.children[1]}=", block)
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
super
|
|
114
|
+
end
|
|
115
|
+
|
|
38
116
|
elsif method == :merge
|
|
39
117
|
args.unshift target
|
|
40
118
|
|
|
@@ -146,23 +224,33 @@ module Ruby2JS
|
|
|
146
224
|
[s(:str, Regexp.escape(arg.children.first)), s(:regopt)])
|
|
147
225
|
end
|
|
148
226
|
|
|
149
|
-
pattern = arg.children.first.children.first
|
|
150
|
-
pattern = pattern.gsub(/\\./, '').gsub(/\[.*\]/, '')
|
|
151
|
-
|
|
152
227
|
if arg.type == :regexp
|
|
228
|
+
pattern = arg.children.first.children.first
|
|
229
|
+
pattern = pattern.gsub(/\\./, '').gsub(/\[.*\]/, '')
|
|
230
|
+
|
|
153
231
|
gpattern = arg.updated(:regexp, [*arg.children[0...-1],
|
|
154
232
|
s(:regopt, :g, *arg.children.last)])
|
|
155
233
|
else
|
|
156
|
-
|
|
234
|
+
gpattern = s(:send, s(:const, nil, :RegExp), :new, arg, s(:str, 'g'))
|
|
157
235
|
end
|
|
158
236
|
|
|
159
|
-
if pattern.include? '('
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
s(:
|
|
163
|
-
|
|
164
|
-
|
|
237
|
+
if arg.type != :regexp or pattern.include? '('
|
|
238
|
+
if es2020
|
|
239
|
+
# Array.from(str.matchAll(/.../g), s => s.slice(1))
|
|
240
|
+
s(:send, s(:const, nil, :Array), :from,
|
|
241
|
+
s(:send, process(target), :matchAll, gpattern),
|
|
242
|
+
s(:block, s(:send, nil, :proc), s(:args, s(:arg, :s)),
|
|
243
|
+
s(:send, s(:lvar, :s), :slice, s(:int, 1))))
|
|
244
|
+
else
|
|
245
|
+
# str.match(/.../g).map(s => s.match(/.../).slice(1))
|
|
246
|
+
s(:block, s(:send,
|
|
247
|
+
s(:send, process(target), :match, gpattern), :map),
|
|
248
|
+
s(:args, s(:arg, :s)),
|
|
249
|
+
s(:return, s(:send, s(:send, s(:lvar, :s), :match, arg),
|
|
250
|
+
:slice, s(:int, 1))))
|
|
251
|
+
end
|
|
165
252
|
else
|
|
253
|
+
# str.match(/.../g)
|
|
166
254
|
S(:send, process(target), :match, gpattern)
|
|
167
255
|
end
|
|
168
256
|
|
|
@@ -290,6 +378,9 @@ module Ruby2JS
|
|
|
290
378
|
end
|
|
291
379
|
|
|
292
380
|
|
|
381
|
+
elsif method == :[] and target == s(:const, nil, :Hash)
|
|
382
|
+
s(:send, s(:const, nil, :Object), :fromEntries, *process_all(args))
|
|
383
|
+
|
|
293
384
|
elsif method == :[]
|
|
294
385
|
# resolve negative literal indexes
|
|
295
386
|
i = proc do |index|
|
|
@@ -307,9 +398,15 @@ module Ruby2JS
|
|
|
307
398
|
super
|
|
308
399
|
|
|
309
400
|
elsif index.type == :regexp
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
401
|
+
if es2020
|
|
402
|
+
process S(:csend,
|
|
403
|
+
S(:send, process(target), :match, index),
|
|
404
|
+
:[], args[1] || s(:int, 0))
|
|
405
|
+
else
|
|
406
|
+
process S(:send,
|
|
407
|
+
s(:or, S(:send, process(target), :match, index), s(:array)),
|
|
408
|
+
:[], args[1] || s(:int, 0))
|
|
409
|
+
end
|
|
313
410
|
|
|
314
411
|
elsif node.children.length != 3
|
|
315
412
|
super
|
|
@@ -319,7 +416,9 @@ module Ruby2JS
|
|
|
319
416
|
|
|
320
417
|
elsif index.type == :erange
|
|
321
418
|
start, finish = index.children
|
|
322
|
-
if finish
|
|
419
|
+
if not finish
|
|
420
|
+
process S(:send, target, :slice, start)
|
|
421
|
+
elsif finish.type == :int
|
|
323
422
|
process S(:send, target, :slice, i.(start), finish)
|
|
324
423
|
else
|
|
325
424
|
process S(:send, target, :slice, i.(start), i.(finish))
|
|
@@ -327,7 +426,7 @@ module Ruby2JS
|
|
|
327
426
|
|
|
328
427
|
elsif index.type == :irange
|
|
329
428
|
start, finish = index.children
|
|
330
|
-
if finish.type == :int
|
|
429
|
+
if finish and finish.type == :int
|
|
331
430
|
final = S(:int, finish.children.first+1)
|
|
332
431
|
else
|
|
333
432
|
final = S(:send, finish, :+, s(:int, 1))
|
|
@@ -335,7 +434,7 @@ module Ruby2JS
|
|
|
335
434
|
|
|
336
435
|
# No need for the last argument if it's -1
|
|
337
436
|
# This means take all to the end of array
|
|
338
|
-
if finish.children.first == -1
|
|
437
|
+
if not finish or finish.children.first == -1
|
|
339
438
|
process S(:send, target, :slice, start)
|
|
340
439
|
else
|
|
341
440
|
process S(:send, target, :slice, start, final)
|
|
@@ -407,15 +506,28 @@ module Ruby2JS
|
|
|
407
506
|
process node.updated(nil, [s(:const, nil, :Object), :fromEntries,
|
|
408
507
|
target])
|
|
409
508
|
|
|
410
|
-
elsif
|
|
411
|
-
|
|
509
|
+
elsif method==:rstrip
|
|
510
|
+
if es2019
|
|
511
|
+
process node.updated(nil, [target, :trimEnd, *args])
|
|
512
|
+
else
|
|
513
|
+
node.updated(nil, [process(target), :replace,
|
|
514
|
+
s(:regexp, s(:str, '\s+\z') , s(:regopt)), s(:str, '')])
|
|
515
|
+
end
|
|
412
516
|
|
|
413
|
-
elsif
|
|
414
|
-
|
|
517
|
+
elsif method==:lstrip and args.length == 0
|
|
518
|
+
if es2019
|
|
519
|
+
process s(:send, target, :trimStart)
|
|
520
|
+
else
|
|
521
|
+
node.updated(nil, [process(target), :replace,
|
|
522
|
+
s(:regexp, s(:str, '\A\s+') , s(:regopt)), s(:str, '')])
|
|
523
|
+
end
|
|
415
524
|
|
|
416
525
|
elsif method == :class and args.length==0 and not node.is_method?
|
|
417
526
|
process node.updated(:attr, [target, :constructor])
|
|
418
527
|
|
|
528
|
+
elsif method == :new and target == s(:const, nil, :Exception)
|
|
529
|
+
process S(:send, s(:const, nil, :Error), :new, *args)
|
|
530
|
+
|
|
419
531
|
else
|
|
420
532
|
super
|
|
421
533
|
end
|
|
@@ -510,7 +622,7 @@ module Ruby2JS
|
|
|
510
622
|
call = call.updated(nil, [s(:begin, range), :step, s(:int, 1)])
|
|
511
623
|
process node.updated(nil, [call, *node.children[1..-1]])
|
|
512
624
|
|
|
513
|
-
elsif
|
|
625
|
+
elsif \
|
|
514
626
|
method == :each and call.children[0].type == :send and
|
|
515
627
|
call.children[0].children[1] == :step
|
|
516
628
|
then
|
|
@@ -522,7 +634,7 @@ module Ruby2JS
|
|
|
522
634
|
:step, step])
|
|
523
635
|
process node.updated(nil, [call, *node.children[1..-1]])
|
|
524
636
|
|
|
525
|
-
elsif
|
|
637
|
+
elsif \
|
|
526
638
|
# (a..b).each {|v| ...}
|
|
527
639
|
method == :each and
|
|
528
640
|
call.children[0].type == :begin and
|
|
@@ -530,13 +642,13 @@ module Ruby2JS
|
|
|
530
642
|
[:irange, :erange].include? call.children[0].children[0].type and
|
|
531
643
|
node.children[1].children.length == 1
|
|
532
644
|
then
|
|
533
|
-
s(:for, s(:lvasgn, node.children[1].children[0].children[0]),
|
|
645
|
+
process s(:for, s(:lvasgn, node.children[1].children[0].children[0]),
|
|
534
646
|
call.children[0].children[0], node.children[2])
|
|
535
647
|
|
|
536
|
-
elsif
|
|
648
|
+
elsif \
|
|
537
649
|
[:each, :each_value].include? method
|
|
538
650
|
then
|
|
539
|
-
if es2015
|
|
651
|
+
if es2015 or @jsx
|
|
540
652
|
if node.children[1].children.length > 1
|
|
541
653
|
process node.updated(:for_of,
|
|
542
654
|
[s(:mlhs, *node.children[1].children.map {|child|
|
|
@@ -557,7 +669,7 @@ module Ruby2JS
|
|
|
557
669
|
:forEach), *node.children[1..2]])
|
|
558
670
|
end
|
|
559
671
|
|
|
560
|
-
elsif
|
|
672
|
+
elsif \
|
|
561
673
|
method == :each_key and
|
|
562
674
|
[:each, :each_key].include? method and
|
|
563
675
|
node.children[1].children.length == 1
|
|
@@ -587,6 +699,10 @@ module Ruby2JS
|
|
|
587
699
|
node.children[2])])
|
|
588
700
|
end
|
|
589
701
|
|
|
702
|
+
elsif method == :scan and call.children.length == 3
|
|
703
|
+
process call.updated(nil, [*call.children, s(:block,
|
|
704
|
+
s(:send, nil, :proc), *node.children[1..-1])])
|
|
705
|
+
|
|
590
706
|
else
|
|
591
707
|
super
|
|
592
708
|
end
|