Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
438 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
require 'rake/testtask' | ||
|
||
Rake::TestTask.new do |t| | ||
t.test_files = FileList['test/*_test.rb'] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Uncomment this when developing: | ||
#$:.unshift './lib' | ||
|
||
require 'erb' | ||
require 'rubygems' | ||
require 'treetop' | ||
require 'rbbcode/node_extensions' | ||
|
||
class RbbCode | ||
def self.parser_class | ||
if !@grammar_loaded | ||
Treetop.load_from_string( | ||
ERB.new( | ||
File.read( | ||
File.join( | ||
File.dirname(__FILE__), | ||
'rbbcode/rbbcode_grammar.treetop' | ||
) | ||
) | ||
).result | ||
) | ||
@grammar_loaded = true | ||
end | ||
RbbCodeGrammarParser | ||
end | ||
|
||
def initialize(options = {}) | ||
@options = options | ||
end | ||
|
||
def convert(bb_code) | ||
html = self.class.parser_class.new.parse("\n\n" + bb_code + "\n\n").to_html | ||
if @options[:emoticons] | ||
@options[:emoticons].each do |emoticon, url| | ||
html.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>') | ||
end | ||
end | ||
html | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
class RbbCode | ||
module RecursiveConversion | ||
def recursively_convert(node, depth = 0) | ||
if node.terminal? | ||
if node.respond_to?(:to_html) | ||
node.to_html | ||
else | ||
node.text_value.match(/^[\r\n\t]+$/) ? '' : node.text_value | ||
end | ||
else | ||
if node.respond_to?(:to_html) | ||
node.to_html | ||
else | ||
node.elements.collect do |sub_node| | ||
recursively_convert(sub_node, depth + 1) | ||
end.join | ||
end | ||
end | ||
end | ||
end | ||
|
||
module DocumentNode | ||
def to_html | ||
contents.elements.collect { |p| p.to_html }.join | ||
end | ||
end | ||
|
||
module ParagraphNode | ||
include RecursiveConversion | ||
|
||
def to_html | ||
html = elements.collect do |node| | ||
recursively_convert(node) | ||
end.join | ||
"\n<p>" + html + "</p>\n" | ||
end | ||
end | ||
|
||
module BlockquoteNode | ||
include RecursiveConversion | ||
|
||
def to_html | ||
"\n<blockquote>" + recursively_convert(contents) + "</blockquote>\n" | ||
end | ||
end | ||
|
||
module ListNode | ||
include RecursiveConversion | ||
|
||
def to_html | ||
"\n<ul>" + recursively_convert(contents) + "</ul>\n" | ||
end | ||
end | ||
|
||
module ListItemNode | ||
include RecursiveConversion | ||
|
||
def to_html | ||
"\n<li>" + recursively_convert(contents) + "</li>\n" | ||
end | ||
end | ||
|
||
module URLTagNode | ||
def url_to_html | ||
if respond_to?(:url) and respond_to?(:text) | ||
# A URL tag formatted like [url=http://example.com]Example[/url] | ||
'<a href="' + url.text_value + '">' + text.text_value + '</a>' | ||
else | ||
# A URL tag formatted like [url]http://example.com[/url] | ||
'<a href="' + inner_bbcode + '">' + inner_bbcode + '</a>' | ||
end | ||
end | ||
end | ||
|
||
module ImgTagNode | ||
def img_to_html | ||
'<img src="' + inner_bbcode + '" alt="Image"/>' | ||
end | ||
end | ||
|
||
module TagNode | ||
include RecursiveConversion | ||
|
||
TAG_MAPPINGS = {'b' => 'strong', 'i' => 'em', 'u' => 'u', 'url' => URLTagNode, 'img' => ImgTagNode} | ||
|
||
def contents | ||
# The first element is the opening tag, the second is everything inside, | ||
# and the third is the closing tag. | ||
elements[1] | ||
end | ||
|
||
def tag_name | ||
elements.first.text_value.slice(1..-2).downcase | ||
end | ||
|
||
def inner_bbcode | ||
contents.elements.collect { |e| e.text_value }.join | ||
end | ||
|
||
def inner_html | ||
contents.elements.collect do |node| | ||
recursively_convert(node) | ||
end.join | ||
end | ||
|
||
def to_html | ||
t = TAG_MAPPINGS[tag_name] | ||
if t.nil? | ||
raise "No tag mapping found for #{tag_name}" | ||
elsif t.is_a?(Module) | ||
extend(t) | ||
send(tag_name + '_to_html') | ||
# Thus, if our tag_name is"url, and TAG_MAPPINGS points us to URLTagNode, | ||
# that module must define url_to_html. | ||
else | ||
"<#{t}>" + inner_html + "</#{t}>" | ||
end | ||
end | ||
end | ||
|
||
module SingleBreakNode | ||
def to_html | ||
'<br/>' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<% | ||
def def_tag(rule_name, tag_name) | ||
" | ||
rule #{rule_name} | ||
('[#{tag_name.downcase}]'/'[#{tag_name.upcase}]') | ||
(!'[/#{tag_name.downcase}]' !'[/#{tag_name.upcase}]' | ||
(tag <RbbCode::TagNode> / .))+ | ||
('[/#{tag_name.downcase}]' / '[/#{tag_name.upcase}]') | ||
end | ||
" | ||
end | ||
%> | ||
|
||
grammar RbbCodeGrammar | ||
rule document | ||
# Consume the trailing linebreaks, because the paragraph lookahead | ||
# doesn't consume them. | ||
contents:(blockquote <RbbCode::BlockquoteNode> / list <RbbCode::ListNode> / paragraph <RbbCode::ParagraphNode>)* break_ws* <RbbCode::DocumentNode> | ||
end | ||
|
||
rule paragraph | ||
(break_ws 2..) | ||
( | ||
!(break_ws 2..) | ||
paragraph_contents | ||
)+ | ||
end | ||
|
||
rule paragraph_contents | ||
(tag <RbbCode::TagNode> / single_break_ws / .) | ||
end | ||
|
||
rule break_ws | ||
# Allow whitespace around the linebreaks | ||
[ \t]* [\r\n] [ \t]* | ||
end | ||
|
||
rule whitespace | ||
# Any whitespace, including linebreaks | ||
[ \t\r\n] | ||
end | ||
|
||
rule single_break_ws | ||
# We don't count linebreaks when they're immediately followed by | ||
# certain keywords. This avoids printing an extra <br/> in some cases. | ||
break_ws !break_ws !(break_ws* ('[/quote]' / '[*]' / '[/list]')) <RbbCode::SingleBreakNode> | ||
end | ||
|
||
rule blockquote | ||
break_ws* | ||
'[quote]' | ||
contents:( | ||
# Possible linebreaks after opening quote tag | ||
break_ws* | ||
|
||
# First paragraph (mandatory) | ||
(blockquote_paragraph <RbbCode::ParagraphNode>) | ||
|
||
# Subsequent paragraphs (optional) | ||
( | ||
(break_ws 2..) | ||
(blockquote_paragraph <RbbCode::ParagraphNode>) | ||
)* | ||
|
||
# Possible linebreaks before closing quote tag | ||
break_ws* | ||
) | ||
'[/quote]' | ||
end | ||
|
||
rule blockquote_paragraph | ||
(!('[/quote]' / (break_ws 2..)) paragraph_contents)+ | ||
end | ||
|
||
rule list | ||
break_ws* | ||
'[list]' | ||
contents:( | ||
# Possible linebreaks after opening list tag | ||
whitespace* | ||
|
||
# At least one list item | ||
( | ||
( | ||
'[*]' | ||
contents:(!'[/list]' !'[*]' paragraph_contents)* | ||
<RbbCode::ListItemNode> | ||
) | ||
)+ | ||
|
||
# Possible linebreaks before closing list tag | ||
whitespace* | ||
) | ||
'[/list]' | ||
end | ||
|
||
rule tag | ||
# Make sure that anytime you call def_tag, you add it to this list: | ||
bold / italic / underline / simple_url / complex_url / img | ||
end | ||
|
||
<%= def_tag 'bold', 'b' %> | ||
<%= def_tag 'italic', 'i' %> | ||
<%= def_tag 'underline', 'u' %> | ||
<%= def_tag 'simple_url', 'url' %> | ||
<%= def_tag 'img', 'img' %> | ||
|
||
rule complex_url | ||
'[url=' url:[^\]]+ ']' | ||
text:(!'[/url]' .)+ | ||
'[/url]' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper.rb') | ||
|
||
class TestBlockquotes < Test::Unit::TestCase | ||
include RbbCode::HTMLAssertions | ||
|
||
def test_blockquotes | ||
0.upto(3) do |pre_breaks| | ||
0.upto(3).each do |post_breaks| | ||
bb_code = '[quote]' + ("\n" * pre_breaks) + 'Quoth the raven' + ("\n" * post_breaks) + '[/quote]' | ||
assert_converts_to( | ||
'<blockquote><p>Quoth the raven</p></blockquote>', | ||
bb_code, | ||
{}, | ||
"#{bb_code} did not convert properly" | ||
) | ||
end | ||
end | ||
|
||
0.upto(3) do |pre_breaks| | ||
0.upto(3).each do |post_breaks| | ||
bb_code = '[quote]' + ("\n" * pre_breaks) + "Quoth\n\nthe\n\nraven" + ("\n" * post_breaks) + '[/quote]' | ||
assert_converts_to( | ||
'<blockquote> | ||
<p>Quoth</p> | ||
<p>the</p> | ||
<p>raven</p> | ||
</blockquote>', | ||
bb_code, | ||
{}, | ||
"#{bb_code} did not convert properly" | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper.rb') | ||
|
||
class TestDocParsing < Test::Unit::TestCase | ||
include RbbCode::HTMLAssertions | ||
|
||
def test_parsing_whole_document | ||
assert_converts_to( | ||
# Expected HTML: | ||
' | ||
<p><strong>This <em>is</em> awesome</strong></p> | ||
<p>Another paragraph.</p> | ||
<p>A paragraph with<br/>a line break.</p> | ||
<p>Here\'s one kind of link: <a href="http://example.org">http://example.org</a></p> | ||
<p>Here\'s another: <a href="http://example.com">Example</a></p> | ||
<p>This is an image: <img src="http://example.com/foo.png" alt="Image"/></p> | ||
<blockquote> | ||
<p>This <em>is</em> a quotation.</p> | ||
<p>Yes it is.</p> | ||
</blockquote> | ||
<ul> | ||
<li>Entry 1</li> | ||
<li>Entry 2</li> | ||
<li>Entry 3</li> | ||
</ul> | ||
<p>Smile: <img src="/happy.png" alt="Emoticon"/> Frown: <img src="/sad.png" alt="Emoticon"/></p> | ||
', | ||
# BBCode: | ||
' | ||
[b]This [i]is[/i] awesome[/b] | ||
Another paragraph. | ||
A paragraph with | ||
a line break. | ||
Here\'s one kind of link: [url]http://example.org[/url] | ||
Here\'s another: [url=http://example.com]Example[/url] | ||
This is an image: [img]http://example.com/foo.png[/img] | ||
[quote] | ||
This [i]is[/i] a quotation. | ||
Yes it is. | ||
[/quote] | ||
[list] [*]Entry 1 [*]Entry 2 | ||
[*]Entry 3 | ||
[/list] | ||
Smile: :) Frown: :( | ||
', | ||
# RbbCode options: | ||
:emoticons => {':)' => '/happy.png', ':(' => '/sad.png'} | ||
) | ||
end | ||
end |
Oops, something went wrong.