rbbcode 1.0.0 → 1.1.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/lib/rbbcode.rb +63 -49
- data/lib/rbbcode/node_extensions.rb +285 -125
- data/lib/rbbcode/rbbcode_grammar.treetop +109 -112
- data/lib/rbbcode/sanitize.rb +12 -12
- metadata +100 -56
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 05a9ae1e74abb5b265316ce91cefd13b76e6f484040e37b10c7f39a86e44695f
|
4
|
+
data.tar.gz: 40a4650b699a0bd33dd35d285eb5f9ef2f3962b0628601be3702362495217bb7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: defb74848c8d0632853ea9e3437188a1411978e523079778f29cd73826b3544937e14a36dde7144329d96d2886ca7275afd214af3bf5c8ba9c34da246a628743
|
7
|
+
data.tar.gz: a67405feba360b5c719bb1b0f958d2187efe8b217eb52e2635b8b41f3fbc66cd47b3c83c48ea53cae18d63298260bb2e4cd1691e0026b9c0a70e1d631d433ced
|
data/lib/rbbcode.rb
CHANGED
@@ -1,50 +1,64 @@
|
|
1
|
-
# Uncomment this when developing:
|
2
|
-
#$:.unshift './lib'
|
3
|
-
|
4
|
-
require 'erb'
|
5
|
-
require 'rubygems'
|
6
|
-
require 'treetop'
|
7
|
-
require 'sanitize'
|
8
|
-
require 'rbbcode/node_extensions'
|
9
|
-
require 'rbbcode/sanitize'
|
10
|
-
|
11
|
-
class RbbCode
|
12
|
-
def self.parser_class
|
13
|
-
if !@grammar_loaded
|
14
|
-
Treetop.load_from_string(
|
15
|
-
ERB.new(
|
16
|
-
File.read(
|
17
|
-
File.join(
|
18
|
-
File.dirname(__FILE__),
|
19
|
-
'rbbcode/rbbcode_grammar.treetop'
|
20
|
-
)
|
21
|
-
)
|
22
|
-
).result
|
23
|
-
)
|
24
|
-
@grammar_loaded = true
|
25
|
-
end
|
26
|
-
RbbCodeGrammarParser
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize(options = {})
|
30
|
-
@options = {
|
31
|
-
:
|
32
|
-
:
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
1
|
+
# Uncomment this when developing:
|
2
|
+
#$:.unshift './lib'
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'treetop'
|
7
|
+
require 'sanitize'
|
8
|
+
require 'rbbcode/node_extensions'
|
9
|
+
require 'rbbcode/sanitize'
|
10
|
+
|
11
|
+
class RbbCode
|
12
|
+
def self.parser_class
|
13
|
+
if !instance_variable_defined?(:@grammar_loaded) or !@grammar_loaded
|
14
|
+
Treetop.load_from_string(
|
15
|
+
ERB.new(
|
16
|
+
File.read(
|
17
|
+
File.join(
|
18
|
+
File.dirname(__FILE__),
|
19
|
+
'rbbcode/rbbcode_grammar.treetop'
|
20
|
+
)
|
21
|
+
)
|
22
|
+
).result
|
23
|
+
)
|
24
|
+
@grammar_loaded = true
|
25
|
+
end
|
26
|
+
RbbCodeGrammarParser
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(options = {})
|
30
|
+
@options = {
|
31
|
+
:output_format => :html,
|
32
|
+
:sanitize => true,
|
33
|
+
:sanitize_config => RbbCode::DEFAULT_SANITIZE_CONFIG
|
34
|
+
}.merge(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def convert(bb_code)
|
38
|
+
# Collapse CRLFs to LFs. Then replace any solitary CRs with LFs.
|
39
|
+
bb_code = bb_code.gsub("\r\n", "\n").gsub("\r", "\n")
|
40
|
+
# Add linebreaks before and after so that paragraphs etc. can be recognized.
|
41
|
+
bb_code = "\n\n" + bb_code + "\n\n"
|
42
|
+
output = self.class.parser_class.new.parse(bb_code).send("to_#{@options[:output_format]}")
|
43
|
+
if @options[:emoticons]
|
44
|
+
output = convert_emoticons(output)
|
45
|
+
end
|
46
|
+
# Sanitization works for HTML only.
|
47
|
+
if @options[:output_format] == :html and @options[:sanitize]
|
48
|
+
Sanitize.clean(output, @options[:sanitize_config])
|
49
|
+
else
|
50
|
+
output
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_emoticons(output)
|
55
|
+
@options[:emoticons].each do |emoticon, url|
|
56
|
+
output.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>')
|
57
|
+
end
|
58
|
+
output
|
59
|
+
end
|
60
|
+
|
61
|
+
def output_format
|
62
|
+
@options[:output_format]
|
63
|
+
end
|
50
64
|
end
|
@@ -1,126 +1,286 @@
|
|
1
|
-
class RbbCode
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
1
|
+
class RbbCode
|
2
|
+
module Attributes
|
3
|
+
# Strips any number of double quotes from the beginning and end of the string.
|
4
|
+
def strip_quotes(str)
|
5
|
+
str.sub(/^"+/, '').sub(/"+$/, '')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module RecursiveConversion
|
10
|
+
def recursively_convert(node, output_method, depth = 0)
|
11
|
+
if node.terminal?
|
12
|
+
if node.respond_to?(output_method)
|
13
|
+
# This is a terminal node with a custom implementation of the output
|
14
|
+
# method (e.g. #to_html).
|
15
|
+
node.send(output_method)
|
16
|
+
else
|
17
|
+
# This is a terminal node without a custom implementation of the
|
18
|
+
# output method. If the node consists solely of whitespace, emit the
|
19
|
+
# empty string. Otherwise, emit the node's text value.
|
20
|
+
node.text_value.match(/\A[\n\t]+\Z/) ? '' : node.text_value
|
21
|
+
end
|
22
|
+
else
|
23
|
+
if node.respond_to?(output_method)
|
24
|
+
# This is a non-terminal node with a custom implementation of the
|
25
|
+
# output method.
|
26
|
+
node.send(output_method)
|
27
|
+
else
|
28
|
+
# This is a non-terminal node without a custom implementation of the
|
29
|
+
# output method. Convert all its child nodes and concatenate the results.
|
30
|
+
node.elements.collect do |sub_node|
|
31
|
+
recursively_convert(sub_node, output_method, depth + 1)
|
32
|
+
end.join
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module DocumentNode
|
39
|
+
def to_html
|
40
|
+
contents.elements.collect { |p| p.to_html }.join
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_markdown
|
44
|
+
contents.elements.collect { |p| p.to_markdown }.join
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ParagraphNode
|
49
|
+
include RecursiveConversion
|
50
|
+
|
51
|
+
def to_html
|
52
|
+
# Convert all child nodes, concatenate the results,
|
53
|
+
# and wrap the concatenated HTML in <p> tags.
|
54
|
+
html = elements.collect do |node|
|
55
|
+
recursively_convert(node, :to_html)
|
56
|
+
end.join
|
57
|
+
"\n<p>" + html + "</p>\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_markdown
|
61
|
+
# Convert all child nodes, concatenate the results,
|
62
|
+
# and append newline characters.
|
63
|
+
markdown = elements.collect do |node|
|
64
|
+
recursively_convert(node, :to_markdown)
|
65
|
+
end.join
|
66
|
+
markdown + "\n\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module BlockquoteNode
|
71
|
+
include RecursiveConversion
|
72
|
+
|
73
|
+
def to_html
|
74
|
+
# Detect paragraph breaks and wrap the result in <blockquote> tags.
|
75
|
+
paragraphs = []
|
76
|
+
cur_para = ''
|
77
|
+
lines.elements.each do |line|
|
78
|
+
inner = recursively_convert(line, :to_html)
|
79
|
+
unless inner.blank?
|
80
|
+
cur_para << inner
|
81
|
+
if line.post_breaks == 1
|
82
|
+
cur_para << ' '
|
83
|
+
elsif line.post_breaks >= 2
|
84
|
+
paragraphs << cur_para
|
85
|
+
cur_para = ''
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
unless cur_para.blank?
|
90
|
+
paragraphs << cur_para
|
91
|
+
end
|
92
|
+
inner = paragraphs.map { |str| "<p>#{str}</p>" }.join("\n")
|
93
|
+
"\n<blockquote>" + inner + "</blockquote>\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_markdown
|
97
|
+
# Add a > character per line, preserving linebreaks as they are in the source.
|
98
|
+
# Then append two newlines.
|
99
|
+
'> ' + lines.elements.inject('') do |output, line|
|
100
|
+
inner_markdown = recursively_convert(line.contents, :to_markdown)
|
101
|
+
output + inner_markdown + ("\n> " * line.post_breaks)
|
102
|
+
end + "\n\n"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
module BlockquoteLineNode
|
107
|
+
# Returns the number of line breaks after this line. May be zero for the final
|
108
|
+
# line, since there doesn't have to be a break before [/quote].
|
109
|
+
def post_breaks
|
110
|
+
breaks.elements.length
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module ListNode
|
115
|
+
include RecursiveConversion
|
116
|
+
|
117
|
+
def to_html
|
118
|
+
# Convert the :contents child node (defined in the .treetop file)
|
119
|
+
# and wrap the result in <ul> tags.
|
120
|
+
"\n<ul>" + recursively_convert(items, :to_html) + "</ul>\n"
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_markdown
|
124
|
+
# Convert the :contents child node (defined in the .treetop file).
|
125
|
+
# (Unlike with HTML, no outer markup needed.) Then append an extra
|
126
|
+
# newline, for a total of two at the end.
|
127
|
+
recursively_convert(items, :to_markdown) + "\n"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
module ListItemNode
|
132
|
+
include RecursiveConversion
|
133
|
+
|
134
|
+
def to_html
|
135
|
+
# Convert the :contents child node (defined in the .treetop file)
|
136
|
+
# and wrap the result in <li> tags.
|
137
|
+
"\n<li>" + recursively_convert(contents, :to_html) + "</li>\n"
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_markdown
|
141
|
+
# Convert the :contents child node (defined in the .treetop file)
|
142
|
+
# and add * characters.
|
143
|
+
"* " + recursively_convert(contents, :to_html) + "\n"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# You won't find this module in the .treetop file. Instead, it's effectively a specialization
|
148
|
+
# of TagNode, which calls to ImgTagNode when processing an img tag. (However, one of the
|
149
|
+
# child nodes used here, :url, is indeed defined in the .treetop file.)
|
150
|
+
module URLTagNode
|
151
|
+
include Attributes
|
152
|
+
|
153
|
+
def url_to_html
|
154
|
+
# The :url child node (defined in the .treetop file) may or may not exist,
|
155
|
+
# depending on how the link is formatted in the BBCode source.
|
156
|
+
if respond_to?(:url) and respond_to?(:text)
|
157
|
+
# This is a URL tag formatted like [url=http://example.com]Example[/url].
|
158
|
+
'<a href="' + strip_quotes(url.text_value) + '">' + text.text_value + '</a>'
|
159
|
+
else
|
160
|
+
# This is a URL tag formatted like [url]http://example.com[/url].
|
161
|
+
'<a href="' + inner_bbcode + '">' + inner_bbcode + '</a>'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def url_to_markdown
|
166
|
+
if respond_to?(:url) and respond_to?(:text)
|
167
|
+
# This is a URL tag formatted like [url=http://example.com]Example[/url].
|
168
|
+
'[' + text.text_value + '](' + strip_quotes(url.text_value) + ')'
|
169
|
+
else
|
170
|
+
# This is a URL tag formatted like [url]http://example.com[/url].
|
171
|
+
'[' + inner_bbcode + '](' + inner_bbcode + ')'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# You won't find this module in the .treetop file. Instead, it's effectively a specialization
|
177
|
+
# of TagNode, which calls to ImgTagNode when processing an img tag.
|
178
|
+
module ImgTagNode
|
179
|
+
def img_to_html
|
180
|
+
'<img src="' + inner_bbcode + '" alt="Image"/>'
|
181
|
+
end
|
182
|
+
|
183
|
+
def img_to_markdown
|
184
|
+
""
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
module UTagNode
|
189
|
+
def u_to_markdown
|
190
|
+
# Underlining is unsupported in Markdown. So we just ignore [u] tags.
|
191
|
+
inner_bbcode
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module TagNode
|
196
|
+
include RecursiveConversion
|
197
|
+
|
198
|
+
# For each tag name, we can either: (a) map to a simple HTML tag or Markdown character, or
|
199
|
+
# (b) invoke a separate Ruby module for more advanced logic.
|
200
|
+
TAG_MAPPINGS = {
|
201
|
+
html: {'b' => 'strong', 'i' => 'em', 'u' => 'u', 'url' => URLTagNode, 'img' => ImgTagNode},
|
202
|
+
markdown: {'b' => '**', 'i' => '*', 'u' => UTagNode, 'url' => URLTagNode, 'img' => ImgTagNode}
|
203
|
+
}
|
204
|
+
|
205
|
+
def contents
|
206
|
+
# The first element is the opening tag, the second is everything inside,
|
207
|
+
# and the third is the closing tag.
|
208
|
+
elements[1]
|
209
|
+
end
|
210
|
+
|
211
|
+
def tag_name
|
212
|
+
elements.first.text_value.slice(1..-2).downcase
|
213
|
+
end
|
214
|
+
|
215
|
+
def inner_bbcode
|
216
|
+
contents.elements.collect { |e| e.text_value }.join
|
217
|
+
end
|
218
|
+
|
219
|
+
def inner_html
|
220
|
+
contents.elements.collect do |node|
|
221
|
+
recursively_convert(node, :to_html)
|
222
|
+
end.join
|
223
|
+
end
|
224
|
+
|
225
|
+
def inner_markdown
|
226
|
+
contents.elements.collect do |node|
|
227
|
+
recursively_convert(node, :to_markdown)
|
228
|
+
end.join
|
229
|
+
end
|
230
|
+
|
231
|
+
def wrap_html(t)
|
232
|
+
"<#{t}>" + inner_html + "</#{t}>"
|
233
|
+
end
|
234
|
+
|
235
|
+
def wrap_markdown(t)
|
236
|
+
t + inner_markdown + t
|
237
|
+
end
|
238
|
+
|
239
|
+
def convert(output_format)
|
240
|
+
# Consult TAG_MAPPINGS to decide how to process this type of tag.
|
241
|
+
t = TAG_MAPPINGS[output_format][tag_name]
|
242
|
+
if t.nil?
|
243
|
+
raise "No tag mapping found for #{tag_name}"
|
244
|
+
elsif t.is_a?(Module)
|
245
|
+
# This type of tag requires more than just a simple mapping from one tag name
|
246
|
+
# to another. So we invoke a separate Ruby module.
|
247
|
+
extend(t)
|
248
|
+
send("#{tag_name}_to_#{output_format}")
|
249
|
+
# Thus, if our tag_name is"url, and TAG_MAPPINGS points us to URLTagNode,
|
250
|
+
# that module must define url_to_html.
|
251
|
+
else
|
252
|
+
# For this type of tag, a simple mapping from the tag name to a string (such as
|
253
|
+
# <i>) suffices.
|
254
|
+
send("wrap_#{output_format}", t)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def to_html
|
259
|
+
convert :html
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_markdown
|
263
|
+
convert :markdown
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
module SingleBreakNode
|
268
|
+
def to_html
|
269
|
+
'<br/>'
|
270
|
+
end
|
271
|
+
|
272
|
+
def to_markdown
|
273
|
+
"\n"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
module LiteralTextNode
|
278
|
+
def to_html
|
279
|
+
text_value
|
280
|
+
end
|
281
|
+
|
282
|
+
def to_markdown
|
283
|
+
text_value
|
284
|
+
end
|
285
|
+
end
|
126
286
|
end
|
@@ -1,113 +1,110 @@
|
|
1
|
-
<%
|
2
|
-
def def_tag(rule_name, tag_name)
|
3
|
-
"
|
4
|
-
rule #{rule_name}
|
5
|
-
|
6
|
-
(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
rule
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
<%= def_tag '
|
103
|
-
<%= def_tag '
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
text:(!'[/url]' .)+
|
111
|
-
'[/url]'
|
112
|
-
end
|
1
|
+
<%
|
2
|
+
def def_tag(rule_name, tag_name)
|
3
|
+
"
|
4
|
+
rule #{rule_name}
|
5
|
+
# Opening tag
|
6
|
+
(
|
7
|
+
'[#{tag_name.downcase}]' /
|
8
|
+
'[#{tag_name.upcase}]'
|
9
|
+
)
|
10
|
+
# Inner BBCode (which may include nested tags)
|
11
|
+
(
|
12
|
+
!'[/#{tag_name.downcase}]'
|
13
|
+
!'[/#{tag_name.upcase}]'
|
14
|
+
(tag / .)
|
15
|
+
)+
|
16
|
+
# Closing tag
|
17
|
+
(
|
18
|
+
'[/#{tag_name.downcase}]' /
|
19
|
+
'[/#{tag_name.upcase}]'
|
20
|
+
)
|
21
|
+
end
|
22
|
+
"
|
23
|
+
end
|
24
|
+
%>
|
25
|
+
|
26
|
+
grammar RbbCodeGrammar
|
27
|
+
rule document
|
28
|
+
contents:(blockquote / list / paragraph / literal_text)*
|
29
|
+
break_ws*
|
30
|
+
<RbbCode::DocumentNode>
|
31
|
+
end
|
32
|
+
|
33
|
+
rule literal_text
|
34
|
+
[^\n]+ <RbbCode::LiteralTextNode>
|
35
|
+
end
|
36
|
+
|
37
|
+
rule paragraph
|
38
|
+
(break_ws 2..)
|
39
|
+
(
|
40
|
+
!(break_ws 2..)
|
41
|
+
(tag / single_break_ws / .)
|
42
|
+
)+
|
43
|
+
<RbbCode::ParagraphNode>
|
44
|
+
end
|
45
|
+
|
46
|
+
rule break_ws
|
47
|
+
# A linebreak, possibly surrounded by whitespace
|
48
|
+
[ \t]* "\n" [ \t]*
|
49
|
+
end
|
50
|
+
|
51
|
+
rule single_break_ws
|
52
|
+
# We don't count linebreaks when they're immediately followed by
|
53
|
+
# certain keywords. This avoids printing an extra <br/> in some cases.
|
54
|
+
break_ws !break_ws !(break_ws* ('[/quote]' / '[*]' / '[/list]')) <RbbCode::SingleBreakNode>
|
55
|
+
end
|
56
|
+
|
57
|
+
rule blockquote
|
58
|
+
break_ws*
|
59
|
+
'[quote]'
|
60
|
+
"\n"*
|
61
|
+
lines:blockquote_line*
|
62
|
+
'[/quote]'
|
63
|
+
<RbbCode::BlockquoteNode>
|
64
|
+
end
|
65
|
+
|
66
|
+
rule blockquote_line
|
67
|
+
contents:(!('[/quote]' / "\n") (tag / .))+
|
68
|
+
[ \t]*
|
69
|
+
breaks:break_ws*
|
70
|
+
<RbbCode::BlockquoteLineNode>
|
71
|
+
end
|
72
|
+
|
73
|
+
rule list
|
74
|
+
break_ws*
|
75
|
+
'[list]'
|
76
|
+
[ \t\n]*
|
77
|
+
items:list_item*
|
78
|
+
[ \t\n]*
|
79
|
+
'[/list]'
|
80
|
+
<RbbCode::ListNode>
|
81
|
+
end
|
82
|
+
|
83
|
+
rule list_item
|
84
|
+
'[*]'
|
85
|
+
[ \t]*
|
86
|
+
contents:(
|
87
|
+
!'[/list]' !'[*]'
|
88
|
+
(tag / single_break_ws / .)
|
89
|
+
)*
|
90
|
+
<RbbCode::ListItemNode>
|
91
|
+
end
|
92
|
+
|
93
|
+
rule tag
|
94
|
+
# Make sure that anytime you call def_tag, you add it to this list:
|
95
|
+
(bold / italic / underline / simple_url / complex_url / img)
|
96
|
+
<RbbCode::TagNode>
|
97
|
+
end
|
98
|
+
|
99
|
+
<%= def_tag 'bold', 'b' %>
|
100
|
+
<%= def_tag 'italic', 'i' %>
|
101
|
+
<%= def_tag 'underline', 'u' %>
|
102
|
+
<%= def_tag 'simple_url', 'url' %>
|
103
|
+
<%= def_tag 'img', 'img' %>
|
104
|
+
|
105
|
+
rule complex_url
|
106
|
+
'[url=' url:[^\]]+ ']'
|
107
|
+
text:(!'[/url]' .)+
|
108
|
+
'[/url]'
|
109
|
+
end
|
113
110
|
end
|
data/lib/rbbcode/sanitize.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
class RbbCode
|
2
|
-
DEFAULT_SANITIZE_CONFIG = {
|
3
|
-
:elements => %w[a blockquote br code del em img li p pre strong ul],
|
4
|
-
:attributes => {
|
5
|
-
'a' => %w[href],
|
6
|
-
'img' => %w[alt src]
|
7
|
-
},
|
8
|
-
|
9
|
-
:protocols => {
|
10
|
-
'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]}
|
11
|
-
}
|
12
|
-
}
|
1
|
+
class RbbCode
|
2
|
+
DEFAULT_SANITIZE_CONFIG = {
|
3
|
+
:elements => %w[a blockquote br code del em img li p pre strong ul u],
|
4
|
+
:attributes => {
|
5
|
+
'a' => %w[href target],
|
6
|
+
'img' => %w[alt src]
|
7
|
+
},
|
8
|
+
|
9
|
+
:protocols => {
|
10
|
+
'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]}
|
11
|
+
}
|
12
|
+
}
|
13
13
|
end
|
metadata
CHANGED
@@ -1,88 +1,132 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbcode
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 1.0.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
6
5
|
platform: ruby
|
7
|
-
authors:
|
6
|
+
authors:
|
8
7
|
- Jarrett Colby
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2020-06-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
17
14
|
name: treetop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.5.3
|
20
|
+
type: :runtime
|
18
21
|
prerelease: false
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.5.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sanitize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
22
31
|
- - ">="
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version:
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
25
34
|
type: :runtime
|
26
|
-
|
27
|
-
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
28
70
|
name: lorax
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.0.rc2
|
76
|
+
type: :development
|
29
77
|
prerelease: false
|
30
|
-
|
31
|
-
|
32
|
-
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.0.rc2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
33
87
|
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version:
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
36
90
|
type: :development
|
37
|
-
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
38
97
|
description: Converts BBCode to HTML. Gracefully handles invalid input.
|
39
98
|
email: jarrett@madebyhq.com
|
40
99
|
executables: []
|
41
|
-
|
42
100
|
extensions: []
|
43
|
-
|
44
101
|
extra_rdoc_files: []
|
45
|
-
|
46
|
-
|
102
|
+
files:
|
103
|
+
- lib/rbbcode.rb
|
47
104
|
- lib/rbbcode/node_extensions.rb
|
48
105
|
- lib/rbbcode/rbbcode_grammar.treetop
|
49
106
|
- lib/rbbcode/sanitize.rb
|
50
|
-
- lib/rbbcode.rb
|
51
|
-
has_rdoc: true
|
52
107
|
homepage: https://github.com/jarrett/rbbcode
|
53
108
|
licenses: []
|
54
|
-
|
55
|
-
post_install_message: "\n\
|
56
|
-
|
57
|
-
|
58
|
-
RbbCode
|
59
|
-
is not compatible with the old one (0.1.11). If\n\
|
60
|
-
you want to upgrade to 1.x.x, you'll need to\n\
|
61
|
-
adjust any calls to RbbCode in your code to match\n\
|
62
|
-
the new API. For more information:\n\n\
|
63
|
-
https://github.com/jarrett/rbbcode\n"
|
109
|
+
metadata: {}
|
110
|
+
post_install_message: "\r\nImportant notice for users of 0.1.11 or lower\r\n=============================================\r\n\r\nRbbCode
|
111
|
+
has been updated! The new release (1.x.x)\r\nis not compatible with the old one
|
112
|
+
(0.1.11). If\r\nyou want to upgrade to 1.x.x, you'll need to\r\nadjust any calls
|
113
|
+
to RbbCode in your code to match\r\nthe new API. For more information:\r\n\r\nhttps://github.com/jarrett/rbbcode\r\n"
|
64
114
|
rdoc_options: []
|
65
|
-
|
66
|
-
require_paths:
|
115
|
+
require_paths:
|
67
116
|
- lib
|
68
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
-
|
70
|
-
requirements:
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
71
119
|
- - ">="
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
version:
|
74
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
-
|
76
|
-
requirements:
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
77
124
|
- - ">="
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version:
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
80
127
|
requirements: []
|
81
|
-
|
82
|
-
rubyforge_project:
|
83
|
-
rubygems_version: 1.6.2
|
128
|
+
rubygems_version: 3.1.2
|
84
129
|
signing_key:
|
85
|
-
specification_version:
|
130
|
+
specification_version: 4
|
86
131
|
summary: RbbCode
|
87
132
|
test_files: []
|
88
|
-
|