rbbcode 1.0.4 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3894f3d4d56f04b0c0d7ad5fa194eef37e9933f49993dc9ddeb243b4bdf1bda9
4
+ data.tar.gz: f0167cb15f14b2c37dbbd55205e4e3b08275f26b8d8c9d9e9a95c684741c451f
5
+ SHA512:
6
+ metadata.gz: 1a40401398844f12b7b874cedfc173bb98a887bc4dad718d2d30ece44b338060c84441e87265b12b91e777608a94b29f7eb9f074f49f810acb1f2dc1de9e01be
7
+ data.tar.gz: f0712fb419b27353fd569ce150adf74c47ef2d45cc6ed16dc80bcb6bd759354e4b3233596dfa942cda9845cb288d6a720b7a73dc2dff78b16538c17a8736b315
@@ -1,141 +1,286 @@
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, depth = 0)
11
- if node.terminal?
12
- if node.respond_to?(:to_html)
13
- node.to_html
14
- else
15
- node.text_value.match(/^[\r\n\t]+$/) ? '' : node.text_value
16
- end
17
- else
18
- if node.respond_to?(:to_html)
19
- node.to_html
20
- else
21
- node.elements.collect do |sub_node|
22
- recursively_convert(sub_node, depth + 1)
23
- end.join
24
- end
25
- end
26
- end
27
- end
28
-
29
- module DocumentNode
30
- def to_html
31
- contents.elements.collect { |p| p.to_html }.join
32
- end
33
- end
34
-
35
- module ParagraphNode
36
- include RecursiveConversion
37
-
38
- def to_html
39
- html = elements.collect do |node|
40
- recursively_convert(node)
41
- end.join
42
- "\n<p>" + html + "</p>\n"
43
- end
44
- end
45
-
46
- module BlockquoteNode
47
- include RecursiveConversion
48
-
49
- def to_html
50
- "\n<blockquote>" + recursively_convert(contents) + "</blockquote>\n"
51
- end
52
- end
53
-
54
- module ListNode
55
- include RecursiveConversion
56
-
57
- def to_html
58
- "\n<ul>" + recursively_convert(contents) + "</ul>\n"
59
- end
60
- end
61
-
62
- module ListItemNode
63
- include RecursiveConversion
64
-
65
- def to_html
66
- "\n<li>" + recursively_convert(contents) + "</li>\n"
67
- end
68
- end
69
-
70
- module URLTagNode
71
- include Attributes
72
-
73
- def url_to_html
74
- if respond_to?(:url) and respond_to?(:text)
75
- # A URL tag formatted like [url=http://example.com]Example[/url]
76
- '<a href="' + strip_quotes(url.text_value) + '">' + text.text_value + '</a>'
77
- else
78
- # A URL tag formatted like [url]http://example.com[/url]
79
- '<a href="' + inner_bbcode + '">' + inner_bbcode + '</a>'
80
- end
81
- end
82
- end
83
-
84
- module ImgTagNode
85
- def img_to_html
86
- '<img src="' + inner_bbcode + '" alt="Image"/>'
87
- end
88
- end
89
-
90
- module TagNode
91
- include RecursiveConversion
92
-
93
- TAG_MAPPINGS = {'b' => 'strong', 'i' => 'em', 'u' => 'u', 'url' => URLTagNode, 'img' => ImgTagNode}
94
-
95
- def contents
96
- # The first element is the opening tag, the second is everything inside,
97
- # and the third is the closing tag.
98
- elements[1]
99
- end
100
-
101
- def tag_name
102
- elements.first.text_value.slice(1..-2).downcase
103
- end
104
-
105
- def inner_bbcode
106
- contents.elements.collect { |e| e.text_value }.join
107
- end
108
-
109
- def inner_html
110
- contents.elements.collect do |node|
111
- recursively_convert(node)
112
- end.join
113
- end
114
-
115
- def to_html
116
- t = TAG_MAPPINGS[tag_name]
117
- if t.nil?
118
- raise "No tag mapping found for #{tag_name}"
119
- elsif t.is_a?(Module)
120
- extend(t)
121
- send(tag_name + '_to_html')
122
- # Thus, if our tag_name is"url, and TAG_MAPPINGS points us to URLTagNode,
123
- # that module must define url_to_html.
124
- else
125
- "<#{t}>" + inner_html + "</#{t}>"
126
- end
127
- end
128
- end
129
-
130
- module SingleBreakNode
131
- def to_html
132
- '<br/>'
133
- end
134
- end
135
-
136
- module LiteralTextNode
137
- def to_html
138
- text_value
139
- end
140
- end
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, options, 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, options)
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, options, depth + 1)
32
+ end.join
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module DocumentNode
39
+ def to_html(options)
40
+ contents.elements.collect { |p| p.to_html(options) }.join
41
+ end
42
+
43
+ def to_markdown(options)
44
+ contents.elements.collect { |p| p.to_markdown(options) }.join
45
+ end
46
+ end
47
+
48
+ module ParagraphNode
49
+ include RecursiveConversion
50
+
51
+ def to_html(options)
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, options)
56
+ end.join
57
+ "\n<p>" + html + "</p>\n"
58
+ end
59
+
60
+ def to_markdown(options)
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, options)
65
+ end.join
66
+ markdown + "\n\n"
67
+ end
68
+ end
69
+
70
+ module BlockquoteNode
71
+ include RecursiveConversion
72
+
73
+ def to_html(options)
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, options)
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(options)
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, options)
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(options)
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, options) + "</ul>\n"
121
+ end
122
+
123
+ def to_markdown(options)
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, options) + "\n"
128
+ end
129
+ end
130
+
131
+ module ListItemNode
132
+ include RecursiveConversion
133
+
134
+ def to_html(options)
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, options) + "</li>\n"
138
+ end
139
+
140
+ def to_markdown(options)
141
+ # Convert the :contents child node (defined in the .treetop file)
142
+ # and add * characters.
143
+ "* " + recursively_convert(contents, :to_html, options) + "\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(options)
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(options)
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(options)
180
+ '<img src="' + inner_bbcode + '" alt="Image"/>'
181
+ end
182
+
183
+ def img_to_markdown(options)
184
+ "![Image](#{inner_bbcode})"
185
+ end
186
+ end
187
+
188
+ module UTagNode
189
+ def u_to_markdown(options)
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(options)
220
+ contents.elements.collect do |node|
221
+ recursively_convert(node, :to_html, options)
222
+ end.join
223
+ end
224
+
225
+ def inner_markdown(options)
226
+ contents.elements.collect do |node|
227
+ recursively_convert(node, :to_markdown, options)
228
+ end.join
229
+ end
230
+
231
+ def wrap_html(t, options)
232
+ "<#{t}>" + inner_html(options) + "</#{t}>"
233
+ end
234
+
235
+ def wrap_markdown(t, options)
236
+ t + inner_markdown(options) + t
237
+ end
238
+
239
+ def convert(output_format, options)
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}", options)
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, options)
255
+ end
256
+ end
257
+
258
+ def to_html(options)
259
+ convert :html, options
260
+ end
261
+
262
+ def to_markdown(options)
263
+ convert :markdown, options
264
+ end
265
+ end
266
+
267
+ module SingleBreakNode
268
+ def to_html(options)
269
+ '<br/>'
270
+ end
271
+
272
+ def to_markdown(options)
273
+ "\n"
274
+ end
275
+ end
276
+
277
+ module LiteralTextNode
278
+ def to_html(options)
279
+ text_value
280
+ end
281
+
282
+ def to_markdown(options)
283
+ text_value
284
+ end
285
+ end
141
286
  end
