redcarpet 3.2.3 → 3.3.0
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 redcarpet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/COPYING +0 -0
- data/README.markdown +37 -0
- data/bin/redcarpet +4 -40
- data/ext/redcarpet/buffer.c +6 -6
- data/ext/redcarpet/buffer.h +1 -1
- data/ext/redcarpet/html.c +92 -27
- data/ext/redcarpet/html.h +7 -1
- data/ext/redcarpet/markdown.c +17 -8
- data/ext/redcarpet/markdown.h +0 -3
- data/ext/redcarpet/rc_markdown.c +3 -7
- data/ext/redcarpet/rc_render.c +13 -8
- data/lib/redcarpet.rb +1 -1
- data/lib/redcarpet/compat.rb +0 -3
- data/redcarpet.gemspec +5 -4
- data/test/custom_render_test.rb +1 -1
- data/test/fixtures/benchmark.md +232 -0
- data/test/html_render_test.rb +77 -76
- data/test/html_toc_render_test.rb +35 -9
- data/test/markdown_test.rb +62 -26
- data/test/redcarpet_bin_test.rb +80 -0
- data/test/redcarpet_compat_test.rb +6 -6
- data/test/test_helper.rb +15 -11
- metadata +15 -25
@@ -3,13 +3,12 @@ require 'test_helper'
|
|
3
3
|
|
4
4
|
class HTMLTOCRenderTest < Redcarpet::TestCase
|
5
5
|
def setup
|
6
|
-
@
|
6
|
+
@renderer = Redcarpet::Render::HTML_TOC
|
7
7
|
@markdown = "# A title \n## A __nice__ subtitle\n## Another one \n### A sub-sub-title"
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_simple_toc_render
|
11
|
-
|
12
|
-
output = renderer.render(@markdown).strip
|
11
|
+
output = render(@markdown).strip
|
13
12
|
|
14
13
|
assert output.start_with?("<ul>")
|
15
14
|
assert output.end_with?("</ul>")
|
@@ -19,8 +18,7 @@ class HTMLTOCRenderTest < Redcarpet::TestCase
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def test_granular_toc_render
|
22
|
-
|
23
|
-
output = renderer.render(@markdown).strip
|
21
|
+
output = render(@markdown, with: { nesting_level: 2 }).strip
|
24
22
|
|
25
23
|
assert output.start_with?("<ul>")
|
26
24
|
assert output.end_with?("</ul>")
|
@@ -30,8 +28,7 @@ class HTMLTOCRenderTest < Redcarpet::TestCase
|
|
30
28
|
end
|
31
29
|
|
32
30
|
def test_toc_heading_id
|
33
|
-
|
34
|
-
output = renderer.render(@markdown)
|
31
|
+
output = render(@markdown)
|
35
32
|
|
36
33
|
assert_match /a-title/, output
|
37
34
|
assert_match /a-nice-subtitle/, output
|
@@ -40,10 +37,39 @@ class HTMLTOCRenderTest < Redcarpet::TestCase
|
|
40
37
|
end
|
41
38
|
|
42
39
|
def test_toc_heading_with_hyphen_and_equal
|
43
|
-
|
44
|
-
output = renderer.render("# Hello World\n\n-\n\n=")
|
40
|
+
output = render("# Hello World\n\n-\n\n=")
|
45
41
|
|
46
42
|
assert_equal 1, output.scan("<li>").length
|
47
43
|
assert !output.include?('<a href=\"#\"></a>')
|
48
44
|
end
|
45
|
+
|
46
|
+
def test_anchor_generation_with_edge_cases
|
47
|
+
# Imported from ActiveSupport::Inflector#parameterize's tests
|
48
|
+
titles = {
|
49
|
+
"Donald E. Knuth" => "donald-e-knuth",
|
50
|
+
"Random text with *(bad)* characters" => "random-text-with-bad-characters",
|
51
|
+
"!@#Surrounding bad characters!@#" => "surrounding-bad-characters",
|
52
|
+
"Squeeze separators" => "squeeze-separators",
|
53
|
+
"Test with + sign" => "test-with-sign",
|
54
|
+
"Test with a Namespaced::Class" => "test-with-a-namespaced-class"
|
55
|
+
}
|
56
|
+
|
57
|
+
titles.each do |title, anchor|
|
58
|
+
assert_match %("##{anchor}"), render("# #{title}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_inline_markup_is_not_escaped
|
63
|
+
output = render(@markdown)
|
64
|
+
|
65
|
+
assert_match "A <strong>nice</strong> subtitle", output
|
66
|
+
assert_no_match %r{<}, output
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_inline_markup_escaping
|
70
|
+
output = render(@markdown, with: [:escape_html])
|
71
|
+
|
72
|
+
assert_match "<strong>", output
|
73
|
+
assert_no_match %r{<strong>}, output
|
74
|
+
end
|
49
75
|
end
|
data/test/markdown_test.rb
CHANGED
@@ -13,37 +13,37 @@ class MarkdownTest < Redcarpet::TestCase
|
|
13
13
|
|
14
14
|
def test_that_simple_one_liner_goes_to_html
|
15
15
|
assert_respond_to @markdown, :render
|
16
|
-
|
16
|
+
assert_equal "<p>Hello World.</p>\n", @markdown.render("Hello World.")
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_that_inline_markdown_goes_to_html
|
20
20
|
markdown = @markdown.render('_Hello World_!')
|
21
|
-
|
21
|
+
assert_equal "<p><em>Hello World</em>!</p>\n", markdown
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_that_inline_markdown_starts_and_ends_correctly
|
25
25
|
markdown = render_with({:no_intra_emphasis => true}, '_start _ foo_bar bar_baz _ end_ *italic* **bold** <a>_blah_</a>')
|
26
26
|
|
27
|
-
|
27
|
+
assert_equal "<p><em>start _ foo_bar bar_baz _ end</em> <em>italic</em> <strong>bold</strong> <a><em>blah</em></a></p>\n", markdown
|
28
28
|
|
29
29
|
markdown = @markdown.render("Run 'rake radiant:extensions:rbac_base:migrate'")
|
30
|
-
|
30
|
+
assert_equal "<p>Run 'rake radiant:extensions:rbac_base:migrate'</p>\n", markdown
|
31
31
|
end
|
32
32
|
|
33
33
|
def test_that_urls_are_not_doubly_escaped
|
34
34
|
markdown = @markdown.render('[Page 2](/search?query=Markdown+Test&page=2)')
|
35
|
-
|
35
|
+
assert_equal "<p><a href=\"/search?query=Markdown+Test&page=2\">Page 2</a></p>\n", markdown
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_simple_inline_html
|
39
39
|
#markdown = Markdown.new("before\n\n<div>\n foo\n</div>\nafter")
|
40
40
|
markdown = @markdown.render("before\n\n<div>\n foo\n</div>\n\nafter")
|
41
|
-
|
41
|
+
assert_equal "<p>before</p>\n\n<div>\n foo\n</div>\n\n<p>after</p>\n", markdown
|
42
42
|
end
|
43
43
|
|
44
44
|
def test_that_html_blocks_do_not_require_their_own_end_tag_line
|
45
45
|
markdown = @markdown.render("Para 1\n\n<div><pre>HTML block\n</pre></div>\n\nPara 2 [Link](#anchor)")
|
46
|
-
|
46
|
+
assert_equal "<p>Para 1</p>\n\n<div><pre>HTML block\n</pre></div>\n\n<p>Para 2 <a href=\"#anchor\">Link</a></p>\n",
|
47
47
|
markdown
|
48
48
|
end
|
49
49
|
|
@@ -53,8 +53,8 @@ class MarkdownTest < Redcarpet::TestCase
|
|
53
53
|
"A wise man once said:\n\n" +
|
54
54
|
" > Isn't it wonderful just to be alive.\n"
|
55
55
|
)
|
56
|
-
|
57
|
-
"<blockquote
|
56
|
+
assert_equal "<p>A wise man once said:</p>\n\n" +
|
57
|
+
"<blockquote>\n<p>Isn't it wonderful just to be alive.</p>\n</blockquote>\n",
|
58
58
|
markdown
|
59
59
|
end
|
60
60
|
|
@@ -63,7 +63,7 @@ class MarkdownTest < Redcarpet::TestCase
|
|
63
63
|
"Things to watch out for\n" +
|
64
64
|
"<ul>\n<li>Blah</li>\n</ul>\n")
|
65
65
|
|
66
|
-
|
66
|
+
assert_equal "<p>Things to watch out for</p>\n\n" +
|
67
67
|
"<ul>\n<li>Blah</li>\n</ul>\n", markdown
|
68
68
|
end
|
69
69
|
|
@@ -85,7 +85,7 @@ MARKDOWN
|
|
85
85
|
|
86
86
|
<p>This paragraph is not part of the list.</p>
|
87
87
|
HTML
|
88
|
-
|
88
|
+
assert_equal expected, @markdown.render(text)
|
89
89
|
end
|
90
90
|
|
91
91
|
# http://github.com/rtomayko/rdiscount/issues/#issue/13
|
@@ -93,37 +93,37 @@ HTML
|
|
93
93
|
text = "The Ant-Sugar Tales \n" +
|
94
94
|
"=================== \n\n" +
|
95
95
|
"By Candice Yellowflower \n"
|
96
|
-
|
96
|
+
assert_equal "<h1>The Ant-Sugar Tales </h1>\n\n<p>By Candice Yellowflower </p>\n", @markdown.render(text)
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_that_intra_emphasis_works
|
100
100
|
rd = render_with({}, "foo_bar_baz")
|
101
|
-
|
101
|
+
assert_equal "<p>foo<em>bar</em>baz</p>\n", rd
|
102
102
|
|
103
103
|
rd = render_with({:no_intra_emphasis => true},"foo_bar_baz")
|
104
|
-
|
104
|
+
assert_equal "<p>foo_bar_baz</p>\n", rd
|
105
105
|
end
|
106
106
|
|
107
107
|
def test_that_autolink_flag_works
|
108
108
|
rd = render_with({:autolink => true}, "http://github.com/rtomayko/rdiscount")
|
109
|
-
|
109
|
+
assert_equal "<p><a href=\"http://github.com/rtomayko/rdiscount\">http://github.com/rtomayko/rdiscount</a></p>\n", rd
|
110
110
|
end
|
111
111
|
|
112
112
|
def test_that_tags_can_have_dashes_and_underscores
|
113
113
|
rd = @markdown.render("foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b>")
|
114
|
-
|
114
|
+
assert_equal "<p>foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b></p>\n", rd
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_link_syntax_is_not_processed_within_code_blocks
|
118
118
|
markdown = @markdown.render(" This is a code block\n This is a link [[1]] inside\n")
|
119
|
-
|
119
|
+
assert_equal "<pre><code>This is a code block\nThis is a link [[1]] inside\n</code></pre>\n",
|
120
120
|
markdown
|
121
121
|
end
|
122
122
|
|
123
123
|
def test_whitespace_after_urls
|
124
124
|
rd = render_with({:autolink => true}, "Japan: http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm (yes, japan)")
|
125
125
|
exp = %{<p>Japan: <a href="http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm">http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm</a> (yes, japan)</p>\n}
|
126
|
-
|
126
|
+
assert_equal exp, rd
|
127
127
|
end
|
128
128
|
|
129
129
|
def test_memory_leak_when_parsing_char_links
|
@@ -142,7 +142,7 @@ HTML
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def test_infinite_loop_in_header
|
145
|
-
|
145
|
+
assert_equal "<h1>Body</h1>\n", @markdown.render(<<-header)
|
146
146
|
######
|
147
147
|
#Body#
|
148
148
|
######
|
@@ -150,8 +150,8 @@ HTML
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def test_a_hyphen_and_a_equal_should_not_be_converted_to_heading
|
153
|
-
|
154
|
-
|
153
|
+
assert_equal "<p>-</p>\n", @markdown.render("-")
|
154
|
+
assert_equal "<p>=</p>\n", @markdown.render("=")
|
155
155
|
end
|
156
156
|
|
157
157
|
def test_that_tables_flag_works
|
@@ -238,6 +238,32 @@ fenced
|
|
238
238
|
assert !out.include?("<pre><code>")
|
239
239
|
end
|
240
240
|
|
241
|
+
def test_that_indented_code_preserves_references
|
242
|
+
text = <<indented
|
243
|
+
This is normal text
|
244
|
+
|
245
|
+
Link to [Google][1]
|
246
|
+
|
247
|
+
[1]: http://google.com
|
248
|
+
indented
|
249
|
+
out = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :fenced_code_blocks => true).render(text)
|
250
|
+
assert out.include?("[1]: http://google.com")
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_that_fenced_flag_preserves_references
|
254
|
+
text = <<fenced
|
255
|
+
This is normal text
|
256
|
+
|
257
|
+
```
|
258
|
+
Link to [Google][1]
|
259
|
+
|
260
|
+
[1]: http://google.com
|
261
|
+
```
|
262
|
+
fenced
|
263
|
+
out = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :fenced_code_blocks => true).render(text)
|
264
|
+
assert out.include?("[1]: http://google.com")
|
265
|
+
end
|
266
|
+
|
241
267
|
def test_that_indented_flag_works
|
242
268
|
text = <<indented
|
243
269
|
This is a simple text
|
@@ -254,14 +280,14 @@ indented
|
|
254
280
|
|
255
281
|
def test_that_headers_are_linkable
|
256
282
|
markdown = @markdown.render('### Hello [GitHub](http://github.com)')
|
257
|
-
|
283
|
+
assert_equal "<h3>Hello <a href=\"http://github.com\">GitHub</a></h3>\n", markdown
|
258
284
|
end
|
259
285
|
|
260
286
|
def test_autolinking_with_ent_chars
|
261
287
|
markdown = render_with({:autolink => true}, <<text)
|
262
288
|
This a stupid link: https://github.com/rtomayko/tilt/issues?milestone=1&state=open
|
263
289
|
text
|
264
|
-
|
290
|
+
assert_equal "<p>This a stupid link: <a href=\"https://github.com/rtomayko/tilt/issues?milestone=1&state=open\">https://github.com/rtomayko/tilt/issues?milestone=1&state=open</a></p>\n", markdown
|
265
291
|
end
|
266
292
|
|
267
293
|
def test_spaced_headers
|
@@ -287,13 +313,13 @@ text
|
|
287
313
|
|
288
314
|
def test_emphasis_escaping
|
289
315
|
markdown = @markdown.render("**foo\\*** _dd\\_dd_")
|
290
|
-
|
316
|
+
assert_equal "<p><strong>foo*</strong> <em>dd_dd</em></p>\n", markdown
|
291
317
|
end
|
292
318
|
|
293
319
|
def test_char_escaping_when_highlighting
|
294
320
|
markdown = "==attribute\\==="
|
295
321
|
output = render_with({highlight: true}, markdown)
|
296
|
-
|
322
|
+
assert_equal "<p><mark>attribute=</mark></p>\n", output
|
297
323
|
end
|
298
324
|
|
299
325
|
def test_ordered_lists_with_lax_spacing
|
@@ -306,7 +332,17 @@ text
|
|
306
332
|
|
307
333
|
def test_references_with_tabs_after_colon
|
308
334
|
markdown = @markdown.render("[Link][id]\n[id]:\t\t\thttp://google.es")
|
309
|
-
|
335
|
+
assert_equal "<p><a href=\"http://google.es\">Link</a></p>\n", markdown
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_superscript
|
339
|
+
markdown = render_with({:superscript => true}, "this is the 2^nd time")
|
340
|
+
assert_equal "<p>this is the 2<sup>nd</sup> time</p>\n", markdown
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_superscript_enclosed_in_parenthesis
|
344
|
+
markdown = render_with({:superscript => true}, "this is the 2^(nd) time")
|
345
|
+
assert_equal "<p>this is the 2<sup>nd</sup> time</p>\n", markdown
|
310
346
|
end
|
311
347
|
|
312
348
|
def test_no_rewind_into_previous_inline
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class RedcarpetBinTest < Redcarpet::TestCase
|
5
|
+
def setup
|
6
|
+
@fixture_file = Tempfile.new('bin')
|
7
|
+
@fixture_path = @fixture_file.path
|
8
|
+
|
9
|
+
@fixture_file.write "A ==simple== fixture file -- with " \
|
10
|
+
"a [link](https://github.com)."
|
11
|
+
@fixture_file.rewind
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@fixture_file.unlink
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_vanilla_bin
|
19
|
+
run_bin(@fixture_path)
|
20
|
+
|
21
|
+
expected = "<p>A ==simple== fixture file -- with " \
|
22
|
+
"a <a href=\"https://github.com\">link</a>.</p>\n"
|
23
|
+
|
24
|
+
assert_equal expected, @output
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_enabling_a_parse_option
|
28
|
+
run_bin("--parse", "highlight", @fixture_path)
|
29
|
+
|
30
|
+
assert_output "<mark>"
|
31
|
+
refute_output "=="
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_enabling_a_render_option
|
35
|
+
run_bin("--render", "no-links", @fixture_path)
|
36
|
+
|
37
|
+
assert_output "[link]"
|
38
|
+
refute_output "</a>"
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_enabling_smarty_pants
|
42
|
+
run_bin("--smarty", @fixture_path)
|
43
|
+
|
44
|
+
assert_output "&ndash"
|
45
|
+
refute_output "--"
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_version_option
|
49
|
+
run_bin("--version")
|
50
|
+
assert_output "Redcarpet #{Redcarpet::VERSION}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_legacy_option_parsing
|
54
|
+
run_bin("--parse-highlight", "--render-no-links", @fixture_path)
|
55
|
+
|
56
|
+
assert_output "<mark>"
|
57
|
+
refute_output "=="
|
58
|
+
|
59
|
+
assert_output "[link]"
|
60
|
+
refute_output "</a>"
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def run_bin(*args)
|
66
|
+
bin_path = File.expand_path('../../bin/redcarpet', __FILE__)
|
67
|
+
|
68
|
+
IO.popen("#{bin_path} #{args.join(" ")}") do |stream|
|
69
|
+
@output = stream.read
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def assert_output(pattern)
|
74
|
+
assert_match pattern, @output
|
75
|
+
end
|
76
|
+
|
77
|
+
def refute_output(pattern)
|
78
|
+
refute_match Regexp.new(pattern), @output
|
79
|
+
end
|
80
|
+
end
|
@@ -4,35 +4,35 @@ require 'test_helper'
|
|
4
4
|
class RedcarpetCompatTest < Redcarpet::TestCase
|
5
5
|
def test_simple_compat_api
|
6
6
|
html = RedcarpetCompat.new("This is_just_a test").to_html
|
7
|
-
|
7
|
+
assert_equal "<p>This is<em>just</em>a test</p>\n", html
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_compat_api_enables_extensions
|
11
11
|
html = RedcarpetCompat.new("This is_just_a test", :no_intra_emphasis).to_html
|
12
|
-
|
12
|
+
assert_equal "<p>This is_just_a test</p>\n", html
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_compat_api_knows_fenced_code_extension
|
16
16
|
text = "```ruby\nx = 'foo'\n```"
|
17
17
|
html = RedcarpetCompat.new(text, :fenced_code).to_html
|
18
|
-
|
18
|
+
assert_equal "<pre><code class=\"ruby\">x = 'foo'\n</code></pre>\n", html
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_compat_api_ignores_gh_blockcode_extension
|
22
22
|
text = "```ruby\nx = 'foo'\n```"
|
23
23
|
html = RedcarpetCompat.new(text, :fenced_code, :gh_blockcode).to_html
|
24
|
-
|
24
|
+
assert_equal "<pre><code class=\"ruby\">x = 'foo'\n</code></pre>\n", html
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_compat_api_knows_no_intraemphasis_extension
|
28
28
|
html = RedcarpetCompat.new("This is_just_a test", :no_intraemphasis).to_html
|
29
|
-
|
29
|
+
assert_equal "<p>This is_just_a test</p>\n", html
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_translate_outdated_extensions
|
33
33
|
# these extensions are no longer used
|
34
34
|
exts = [:gh_blockcode, :no_tables, :smart, :strict]
|
35
35
|
html = RedcarpetCompat.new('"TEST"', *exts).to_html
|
36
|
-
|
36
|
+
assert_equal "<p>"TEST"</p>\n", html
|
37
37
|
end
|
38
38
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,29 +1,33 @@
|
|
1
1
|
# coding: UTF-8
|
2
2
|
Encoding.default_internal = 'UTF-8' if defined? Encoding
|
3
3
|
|
4
|
-
gem 'test-unit', '>= 2' # necessary when not using bundle exec
|
5
|
-
|
6
4
|
require 'test/unit'
|
7
|
-
require 'nokogiri'
|
8
5
|
|
9
6
|
require 'redcarpet'
|
10
7
|
require 'redcarpet/render_strip'
|
11
8
|
require 'redcarpet/render_man'
|
12
|
-
require 'redcarpet/compat'
|
13
9
|
|
14
10
|
class Redcarpet::TestCase < Test::Unit::TestCase
|
15
|
-
def
|
16
|
-
assert_equal
|
17
|
-
Nokogiri::HTML::DocumentFragment.parse(html_b).to_html
|
11
|
+
def assert_renders(html, markdown)
|
12
|
+
assert_equal html, render(markdown)
|
18
13
|
end
|
19
14
|
|
20
|
-
def
|
21
|
-
|
15
|
+
def render(markdown, options = {})
|
16
|
+
options = options.fetch(:with, {})
|
17
|
+
|
18
|
+
if options.kind_of?(Array)
|
19
|
+
options = Hash[options.map {|o| [o, true]}]
|
20
|
+
end
|
21
|
+
|
22
|
+
render = renderer.new(options)
|
23
|
+
parser = Redcarpet::Markdown.new(render, options)
|
24
|
+
|
25
|
+
parser.render(markdown)
|
22
26
|
end
|
23
27
|
|
24
28
|
private
|
25
29
|
|
26
|
-
def
|
27
|
-
@
|
30
|
+
def renderer
|
31
|
+
@renderer ||= Redcarpet::Render::HTML
|
28
32
|
end
|
29
33
|
end
|