wikitext 0.1
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.
- data/ext/ary.h +99 -0
- data/ext/depend +22 -0
- data/ext/extconf.rb +23 -0
- data/ext/parser.c +2174 -0
- data/ext/parser.h +31 -0
- data/ext/str.h +135 -0
- data/ext/token.c +109 -0
- data/ext/token.h +95 -0
- data/ext/wikitext.c +60 -0
- data/ext/wikitext.h +30 -0
- data/ext/wikitext_ragel.c +3354 -0
- data/ext/wikitext_ragel.h +17 -0
- data/spec/autolinking_spec.rb +122 -0
- data/spec/blockquote_spec.rb +570 -0
- data/spec/em_spec.rb +97 -0
- data/spec/encoding_spec.rb +124 -0
- data/spec/entity_spec.rb +40 -0
- data/spec/external_link_spec.rb +289 -0
- data/spec/h1_spec.rb +59 -0
- data/spec/h2_spec.rb +59 -0
- data/spec/h3_spec.rb +59 -0
- data/spec/h4_spec.rb +59 -0
- data/spec/h5_spec.rb +59 -0
- data/spec/h6_spec.rb +59 -0
- data/spec/indentation_spec.rb +70 -0
- data/spec/integration_spec.rb +265 -0
- data/spec/internal_link_spec.rb +445 -0
- data/spec/line_endings_spec.rb +81 -0
- data/spec/link_encoding_spec.rb +132 -0
- data/spec/link_sanitizing_spec.rb +228 -0
- data/spec/nowiki_spec.rb +155 -0
- data/spec/p_spec.rb +44 -0
- data/spec/pre_spec.rb +411 -0
- data/spec/regressions_spec.rb +45 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/strong_em_spec.rb +89 -0
- data/spec/strong_spec.rb +99 -0
- data/spec/tokenizing_spec.rb +190 -0
- data/spec/tt_spec.rb +100 -0
- data/spec/ul_spec.rb +307 -0
- data/spec/wikitext_spec.rb +50 -0
- metadata +93 -0
data/spec/tt_spec.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2007-2008 Wincent Colaiuta
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
17
|
+
require 'wikitext'
|
18
|
+
|
19
|
+
describe Wikitext::Parser, 'parsing <tt> spans' do
|
20
|
+
before do
|
21
|
+
@parser = Wikitext::Parser.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should recognize paired <tt> and </tt> tags' do
|
25
|
+
@parser.parse('foo <tt>bar</tt> baz').should == "<p>foo <tt>bar</tt> baz</p>\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should recognize <tt> tags case-insensitively' do
|
29
|
+
@parser.parse('foo <TT>bar</tT> baz').should == "<p>foo <tt>bar</tt> baz</p>\n"
|
30
|
+
@parser.parse('foo <tT>bar</Tt> baz').should == "<p>foo <tt>bar</tt> baz</p>\n"
|
31
|
+
@parser.parse('foo <Tt>bar</TT> baz').should == "<p>foo <tt>bar</tt> baz</p>\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should automatically insert missing closing tags' do
|
35
|
+
@parser.parse('foo <tt>bar').should == "<p>foo <tt>bar</tt></p>\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should automatically close unclosed spans upon hitting newline' do
|
39
|
+
@parser.parse("foo <tt>bar\nbaz").should == "<p>foo <tt>bar</tt> baz</p>\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should convert unexpected closing tags into entities' do
|
43
|
+
@parser.parse('foo </tt>bar').should == "<p>foo </tt>bar</p>\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should handle (illegal) nested <tt> spans' do
|
47
|
+
@parser.parse('foo <tt>bar <tt>inner</tt></tt> baz').should == "<p>foo <tt>bar <tt>inner</tt></tt> baz</p>\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should handle (illegal) interleaved spans' do
|
51
|
+
@parser.parse("foo <tt>bar '''inner</tt> baz'''").should == "<p>foo <tt>bar <strong>inner</strong></tt> baz<strong></strong></p>\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should have no effect inside <pre> blocks' do
|
55
|
+
@parser.parse(' <tt>foo</tt>').should == "<pre><tt>foo</tt></pre>\n"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have no effect inside <nowiki> spans' do
|
59
|
+
@parser.parse('<nowiki><tt>foo</tt></nowiki>').should == "<p><tt>foo</tt></p>\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should have no effect if a backtick span is already open' do
|
63
|
+
@parser.parse('foo `<tt>bar</tt>` baz').should == "<p>foo <tt><tt>bar</tt></tt> baz</p>\n"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe Wikitext::Parser, 'parsing backtick spans' do
|
68
|
+
before do
|
69
|
+
@parser = Wikitext::Parser.new
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should recognize paired backticks' do
|
73
|
+
@parser.parse('foo `bar` baz').should == "<p>foo <tt>bar</tt> baz</p>\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should automatically insert missing closing backtick' do
|
77
|
+
@parser.parse('foo `bar').should == "<p>foo <tt>bar</tt></p>\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should automatically close unclosed spans upon hitting newline' do
|
81
|
+
@parser.parse("foo `bar\nbaz").should == "<p>foo <tt>bar</tt> baz</p>\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should handle (illegal) interleaved spans' do
|
85
|
+
@parser.parse("foo `bar '''inner` baz'''").should == "<p>foo <tt>bar <strong>inner</strong></tt> baz<strong></strong></p>\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should have no effect inside <pre> blocks' do
|
89
|
+
@parser.parse(' `foo`').should == "<pre>`foo`</pre>\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should have no effect inside <nowiki> spans' do
|
93
|
+
@parser.parse('<nowiki>`foo`</nowiki>').should == "<p>`foo`</p>\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should have no effect if a <tt> span is already open' do
|
97
|
+
@parser.parse('foo <tt>`bar`</tt> baz').should == "<p>foo <tt>`bar`</tt> baz</p>\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
data/spec/ul_spec.rb
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2007-2008 Wincent Colaiuta
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
17
|
+
require 'wikitext'
|
18
|
+
|
19
|
+
describe Wikitext::Parser, 'parsing unordered lists' do
|
20
|
+
before do
|
21
|
+
@parser = Wikitext::Parser.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should recognize a single item list' do
|
25
|
+
@parser.parse('*foo').should == "<ul>\n <li>foo</li>\n</ul>\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should allow and consume optional space after the last <ul> marker' do
|
29
|
+
@parser.parse('* foo').should == "<ul>\n <li>foo</li>\n</ul>\n" # exactly one space consumed
|
30
|
+
@parser.parse('* foo').should == "<ul>\n <li>foo</li>\n</ul>\n" # multiple spaces consumed
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should consider a space after an <ul> marker to indicate that it will be the last marker' do
|
34
|
+
@parser.parse('* * foo').should == "<ul>\n <li>* foo</li>\n</ul>\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should only recognize <ul> markers if they or a direct ancestor start in the left column' do
|
38
|
+
@parser.parse(' * foo').should == "<pre>* foo</pre>\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should recognize <ul> markers nested inside blockquote blocks' do
|
42
|
+
expected = dedent <<-END
|
43
|
+
<blockquote>
|
44
|
+
<ul>
|
45
|
+
<li>foo</li>
|
46
|
+
</ul>
|
47
|
+
</blockquote>
|
48
|
+
END
|
49
|
+
@parser.parse('> * foo').should == expected
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should display excess <ul> markers as literals' do
|
53
|
+
# this provides feedback to the user
|
54
|
+
@parser.parse('** foo').should == "<ul>\n <li>* foo</li>\n</ul>\n"
|
55
|
+
@parser.parse('*** foo').should == "<ul>\n <li>** foo</li>\n</ul>\n"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should recognize a multi-item, single-level list' do
|
59
|
+
expected = dedent <<-END
|
60
|
+
<ul>
|
61
|
+
<li>foo</li>
|
62
|
+
<li>bar</li>
|
63
|
+
</ul>
|
64
|
+
END
|
65
|
+
@parser.parse("* foo\n* bar").should == expected
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should recognize a multi-item, nested list (two levels)' do
|
69
|
+
# indentation of nested lists is tricky
|
70
|
+
# the last </li> appears too far to the left
|
71
|
+
# the difficult is that sometimes li has to act like a block level element (like blockquote, does emit before dedent)
|
72
|
+
# and at other times it has to act like p (doesn't emit before dedent)
|
73
|
+
# so basically when nested we need to do an emitting dedent
|
74
|
+
# and when not we need to do a non-emitting one
|
75
|
+
expected = dedent <<-END
|
76
|
+
<ul>
|
77
|
+
<li>foo
|
78
|
+
<ul>
|
79
|
+
<li>bar</li>
|
80
|
+
</ul>
|
81
|
+
</li>
|
82
|
+
</ul>
|
83
|
+
END
|
84
|
+
@parser.parse("* foo\n** bar").should == expected
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should recognize a multi-item, nested list (three levels)' do
|
88
|
+
expected = dedent <<-END
|
89
|
+
<ul>
|
90
|
+
<li>foo
|
91
|
+
<ul>
|
92
|
+
<li>bar
|
93
|
+
<ul>
|
94
|
+
<li>baz</li>
|
95
|
+
</ul>
|
96
|
+
</li>
|
97
|
+
</ul>
|
98
|
+
</li>
|
99
|
+
</ul>
|
100
|
+
END
|
101
|
+
@parser.parse("* foo\n** bar\n*** baz").should == expected
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should recognize lists in which nesting level increases and then is maintained' do
|
105
|
+
expected = dedent <<-END
|
106
|
+
<ul>
|
107
|
+
<li>foo
|
108
|
+
<ul>
|
109
|
+
<li>bar</li>
|
110
|
+
<li>baz</li>
|
111
|
+
</ul>
|
112
|
+
</li>
|
113
|
+
</ul>
|
114
|
+
END
|
115
|
+
@parser.parse("* foo\n** bar\n** baz").should == expected
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should recognize lists in which nesting level increases and then decreases' do
|
119
|
+
expected = dedent <<-END
|
120
|
+
<ul>
|
121
|
+
<li>foo
|
122
|
+
<ul>
|
123
|
+
<li>bar</li>
|
124
|
+
</ul>
|
125
|
+
</li>
|
126
|
+
<li>baz</li>
|
127
|
+
</ul>
|
128
|
+
END
|
129
|
+
@parser.parse("* foo\n** bar\n* baz").should == expected
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should be terminated by subsequent paragraph at the same level' do
|
133
|
+
expected = dedent <<-END
|
134
|
+
<ul>
|
135
|
+
<li>foo</li>
|
136
|
+
</ul>
|
137
|
+
<p>bar</p>
|
138
|
+
END
|
139
|
+
@parser.parse("* foo\nbar").should == expected
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should be terminated by subsequent blockquote at the same level' do
|
143
|
+
expected = dedent <<-END
|
144
|
+
<ul>
|
145
|
+
<li>foo</li>
|
146
|
+
</ul>
|
147
|
+
<blockquote>
|
148
|
+
<p>bar</p>
|
149
|
+
</blockquote>
|
150
|
+
END
|
151
|
+
@parser.parse("* foo\n> bar").should == expected
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should be terminated by subsequent heading at the same level' do
|
155
|
+
@parser.parse("* foo\n====== bar ======").should == "<ul>\n <li>foo</li>\n</ul>\n<h6>bar</h6>\n"
|
156
|
+
@parser.parse("* foo\n===== bar =====").should == "<ul>\n <li>foo</li>\n</ul>\n<h5>bar</h5>\n"
|
157
|
+
@parser.parse("* foo\n==== bar ====").should == "<ul>\n <li>foo</li>\n</ul>\n<h4>bar</h4>\n"
|
158
|
+
@parser.parse("* foo\n=== bar ===").should == "<ul>\n <li>foo</li>\n</ul>\n<h3>bar</h3>\n"
|
159
|
+
@parser.parse("* foo\n== bar ==").should == "<ul>\n <li>foo</li>\n</ul>\n<h2>bar</h2>\n"
|
160
|
+
@parser.parse("* foo\n= bar =").should == "<ul>\n <li>foo</li>\n</ul>\n<h1>bar</h1>\n"
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should be terminated by subsequent <pre> block at the same level' do
|
164
|
+
@parser.parse("* foo\n bar").should == "<ul>\n <li>foo</li>\n</ul>\n<pre>bar</pre>\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should be terminated by subsequent ordered list at the same level' do
|
168
|
+
expected = dedent 6,<<-END
|
169
|
+
<ul>
|
170
|
+
<li>foo</li>
|
171
|
+
</ul>
|
172
|
+
<ol>
|
173
|
+
<li>bar</li>
|
174
|
+
</ol>
|
175
|
+
END
|
176
|
+
@parser.parse("* foo\n# bar").should == expected
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should recognize lists which contain nested ordered lists' do
|
180
|
+
expected = dedent <<-END
|
181
|
+
<ul>
|
182
|
+
<li>foo
|
183
|
+
<ol>
|
184
|
+
<li>bar</li>
|
185
|
+
</ol>
|
186
|
+
</li>
|
187
|
+
</ul>
|
188
|
+
END
|
189
|
+
@parser.parse("* foo\n*# bar").should == expected
|
190
|
+
|
191
|
+
input = dedent <<-END
|
192
|
+
* foo
|
193
|
+
*# bar
|
194
|
+
*# baz
|
195
|
+
END
|
196
|
+
expected = dedent <<-END
|
197
|
+
<ul>
|
198
|
+
<li>foo
|
199
|
+
<ol>
|
200
|
+
<li>bar</li>
|
201
|
+
<li>baz</li>
|
202
|
+
</ol>
|
203
|
+
</li>
|
204
|
+
</ul>
|
205
|
+
END
|
206
|
+
@parser.parse(input).should == expected
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should automatically close open TT_START elements on reaching the end of the line' do
|
210
|
+
# this (and the same for all other span-level elements) was a bug
|
211
|
+
input = dedent <<-END
|
212
|
+
* <tt>hello
|
213
|
+
* world
|
214
|
+
END
|
215
|
+
expected = dedent <<-END
|
216
|
+
<ul>
|
217
|
+
<li><tt>hello</tt></li>
|
218
|
+
<li>world</li>
|
219
|
+
</ul>
|
220
|
+
END
|
221
|
+
@parser.parse(input).should == expected
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should automatically close open TT elements on reaching the end of the line' do
|
225
|
+
input = dedent <<-END
|
226
|
+
* `hello
|
227
|
+
* world
|
228
|
+
END
|
229
|
+
expected = dedent <<-END
|
230
|
+
<ul>
|
231
|
+
<li><tt>hello</tt></li>
|
232
|
+
<li>world</li>
|
233
|
+
</ul>
|
234
|
+
END
|
235
|
+
@parser.parse(input).should == expected
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should automatically close open EM_START elements on reaching the end of the line' do
|
239
|
+
input = dedent <<-END
|
240
|
+
* <em>hello
|
241
|
+
* world
|
242
|
+
END
|
243
|
+
expected = dedent <<-END
|
244
|
+
<ul>
|
245
|
+
<li><em>hello</em></li>
|
246
|
+
<li>world</li>
|
247
|
+
</ul>
|
248
|
+
END
|
249
|
+
@parser.parse(input).should == expected
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should automatically close open EM elements on reaching the end of the line' do
|
253
|
+
input = dedent <<-END
|
254
|
+
* ''hello
|
255
|
+
* world
|
256
|
+
END
|
257
|
+
expected = dedent <<-END
|
258
|
+
<ul>
|
259
|
+
<li><em>hello</em></li>
|
260
|
+
<li>world</li>
|
261
|
+
</ul>
|
262
|
+
END
|
263
|
+
@parser.parse(input).should == expected
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should automatically close open STRONG_START elements on reaching the end of the line' do
|
267
|
+
input = dedent <<-END
|
268
|
+
* <strong>hello
|
269
|
+
* world
|
270
|
+
END
|
271
|
+
expected = dedent <<-END
|
272
|
+
<ul>
|
273
|
+
<li><strong>hello</strong></li>
|
274
|
+
<li>world</li>
|
275
|
+
</ul>
|
276
|
+
END
|
277
|
+
@parser.parse(input).should == expected
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should automatically close open STRONG elements on reaching the end of the line' do
|
281
|
+
input = dedent <<-END
|
282
|
+
* '''hello
|
283
|
+
* world
|
284
|
+
END
|
285
|
+
expected = dedent <<-END
|
286
|
+
<ul>
|
287
|
+
<li><strong>hello</strong></li>
|
288
|
+
<li>world</li>
|
289
|
+
</ul>
|
290
|
+
END
|
291
|
+
@parser.parse(input).should == expected
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'should automatically close open STRONG_EM elements on reaching the end of the line' do
|
295
|
+
input = dedent <<-END
|
296
|
+
* '''''hello
|
297
|
+
* world
|
298
|
+
END
|
299
|
+
expected = dedent <<-END
|
300
|
+
<ul>
|
301
|
+
<li><strong><em>hello</em></strong></li>
|
302
|
+
<li>world</li>
|
303
|
+
</ul>
|
304
|
+
END
|
305
|
+
@parser.parse(input).should == expected
|
306
|
+
end
|
307
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2007-2008 Wincent Colaiuta
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
17
|
+
require 'wikitext'
|
18
|
+
|
19
|
+
describe Wikitext::Parser, 'parsing non-ASCII input' do
|
20
|
+
before do
|
21
|
+
@parser = Wikitext::Parser.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should convert non-ASCII characters to numeric entities' do
|
25
|
+
@parser.parse('€').should == "<p>€</p>\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Wikitext::Parser, 'parsing characters which have special meaning in HTML' do
|
30
|
+
before do
|
31
|
+
@parser = Wikitext::Parser.new
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should convert "<" into the corresponding named entity' do
|
35
|
+
@parser.parse('<').should == "<p><</p>\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should convert ">" into the corresponding named entity' do
|
39
|
+
# can't put ">" in the first column as that would indicate a blockquote
|
40
|
+
@parser.parse("foo >").should == "<p>foo ></p>\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should convert "&" into the corresponding named entity' do
|
44
|
+
@parser.parse('&').should == "<p>&</p>\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should convert \'"\' into the corresponding named entity' do
|
48
|
+
@parser.parse('"').should == "<p>"</p>\n"
|
49
|
+
end
|
50
|
+
end
|