@@ -1,113 +1,110 @@
1
- <%
2
- def def_tag(rule_name, tag_name)
3
- "
4
- rule #{rule_name}
5
- ('[#{tag_name.downcase}]'/'[#{tag_name.upcase}]')
6
- (!'[/#{tag_name.downcase}]' !'[/#{tag_name.upcase}]'
7
- (tag <RbbCode::TagNode> / .))+
8
- ('[/#{tag_name.downcase}]' / '[/#{tag_name.upcase}]')
9
- end
10
- "
11
- end
12
- %>
13
-
14
- grammar RbbCodeGrammar
15
- rule document
16
- # Consume the trailing linebreaks, because the paragraph lookahead
17
- # doesn't consume them.
18
- contents:(blockquote <RbbCode::BlockquoteNode> / list <RbbCode::ListNode> / paragraph <RbbCode::ParagraphNode> / . <RbbCode::LiteralTextNode>)* break_ws* <RbbCode::DocumentNode>
19
- end
20
-
21
- rule paragraph
22
- (break_ws 2..)
23
- (
24
- !(break_ws 2..)
25
- paragraph_contents
26
- )+
27
- end
28
-
29
- rule paragraph_contents
30
- (tag <RbbCode::TagNode> / single_break_ws / .)
31
- end
32
-
33
- rule break_ws
34
- # Allow whitespace around the linebreaks
35
- [ \t]* [\r\n] [ \t]*
36
- end
37
-
38
- rule whitespace
39
- # Any whitespace, including linebreaks
40
- [ \t\r\n]
41
- end
42
-
43
- rule single_break_ws
44
- # We don't count linebreaks when they're immediately followed by
45
- # certain keywords. This avoids printing an extra <br/> in some cases.
46
- break_ws !break_ws !(break_ws* ('[/quote]' / '[*]' / '[/list]')) <RbbCode::SingleBreakNode>
47
- end
48
-
49
- rule blockquote
50
- break_ws*
51
- '[quote]'
52
- contents:(
53
- # Possible linebreaks after opening quote tag
54
- break_ws*
55
-
56
- # First paragraph (mandatory)
57
- (blockquote_paragraph <RbbCode::ParagraphNode>)
58
-
59
- # Subsequent paragraphs (optional)
60
- (
61
- (break_ws 2..)
62
- (blockquote_paragraph <RbbCode::ParagraphNode>)
63
- )*
64
-
65
- # Possible linebreaks before closing quote tag
66
- break_ws*
67
- )
68
- '[/quote]'
69
- end
70
-
71
- rule blockquote_paragraph
72
- (!('[/quote]' / (break_ws 2..)) paragraph_contents)+
73
- end
74
-
75
- rule list
76
- break_ws*
77
- '[list]'
78
- contents:(
79
- # Possible linebreaks after opening list tag
80
- whitespace*
81
-
82
- # At least one list item
83
- (
84
- (
85
- '[*]'
86
- contents:(!'[/list]' !'[*]' paragraph_contents)*
87
- <RbbCode::ListItemNode>
88
- )
89
- )+
90
-
91
- # Possible linebreaks before closing list tag
92
- whitespace*
93
- )
94
- '[/list]'
95
- end
96
-
97
- rule tag
98
- # Make sure that anytime you call def_tag, you add it to this list:
99
- bold / italic / underline / simple_url / complex_url / img
100
- end
101
-
102
- <%= def_tag 'bold', 'b' %>
103
- <%= def_tag 'italic', 'i' %>
104
- <%= def_tag 'underline', 'u' %>
105
- <%= def_tag 'simple_url', 'url' %>
106
- <%= def_tag 'img', 'img' %>
107
-
108
- rule complex_url
109
- '[url=' url:[^\]]+ ']'
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
@@ -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 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
- }
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
data/lib/rbbcode.rb CHANGED
@@ -1,50 +1,72 @@
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
- :sanitize => true,
32
- :sanitize_config => RbbCode::DEFAULT_SANITIZE_CONFIG
33
- }.merge(options)
34
- end
35
-
36
- def convert(bb_code)
37
- html = self.class.parser_class.new.parse("\n\n" + bb_code + "\n\n").to_html
38
- if @options[:emoticons]
39
- @options[:emoticons].each do |emoticon, url|
40
- html.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>')
41
- end
42
- end
43
- html
44
- if @options[:sanitize]
45
- Sanitize.clean(html, @options[:sanitize_config])
46
- else
47
- html
48
- end
49
- end
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
+ :emoticons => false,
33
+ :sanitize => true,
34
+ :sanitize_config => RbbCode::DEFAULT_SANITIZE_CONFIG
35
+ }.merge(options)
36
+ end
37
+
38
+ def convert(bb_code, options = {})
39
+ # Options passed to #convert will override any options passed to .new.
40
+ options = @options.merge(options)
41
+ output_format = options.delete(:output_format)
42
+ emoticons = options.delete(:emoticons)
43
+ sanitize = options.delete(:sanitize)
44
+ sanitize_config = options.delete(:sanitize_config)
45
+
46
+ # Collapse CRLFs to LFs. Then replace any solitary CRs with LFs.
47
+ bb_code = bb_code.gsub("\r\n", "\n").gsub("\r", "\n")
48
+ # Add linebreaks before and after so that paragraphs etc. can be recognized.
49
+ bb_code = "\n\n" + bb_code + "\n\n"
50
+ output = self.class.parser_class.new.parse(bb_code).send("to_#{output_format}", options)
51
+ if emoticons
52
+ output = convert_emoticons(output)
53
+ end
54
+ # Sanitization works for HTML only.
55
+ if output_format == :html and sanitize
56
+ Sanitize.clean(output, sanitize_config)
57
+ else
58
+ output
59
+ end
60
+ end
61
+
62
+ def convert_emoticons(output)
63
+ @options[:emoticons].each do |emoticon, url|
64
+ output.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>')
65
+ end
66
+ output
67
+ end
68
+
69
+ def output_format
70
+ @options[:output_format]
71
+ end
50
72
  end
