dtext_rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|