Венета обнови решението на 20.11.2011 22:43 (преди около 13 години)
+module BlockFormatter
+ def self.format_headings(text)
+ text.gsub(/^([ \t]*)(\#{1,4})\s+(\S.+)$/) do |match|
+ unless $1 == ' '
+ length = $2.length.to_s
+ match = '<h' + length + '>' + $3.strip + '</h' + length + '>'
+ end
+ match
+ end
+ end
+
+ def self.format_code(text)
+ text.gsub(/(^ {4}.*(\n|$))+/) do |match|
+ match = match.gsub(/^ {4}/, '')
+ '<pre><code>' + match.sub(/(\n?)\z/, '</code></pre>\1')
+ end
+ end
+
+ def self.format_quote(text)
+ text.gsub(/(^([ \t]*)>\s.*(\n|$))+/) do |match|
+ unless $1 == ' '
+ match = match.gsub(/^[ \t]*>\s/, '')
+ match = self.format_olists(self.format_ulists(match))
+ match = self.format_paragraphs(self.format_headings(match))
+ match = '<blockquote>' + match.sub(/(\n?)\z/, "</blockquote>\n")
+ end
+ match
+ end
+ end
+
+ def self.format_paragraphs(text)
+ text.gsub(/(\A|(<\/.+?>\n+))([^<>]+?)(\z|(\n+<.+?>))/m) do
+ open_group = $1
+ close_group = $4
+ matched_group = $3.strip.gsub(/((\A|(^$)+)\n*)(.+?)(\n*(\z|(^$)+))/m) do
+ $1 + '<p>' + $4.strip + '</p>' + $5
+ end
+ open_group + matched_group + close_group
+ end
+ end
+
+ def self.format_ulists(text)
+ text.gsub(/(^[ \t]*\*\s+\S.*(\n|$))+/) do |match|
+ match = match.gsub(/^[ \t]*\*\s+(\S.*)(\n|$)/) { " <li>" + $1.strip + "</li>\n" }
+ "<ul>\n" + match + "</ul>\n"
+ end
+ end
+
+ def self.format_olists(text)
+ text.gsub(/(^[ \t]*\d\.\s+\S.*(\n|$))+/) do |match|
+ match = match.gsub(/^[ \t]*\d\.\s+(\S.*)(\n|$)/) { " <li>" + $1.strip + "</li>\n" }
+ "<ol>\n" + match + "</ol>\n"
+ end
+ end
+end
+
+module TextFormatter
+ def self.escape_special_chars(text)
+ text = text.gsub(/&/, '&')
+ text = text.gsub(/</, '<')
+ text = text.gsub(/(?<=.)>/, '\1>')
+ text = text.gsub(/"/, '"')
+ end
+
+ def self.format_links(text)
+ text.gsub(/^(.*?)\[([^\]]+)\]\(([^\)]+)\)(.*)$/) do
+ $1 + '<a href="' + $3 + '">' + $2 + '</a>' + $4
+ end
+ end
+
+ def self.format_text(text)
+ text.gsub(/^(.*?)((\*{2}.+?\*{2})|(_.+?_))(.*)$/) do |match|
+ self.format_text_line(match, 0)
+ end
+ end
+
+ def self.format_text_line(text, position)
+ max_pos = text.length
+ em_pos = text.index('_', position) || max_pos
+ strong_pos = text.index('**', position) || max_pos
+ text = self.add_strong_emphasize(text, position, em_pos, strong_pos, max_pos)
+ next_pos = [em_pos, strong_pos].min + 1
+ if next_pos < max_pos && text =~ /^(.*?)((\*{2}.+?\*{2})|(_.+?_))(.*)$/
+ text = self.format_text_line(text, next_pos)
+ end
+
+ text
+ end
+
+ def self.add_strong_emphasize(text, pos, em_pos, strong_pos, max_pos)
+ if em_pos < max_pos && em_pos < strong_pos
+ text = self.emphasize(text, pos, max_pos)
+ elsif strong_pos < max_pos
+ text = self.add_strong(text, pos, max_pos)
+ end
+
+ text
+ end
+
+ def self.emphasize(text, pos, max_pos)
+ result_text = pos > 0 ? text[0..(pos-1)] : ''
+ result_text += text[pos..(max_pos-1)].sub(/^(.*?)_(.+?)_(.*)$/) do |match|
+ unless $2.index('</strong>')
+ match = $1 + '<em>' + $2 + '</em>' + $3
+ end
+ match
+ end
+
+ result_text
+ end
+
+ def self.add_strong(text, pos, max_pos)
+ result_text = pos > 0 ? text[0..(pos-1)] : ''
+ result_text += text[pos..(max_pos-1)].sub(/^(.*?)\*{2}(.+?)\*{2}(.*)$/) do |match|
+ unless $2.index('</em>')
+ match = $1 + '<strong>' + $2 + '</strong>' + $3
+ end
+ match
+ end
+
+ result_text
+ end
+end
+
+class Formatter
+ def initialize(raw_text)
+ @plain_text = raw_text
+ @formated_text = @plain_text
+ @formatted = false
+ end
+
+ def add_formatting(text)
+ text = BlockFormatter.format_quote(text)
+ text = BlockFormatter.format_headings(text)
+ text = BlockFormatter.format_ulists(text)
+ text = BlockFormatter.format_olists(text)
+ text = BlockFormatter.format_paragraphs(text)
+ text = TextFormatter.format_links(text)
+ text = TextFormatter.format_text(text)
+ end
+
+ def to_html
+ if (!@formatted)
+ @formated_text = TextFormatter.escape_special_chars(@formated_text)
+ @formated_text = BlockFormatter.format_code(@formated_text)
+ @formated_text = @formated_text.gsub(/(\A|(<\/pre>\n+))([^<]+?)(\z|(\n+<pre>))/m) do
+ $1 + add_formatting($3) + $4
+ end
+ @formated_text = @formated_text.rstrip
+ @formatted = true
+ end
+ @formated_text
+ end
+
+ def to_s
+ to_html
+ end
+
+ def inspect
+ @plain_text
+ end
+end