metadata CHANGED
@@ -1,116 +1,138 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbcode
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
5
- prerelease:
4
+ version: 1.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jarrett Colby
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-10-27 00:00:00.000000000 Z
11
+ date: 2023-07-06 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: treetop
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '='
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: 1.5.3
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '='
28
25
  - !ruby/object:Gem::Version
29
- version: '0'
26
+ version: 1.5.3
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: sanitize
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: '0'
33
+ version: '6'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 6.0.2
38
37
  type: :runtime
39
38
  prerelease: false
40
39
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
40
  requirements:
43
- - - ! '>='
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '6'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 6.0.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
44
59
  - !ruby/object:Gem::Version
45
- version: '0'
60
+ version: '5'
61
+ - !ruby/object:Gem::Dependency
62
+ name: minitest-reporters
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1'
46
75
  - !ruby/object:Gem::Dependency
47
76
  name: lorax
48
77
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
78
  requirements:
51
- - - ! '>='
79
+ - - '='
52
80
  - !ruby/object:Gem::Version
53
- version: '0'
81
+ version: 0.3.0.rc2
54
82
  type: :development
55
83
  prerelease: false
56
84
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
85
  requirements:
59
- - - ! '>='
86
+ - - '='
87
+ - !ruby/object:Gem::Version
88
+ version: 0.3.0.rc2
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
60
94
  - !ruby/object:Gem::Version
