Славена обнови решението на 23.11.2011 22:44 (преди около 13 години)
+module Patterns
+ def self.block
+ pattern = ''
+ pattern << code + '|'
+ pattern << unordered_list + '|'
+ pattern << ordered_list + '|'
+ pattern << blockquote + '|'
+ pattern << header
+ Regexp.new(pattern)
+ end
+
+ @private
+ def self.code
+ '(^ {4}.*\n?)+'
+ end
+
+ def self.unordered_list
+ '(^[ \t\r\f]*\* .*\n?)+'
+ end
+
+ def self.ordered_list
+ '(^[ \t\r\f]*\d+\. .*\n?)+'
+ end
+
+ def self.blockquote
+ '(^[ \t\r\f]*\> .*\n?)+'
+ end
+
+ def self.header
+ '^[ \t\r\f]*#{1,4} .*\S.*'
+ end
+end
+
+class BlocksFormatter
+ def initialize(markdown_text)
+ @markdown_text = markdown_text
+ end
+
+ def to_blocks
+ format_text(@markdown_text).strip
+ end
+
+ def format_text(text)
+ block_element = text.match(Patterns.block)
+ if block_element
+ format_paragraphs(block_element.pre_match) +
+ format_block(block_element[0]) +
+ format_text(block_element.post_match)
+ else
+ format_paragraphs(text)
+ end
+ end
+
+ def format_block(text)
+ case text
+ when /^\s* {4}/ then format_pre(text)
+ when /^\s*> / then format_quote(text)
+ when /^\s*\* / then format_unordered(text)
+ when /^\s*\d+\. / then format_ordered(text)
+ when /^\s*(\#{1,4}) / then format_header(text, $1.size)
+ end
+ end
+
+ def format_paragraphs(text)
+ text.gsub(/(^(.*)\S(.*)\n?)+/) do |paragraph|
+ paragraph_content = paragraph.lines.inject("") do |all, line|
+ all << line.strip + "\n"
+ end
+ "<p>#{paragraph_content.chomp}</p>\n"
+ end
+ end
+
+ def format_header(text, count)
+ clear_text = text.strip.gsub(/\#{1,4} \s*(.*?)\s*$/, "\\1")
+ "<h#{count.to_s}>" + clear_text + "</h#{count.to_s}>"
+ end
+
+ def format_pre(text)
+ clear_text = text.gsub(/ {4}(.*)$/, "\\1").chomp
+ "<pre><code>" + clear_text + "</code></pre>\n"
+ end
+
+ def format_quote(text)
+ clear_text = text.gsub(/> (.*)$/, "\\1")
+ "<blockquote>" + format_paragraphs(clear_text).chomp + "</blockquote>\n"
+ end
+
+ def format_unordered(text)
+ clear_text = text.gsub(/\* (.*)$/, " <li>\\1</li>").chomp
+ "<ul>\n" + clear_text + "\n</ul>\n"
+ end
+
+ def format_ordered(text)
+ clear_text = text.gsub(/\d+\. (.*)$/, " <li>\\1</li>").chomp
+ "<ol>\n" + clear_text + "\n</ol>\n"
+ end
+end
+
+class Formatter
+ def initialize(markdown_text)
+ @markdown_text = markdown_text
+ @html_text = @markdown_text
+ end
+
+ def to_html
+ format_symbols
+ format_anchors
+ format_text_styles
+ format_blocks
+ @html_text
+ end
+
+ def format_blocks
+ blocks_formatter = BlocksFormatter.new(@html_text)
+ @html_text = blocks_formatter.to_blocks
+ end
+
+ def format_symbols
+ @html_text = @html_text.gsub(/&|<|(?<!^)>|\"/) do |symbol|
+ case symbol
+ when /&/ then "&"
+ when /</ then "<"
+ when />/ then ">"
+ when /\"/ then """
+ end
+ end
+ end
+
+ def format_anchors
+ @html_text = @html_text.gsub(/\[(.*)\]\((.*)\)/,"<a href=\"\\2\">\\1</a>")
+ end
+
+ def format_text_styles
+ @html_text = @html_text.gsub(/.*(\*\*|_).*?\1.*/) do |line|
+ line.start_with?("> ", " ") ? line : format_text_style(line)
+ end
+ end
+
+ def format_text_style text
+ text.gsub(/(\*\*|_)(.*?)\1/) do |styled_text|
+ case $1
+ when "**" then "<strong>#{format_text_style($2)}</strong>"
+ when "_" then "<em>#{format_text_style($2)}</em>"
+ end
+ end
+ end
+
+ def to_s
+ to_html
+ end
+
+ def inspect
+ @markdown_text
+ end
+end