dtext_rb 1.0.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.
- checksums.yaml +7 -0
- data/.rbenv-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +61 -0
- data/Rakefile +67 -0
- data/VERSION +1 -0
- data/dtext_rb.gemspec +59 -0
- data/ext/dtext/dtext.c +5837 -0
- data/ext/dtext/dtext.h +4 -0
- data/ext/dtext/dtext.rl +1294 -0
- data/ext/dtext/extconf.rb +8 -0
- data/lib/dtext.rb +1 -0
- data/lib/dtext_ruby.rb +326 -0
- data/test/dtext_test.rb +211 -0
- data/test/ragel_output.txt +4082 -0
- data/test/wiki.txt +3403 -0
- metadata +101 -0
data/lib/dtext.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "dtext/dtext"
|
data/lib/dtext_ruby.rb
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
class DTextRuby
|
5
|
+
MENTION_REGEXP = /(?<=^| )@\S+/
|
6
|
+
|
7
|
+
def self.u(string)
|
8
|
+
CGI.escape(string)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.h(string)
|
12
|
+
CGI.escapeHTML(string)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.strip_blocks(string, tag)
|
16
|
+
blocks = string.scan(/\[\/?#{tag}\]|.+?(?=\[\/?#{tag}\]|$)/m)
|
17
|
+
n = 0
|
18
|
+
stripped = ""
|
19
|
+
blocks.each do |block|
|
20
|
+
case block
|
21
|
+
when "[#{tag}]"
|
22
|
+
n += 1
|
23
|
+
|
24
|
+
when "[/#{tag}]"
|
25
|
+
n -= 1
|
26
|
+
|
27
|
+
else
|
28
|
+
if n == 0
|
29
|
+
stripped += block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
stripped.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_inline(str, options = {})
|
38
|
+
str.gsub!(/&/, "&")
|
39
|
+
str.gsub!(/</, "<")
|
40
|
+
str.gsub!(/>/, ">")
|
41
|
+
str.gsub!(/\n/m, "<br>") unless options[:ignore_newlines]
|
42
|
+
str.gsub!(/\[b\](.+?)\[\/b\]/i, '<strong>\1</strong>')
|
43
|
+
str.gsub!(/\[i\](.+?)\[\/i\]/i, '<em>\1</em>')
|
44
|
+
str.gsub!(/\[s\](.+?)\[\/s\]/i, '<s>\1</s>')
|
45
|
+
str.gsub!(/\[u\](.+?)\[\/u\]/i, '<u>\1</u>')
|
46
|
+
str.gsub!(/\[tn\](.+?)\[\/tn\]/i, '<p class="tn">\1</p>')
|
47
|
+
|
48
|
+
str = parse_mentions(str)
|
49
|
+
str = parse_links(str)
|
50
|
+
str = parse_aliased_wiki_links(str)
|
51
|
+
str = parse_wiki_links(str)
|
52
|
+
str = parse_post_links(str)
|
53
|
+
str = parse_id_links(str)
|
54
|
+
str
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.parse_mentions(str)
|
58
|
+
str.gsub!(MENTION_REGEXP) do |name|
|
59
|
+
next name unless name =~ /[a-z0-9]/i
|
60
|
+
|
61
|
+
if name =~ /([:;,.!?\)\]<>])$/
|
62
|
+
name.chop!
|
63
|
+
ch = $1
|
64
|
+
else
|
65
|
+
ch = ""
|
66
|
+
end
|
67
|
+
|
68
|
+
'<a href="/users?name=' + u(CGI.unescapeHTML(name[1..-1])) + '">' + name + '</a>' + ch
|
69
|
+
end
|
70
|
+
str
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.parse_table_elements(str)
|
74
|
+
str = parse_inline(str, :ignore_newlines => true)
|
75
|
+
str.gsub!(/\[(\/?(?:tr|td|th|thead|tbody))\]/, '<\1>')
|
76
|
+
str
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.parse_links(str)
|
80
|
+
str.gsub(/("[^"]+":(https?:\/\/|\/)[^\s\r\n<>]+|https?:\/\/[^\s\r\n<>]+|"[^"]+":\[(https?:\/\/|\/)[^\s\r\n<>\]]+\])+/) do |url|
|
81
|
+
ch = ""
|
82
|
+
|
83
|
+
if url =~ /^"([^"]+)":\[(.+)\]$/
|
84
|
+
text = $1
|
85
|
+
url = $2
|
86
|
+
else
|
87
|
+
if url =~ /^"([^"]+)":(.+)$/
|
88
|
+
text = $1
|
89
|
+
url = $2
|
90
|
+
else
|
91
|
+
text = url
|
92
|
+
end
|
93
|
+
|
94
|
+
if url =~ /([;,.!?\)\]<>])$/
|
95
|
+
url.chop!
|
96
|
+
ch = $1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
'<a href="' + url + '">' + text + '</a>' + ch
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.parse_aliased_wiki_links(str)
|
105
|
+
str.gsub(/\[\[([^\|\]]+)\|([^\]]+)\]\]/m) do
|
106
|
+
text = CGI.unescapeHTML($2)
|
107
|
+
title = CGI.unescapeHTML($1).tr(" ", "_").downcase
|
108
|
+
%{<a href="/wiki_pages/show_or_new?title=#{u(title)}">#{h(text)}</a>}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.parse_wiki_links(str)
|
113
|
+
str.gsub(/\[\[([^\]]+)\]\]/) do
|
114
|
+
text = CGI.unescapeHTML($1)
|
115
|
+
title = text.tr(" ", "_").downcase
|
116
|
+
%{<a href="/wiki_pages/show_or_new?title=#{u(title)}">#{h(text)}</a>}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.parse_post_links(str)
|
121
|
+
str.gsub(/\{\{([^\}]+)\}\}/) do
|
122
|
+
tags = CGI.unescapeHTML($1)
|
123
|
+
%{<a rel="nofollow" href="/posts?tags=#{u(tags)}">#{h(tags)}</a>}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.parse_id_links(str)
|
128
|
+
str = str.gsub(/\bpost #(\d+)/i, %{<a href="/posts/\\1">post #\\1</a>})
|
129
|
+
str = str.gsub(/\bforum #(\d+)/i, %{<a href="/forum_posts/\\1">forum #\\1</a>})
|
130
|
+
str = str.gsub(/\btopic #(\d+)(?!\/p\d|\d)/i, %{<a href="/forum_topics/\\1">topic #\\1</a>})
|
131
|
+
str = str.gsub(/\btopic #(\d+)\/p(\d+)/i, %{<a href="/forum_topics/\\1?page=\\2">topic #\\1/p\\2</a>})
|
132
|
+
str = str.gsub(/\bcomment #(\d+)/i, %{<a href="/comments/\\1">comment #\\1</a>})
|
133
|
+
str = str.gsub(/\bpool #(\d+)/i, %{<a href="/pools/\\1">pool #\\1</a>})
|
134
|
+
str = str.gsub(/\buser #(\d+)/i, %{<a href="/users/\\1">user #\\1</a>})
|
135
|
+
str = str.gsub(/\bartist #(\d+)/i, %{<a href="/artists/\\1">artist #\\1</a>})
|
136
|
+
str = str.gsub(/\bissue #(\d+)/i, %{<a href="https://github.com/r888888888/danbooru/issues/\\1">issue #\\1</a>})
|
137
|
+
str = str.gsub(/\bpixiv #(\d+)(?!\/p\d|\d)/i, %{<a href="http://www.pixiv.net/member_illust.php?mode=medium&illust_id=\\1">pixiv #\\1</a>})
|
138
|
+
str = str.gsub(/\bpixiv #(\d+)\/p(\d+)/i, %{<a href="http://www.pixiv.net/member_illust.php?mode=manga_big&illust_id=\\1&page=\\2">pixiv #\\1/p\\2</a>})
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.parse_list(str, options = {})
|
142
|
+
html = ""
|
143
|
+
current_item = ""
|
144
|
+
layout = []
|
145
|
+
nest = 0
|
146
|
+
|
147
|
+
str.split(/\n/).each do |line|
|
148
|
+
if line =~ /^\s*(\*+) (.+)/
|
149
|
+
if nest > 0
|
150
|
+
html += "<li>#{current_item}</li>"
|
151
|
+
elsif not current_item.strip.empty?
|
152
|
+
html += "<p>#{current_item}</p>"
|
153
|
+
end
|
154
|
+
|
155
|
+
nest = $1.size
|
156
|
+
current_item = parse_inline($2)
|
157
|
+
else
|
158
|
+
current_item += parse_inline(line)
|
159
|
+
end
|
160
|
+
|
161
|
+
if nest > layout.size
|
162
|
+
html += "<ul>"
|
163
|
+
layout << "ul"
|
164
|
+
end
|
165
|
+
|
166
|
+
while nest < layout.size
|
167
|
+
elist = layout.pop
|
168
|
+
if elist
|
169
|
+
html += "</#{elist}>"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
html += "<li>#{current_item}</li>"
|
175
|
+
|
176
|
+
while layout.any?
|
177
|
+
elist = layout.pop
|
178
|
+
html += "</#{elist}>"
|
179
|
+
end
|
180
|
+
|
181
|
+
html
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.parse(str, options = {})
|
185
|
+
return "" if str.nil?
|
186
|
+
|
187
|
+
# Make sure quote tags are surrounded by newlines
|
188
|
+
|
189
|
+
unless options[:inline]
|
190
|
+
str.gsub!(/\s*\[quote\](?!\])\s*/m, "\n\n[quote]\n\n")
|
191
|
+
str.gsub!(/\s*\[\/quote\]\s*/m, "\n\n[/quote]\n\n")
|
192
|
+
str.gsub!(/\s*\[code\](?!\])/m, "\n\n[code]\n\n")
|
193
|
+
str.gsub!(/\[\/code\]\s*/m, "\n\n[/code]\n\n")
|
194
|
+
str.gsub!(/\s*\[spoilers?\](?!\])\s*/m, "\n\n[spoiler]\n\n")
|
195
|
+
str.gsub!(/\s*\[\/spoilers?\]\s*/m, "\n\n[/spoiler]\n\n")
|
196
|
+
str.gsub!(/^(h[1-6]\.\s*.+)$/, "\n\n\\1\n\n")
|
197
|
+
str.gsub!(/\s*\[expand(\=[^\]]*)?\](?!\])\s*/m, "\n\n[expand\\1]\n\n")
|
198
|
+
str.gsub!(/\s*\[\/expand\]\s*/m, "\n\n[/expand]\n\n")
|
199
|
+
str.gsub!(/\s*\[table\](?!\])\s*/m, "\n\n[table]\n\n")
|
200
|
+
str.gsub!(/\s*\[\/table\]\s*/m, "\n\n[/table]\n\n")
|
201
|
+
end
|
202
|
+
|
203
|
+
str.gsub!(/(?:\r?\n){3,}/, "\n\n")
|
204
|
+
str.strip!
|
205
|
+
blocks = str.split(/(?:\r?\n){2}/)
|
206
|
+
stack = []
|
207
|
+
flags = {}
|
208
|
+
|
209
|
+
html = blocks.map do |block|
|
210
|
+
case block
|
211
|
+
when /\A(h[1-6])\.\s*(.+)\Z/
|
212
|
+
tag = $1
|
213
|
+
content = $2
|
214
|
+
|
215
|
+
if options[:inline]
|
216
|
+
"<h6>" + parse_inline(content, options) + "</h6>"
|
217
|
+
else
|
218
|
+
"<#{tag}>" + parse_inline(content, options) + "</#{tag}>"
|
219
|
+
end
|
220
|
+
|
221
|
+
when /^\s*\*+ /
|
222
|
+
parse_list(block, options)
|
223
|
+
|
224
|
+
when "[quote]"
|
225
|
+
if options[:inline]
|
226
|
+
""
|
227
|
+
else
|
228
|
+
stack << "blockquote"
|
229
|
+
"<blockquote>"
|
230
|
+
end
|
231
|
+
|
232
|
+
when "[/quote]"
|
233
|
+
if options[:inline]
|
234
|
+
""
|
235
|
+
elsif stack.last == "blockquote"
|
236
|
+
stack.pop
|
237
|
+
'</blockquote>'
|
238
|
+
else
|
239
|
+
""
|
240
|
+
end
|
241
|
+
|
242
|
+
when "[spoiler]"
|
243
|
+
stack << "spoiler"
|
244
|
+
'<div class="spoiler">'
|
245
|
+
|
246
|
+
when "[/spoiler]"
|
247
|
+
if stack.last == "spoiler"
|
248
|
+
stack.pop
|
249
|
+
"</div>"
|
250
|
+
else
|
251
|
+
""
|
252
|
+
end
|
253
|
+
|
254
|
+
when "[table]"
|
255
|
+
stack << "table"
|
256
|
+
flags[:table] = true
|
257
|
+
'<table class="striped">'
|
258
|
+
|
259
|
+
when "[/table]"
|
260
|
+
if stack.last == "table"
|
261
|
+
stack.pop
|
262
|
+
flags[:table] = false
|
263
|
+
"</table>"
|
264
|
+
else
|
265
|
+
""
|
266
|
+
end
|
267
|
+
|
268
|
+
when /\[code\](?!\])/
|
269
|
+
flags[:code] = true
|
270
|
+
stack << "pre"
|
271
|
+
'<pre>'
|
272
|
+
|
273
|
+
when /\[\/code\](?!\])/
|
274
|
+
flags[:code] = false
|
275
|
+
if stack.last == "pre"
|
276
|
+
stack.pop
|
277
|
+
"</pre>"
|
278
|
+
else
|
279
|
+
""
|
280
|
+
end
|
281
|
+
|
282
|
+
when /\[expand(?:\=([^\]]*))?\](?!\])/
|
283
|
+
stack << "expandable"
|
284
|
+
expand_html = '<div class="expandable"><div class="expandable-header">'
|
285
|
+
expand_html << "<span>#{h($1)}</span>" if $1.present?
|
286
|
+
expand_html << '<input type="button" value="Show" class="expandable-button"/></div>'
|
287
|
+
expand_html << '<div class="expandable-content">'
|
288
|
+
expand_html
|
289
|
+
|
290
|
+
when /\[\/expand\](?!\])/
|
291
|
+
if stack.last == "expandable"
|
292
|
+
stack.pop
|
293
|
+
'</div></div>'
|
294
|
+
end
|
295
|
+
|
296
|
+
else
|
297
|
+
if flags[:code]
|
298
|
+
CGI.escape_html(block) + "\n\n"
|
299
|
+
elsif flags[:table]
|
300
|
+
parse_table_elements(block)
|
301
|
+
else
|
302
|
+
'<p>' + parse_inline(block) + '</p>'
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
stack.reverse.each do |tag|
|
308
|
+
if tag == "blockquote"
|
309
|
+
html << "</blockquote>"
|
310
|
+
elsif tag == "div"
|
311
|
+
html << "</div>"
|
312
|
+
elsif tag == "pre"
|
313
|
+
html << "</pre>"
|
314
|
+
elsif tag == "spoiler"
|
315
|
+
html << "</div>"
|
316
|
+
elsif tag == "expandable"
|
317
|
+
html << "</div></div>"
|
318
|
+
elsif tag == "table"
|
319
|
+
html << "</table>"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
html.join("")
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
data/test/dtext_test.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'dtext/dtext'
|
3
|
+
|
4
|
+
class DTextTest < Minitest::Test
|
5
|
+
def assert_parse(expected, input)
|
6
|
+
assert_equal(expected, DTextRagel.parse(input))
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_mentions
|
10
|
+
assert_parse('<p><a rel="nofollow" href="/users?name=bob">@bob</a></p>', "@bob")
|
11
|
+
assert_parse('<p>hi <a rel="nofollow" href="/users?name=bob">@bob</a></p>', "hi @bob")
|
12
|
+
assert_parse('<p>this is not @.@ @_@ <a rel="nofollow" href="/users?name=bob">@bob</a></p>', "this is not @.@ @_@ @bob")
|
13
|
+
assert_parse('<p>multiple <a rel="nofollow" href="/users?name=bob">@bob</a> <a rel="nofollow" href="/users?name=anna">@anna</a></p>', "multiple @bob @anna")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_sanitize_heart
|
17
|
+
assert_parse('<p><3</p>', "<3")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_sanitize_less_than
|
21
|
+
assert_parse('<p><</p>', "<")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_sanitize_greater_than
|
25
|
+
assert_parse('<p>></p>', ">")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_sanitize_ampersand
|
29
|
+
assert_parse('<p>&</p>', "&")
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_wiki_links
|
33
|
+
assert_parse("<p>a <a href=\"/wiki_pages/show_or_new?title=b\">b</a> c</p>", "a [[b]] c")
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_wiki_links_spoiler
|
37
|
+
assert_parse("<p>a <a href=\"/wiki_pages/show_or_new?title=spoiler\">spoiler</a> c</p>", "a [[spoiler]] c")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_spoilers_inline
|
41
|
+
assert_parse("<p>this is <span class=\"spoiler\">an inline spoiler</span>.</p>", "this is [spoiler]an inline spoiler[/spoiler].")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_spoilers_block
|
45
|
+
assert_parse("<p>this is</p><div class=\"spoiler\"><p>a block spoiler</p></div><p>.</p>", "this is\n\n[spoiler]\na block spoiler\n[/spoiler].")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_spoilers_with_no_closing_tag_1
|
49
|
+
assert_parse("<div class=\"spoiler\"><p>this is a spoiler with no closing tag</p><p>new text</p></div>", "[spoiler]this is a spoiler with no closing tag\n\nnew text")
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_spoilers_with_no_closing_tag_2
|
53
|
+
assert_parse("<div class=\"spoiler\"><p>this is a spoiler with no closing tag<br>new text</p></div>", "[spoiler]this is a spoiler with no closing tag\nnew text")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_spoilers_with_no_closing_tag_block
|
57
|
+
assert_parse("<div class=\"spoiler\"><p>this is a block spoiler with no closing tag</p></div>", "[spoiler]\nthis is a block spoiler with no closing tag")
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_spoilers_nested
|
61
|
+
assert_parse("<div class=\"spoiler\"><p>this is <span class=\"spoiler\">a nested</span> spoiler</p></div>", "[spoiler]this is [spoiler]a nested[/spoiler] spoiler[/spoiler]")
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_paragraphs
|
65
|
+
assert_parse("<p>abc</p>", "abc")
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_paragraphs_with_newlines_1
|
69
|
+
assert_parse("<p>a<br>b<br>c</p>", "a\nb\nc")
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_paragraphs_with_newlines_2
|
73
|
+
assert_parse("<p>a</p><p>b</p>", "a\n\nb")
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_headers
|
77
|
+
assert_parse("<h1>header</h1>", "h1. header")
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_quote_blocks
|
81
|
+
assert_parse('<blockquote><p>test</p></blockquote>', "[quote]\ntest\n[/quote]")
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_quote_blocks_nested
|
85
|
+
assert_parse("<blockquote><p>a</p><blockquote><p>b</p></blockquote><p>c</p></blockquote>", "[quote]\na\n[quote]\nb\n[/quote]\nc\n[/quote]")
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_quote_blocks_nested_spoiler
|
89
|
+
assert_parse("<blockquote><p>a<br><span class=\"spoiler\">blah</span><br>c</p></blockquote>", "[quote]\na\n[spoiler]blah[/spoiler]\nc[/quote]")
|
90
|
+
assert_parse("<blockquote><p>a</p><div class=\"spoiler\"><p>blah</p></div><p>c</p></blockquote>", "[quote]\na\n\n[spoiler]blah[/spoiler]\n\nc[/quote]")
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_quote_blocks_nested_expand
|
94
|
+
assert_parse("<blockquote><p>a</p><div class=\"expandable\"><div class=\"expandable-header\"><input type=\"button\" value=\"Show\" class=\"expandable-button\"/></div><div class=\"expandable-content\"><p>b</p></div></div><p>c</p></blockquote>", "[quote]\na\n[expand]\nb\n[/expand]\nc\n[/quote]")
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_code
|
98
|
+
assert_parse("<pre>for (i=0; i<5; ++i) {\n printf(1);\n}\n\nexit(1);</pre>", "[code]for (i=0; i<5; ++i) {\n printf(1);\n}\n\nexit(1);")
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_urls
|
102
|
+
assert_parse('<p>a <a href="http://test.com">http://test.com</a> b</p>', p('a http://test.com b'))
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_urls_with_newline
|
106
|
+
assert_parse('<p><a href="http://test.com">http://test.com</a><br>b</p>', "http://test.com\nb")
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_urls_with_paths
|
110
|
+
assert_parse('<p>a <a href="http://test.com/~bob/image.jpg">http://test.com/~bob/image.jpg</a> b</p>', p('a http://test.com/~bob/image.jpg b'))
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_urls_with_fragment
|
114
|
+
assert_parse('<p>a <a href="http://test.com/home.html#toc">http://test.com/home.html#toc</a> b</p>', p('a http://test.com/home.html#toc b'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_auto_urls
|
118
|
+
assert_parse('<p>a <a href="http://test.com">http://test.com</a>. b</p>', p('a http://test.com. b'))
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_auto_urls_in_parentheses
|
122
|
+
assert_parse('<p>a (<a href="http://test.com">http://test.com</a>) b</p>', p('a (http://test.com) b'))
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_old_style_links
|
126
|
+
assert_parse('<p><a href="http://test.com">test</a></p>', p('"test":http://test.com'))
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_old_style_links_with_special_entities
|
130
|
+
assert_parse('<p>"1" <a href="http://three.com">2 & 3</a></p>', p('"1" "2 & 3":http://three.com'))
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_new_style_links
|
134
|
+
assert_parse('<p><a href="http://test.com">test</a></p>', p('"test":[http://test.com]'))
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_new_style_links_with_parentheses
|
138
|
+
assert_parse('<p><a href="http://test.com/(parentheses)">test</a></p>', p('"test":[http://test.com/(parentheses)]'))
|
139
|
+
assert_parse('<p>(<a href="http://test.com/(parentheses)">test</a>)</p>', p('("test":[http://test.com/(parentheses)])'))
|
140
|
+
assert_parse('<p>[<a href="http://test.com/(parentheses)">test</a>]</p>', p('["test":[http://test.com/(parentheses)]]'))
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_lists_1
|
144
|
+
assert_parse('<ul><li>a</li></ul>', p('* a'))
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_lists_2
|
148
|
+
assert_parse('<ul><li>a</li><li>b</li></ul>', "* a\n* b")
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_lists_nested
|
152
|
+
assert_parse('<ul><li>a</li><ul><li>b</li></ul></ul>', "* a\n** b")
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_lists_inline
|
156
|
+
assert_parse('<ul><li><a href="/posts/1">post #1</a></li></ul>', "* post #1")
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_lists_not_preceded_by_newline
|
160
|
+
assert_parse('<p>a<br>b</p><ul><li>c</li><li>d</li></ul>', "a\nb\n* c\n* d")
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_lists_with_multiline_items
|
164
|
+
assert_parse('<p>a</p><ul><li>b<br>c</li><li>d<br>e</li></ul><p>another one</p>', "a\n* b\nc\n* d\ne\n\nanother one")
|
165
|
+
assert_parse('<p>a</p><ul><li>b<br>c</li><ul><li>d<br>e</li></ul></ul><p>another one</p>', "a\n* b\nc\n** d\ne\n\nanother one")
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_inline_tags
|
169
|
+
assert_parse('<p><a rel="nofollow" href="/posts?tags=tag">tag</a></p>', "{{tag}}")
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_inline_tags_conjunction
|
173
|
+
assert_parse('<p><a rel="nofollow" href="/posts?tags=tag1%20tag2">tag1 tag2</a></p>', "{{tag1 tag2}}")
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_inline_tags_special_entities
|
177
|
+
assert_parse('<p><a rel="nofollow" href="/posts?tags=%3C3"><3</a></p>', "{{<3}}")
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_extra_newlines
|
181
|
+
assert_parse('<p>a</p><p>b</p>', "a\n\n\n\n\n\n\nb\n\n\n\n")
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_complex_links_1
|
185
|
+
assert_parse("<p><a href=\"/wiki_pages/show_or_new?title=1\">2 3</a> | <a href=\"/wiki_pages/show_or_new?title=4\">5 6</a></p>", "[[1|2 3]] | [[4|5 6]]")
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_complex_links_2
|
189
|
+
assert_parse("<p>Tags <strong>(<a href=\"/wiki_pages/show_or_new?title=howto:tag\">Tagging Guidelines</a> | <a href=\"/wiki_pages/show_or_new?title=howto:tag_checklist\">Tag Checklist</a> | <a href=\"/wiki_pages/show_or_new?title=tag_groups\">Tag Groups</a>)</strong></p>", "Tags [b]([[howto:tag|Tagging Guidelines]] | [[howto:tag_checklist|Tag Checklist]] | [[Tag Groups]])[/b]")
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_table
|
193
|
+
assert_parse("<table class=\"striped\"><thead><tr><th>header</th></tr></thead><tbody><tr><td><a href=\"/posts/100\">post #100</a></td></tr></tbody></table>", "[table][thead][tr][th]header[/th][/tr][/thead][tbody][tr][td]post #100[/td][/tr][/tbody][/table]")
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_table_with_newlines
|
197
|
+
assert_parse("<table class=\"striped\"><thead><tr><th>header</th></tr></thead><tbody><tr><td><a href=\"/posts/100\">post #100</a></td></tr></tbody></table>", "[table]\n[thead]\n[tr]\n[th]header[/th][/tr][/thead][tbody][tr][td]post #100[/td][/tr][/tbody][/table]")
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_forum_links
|
201
|
+
assert_parse('<p><a href="/forum_topics/1234?page=4">topic #1234/p4</a></p>', "topic #1234/p4")
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_boundary_exploit
|
205
|
+
assert_parse('<p><a rel="nofollow" href="/users?name=mack">@mack</a><</p>', "@mack<")
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_inline_mode
|
209
|
+
assert_equal("hello", DTextRagel.parse("hello", :inline => true).strip)
|
210
|
+
end
|
211
|
+
end
|