61
- version: '0'
95
+ version: '13'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13'
62
103
  description: Converts BBCode to HTML. Gracefully handles invalid input.
63
104
  email: jarrett@madebyhq.com
64
105
  executables: []
65
106
  extensions: []
66
107
  extra_rdoc_files: []
67
108
  files:
109
+ - lib/rbbcode.rb
68
110
  - lib/rbbcode/node_extensions.rb
69
111
  - lib/rbbcode/rbbcode_grammar.treetop
70
112
  - lib/rbbcode/sanitize.rb
71
- - lib/rbbcode.rb
72
113
  homepage: https://github.com/jarrett/rbbcode
73
114
  licenses: []
74
- post_install_message: ! '
75
-
76
- Important notice for users of 0.1.11 or lower
77
-
78
- =============================================
79
-
80
-
81
- RbbCode has been updated! The new release (1.x.x)
82
-
83
- is not compatible with the old one (0.1.11). If
84
-
85
- you want to upgrade to 1.x.x, you''ll need to
86
-
87
- adjust any calls to RbbCode in your code to match
88
-
89
- the new API. For more information:
90
-
91
-
92
- https://github.com/jarrett/rbbcode
93
-
94
- '
115
+ metadata: {}
116
+ post_install_message: "\r\nImportant notice for users of 0.1.11 or lower\r\n=============================================\r\n\r\nRbbCode
117
+ has been updated! The new release (1.x.x)\r\nis not compatible with the old one
118
+ (0.1.11). If\r\nyou want to upgrade to 1.x.x, you'll need to\r\nadjust any calls
119
+ to RbbCode in your code to match\r\nthe new API. For more information:\r\n\r\nhttps://github.com/jarrett/rbbcode\r\n"
95
120
  rdoc_options: []
96
121
  require_paths:
97
122
  - lib
98
123
  required_ruby_version: !ruby/object:Gem::Requirement
99
- none: false
100
124
  requirements:
101
- - - ! '>='
125
+ - - ">="
102
126
  - !ruby/object:Gem::Version
103
127
  version: '0'
104
128
  required_rubygems_version: !ruby/object:Gem::Requirement
105
- none: false
106
129
  requirements:
107
- - - ! '>='
130
+ - - ">="
108
131
  - !ruby/object:Gem::Version
109
132
  version: '0'
110
133
  requirements: []
111
- rubyforge_project:
112
- rubygems_version: 1.8.25
134
+ rubygems_version: 3.1.2
113
135
  signing_key:
114
- specification_version: 3
136
+ specification_version: 4
115
137
  summary: RbbCode
116
138
  test_files: []