/lib/red_cloth_formatters_plain.rb

https://github.com/codenauts/redcloth-formatters-plain · Ruby · 239 lines · 193 code · 23 blank · 23 comment · 2 complexity · d3c527cb81f879c4b36e4babbfddc689 MD5 · raw file

  1. # encoding: UTF-8
  2. require "red_cloth_formatters_plain/version"
  3. require "cgi"
  4. require "RedCloth"
  5. module RedCloth
  6. module Formatters
  7. module Plain
  8. class Sanitizer
  9. def self.strip_tags(text)
  10. # use Rails Sanitizer if available
  11. begin
  12. text = ActionController::Base.helpers.strip_tags(text)
  13. rescue
  14. # otherwise, use custom method inspired from:
  15. # RedCloth::Formatters::HTML.clean_html
  16. # not very secure, but it's ok as output is still
  17. # meant to be escaped if it needs to be shown
  18. text.gsub!( /<!\[CDATA\[/, '' )
  19. text.gsub!( /<(\/*)([A-Za-z]\w*)([^>]*?)(\s?\/?)>/ ){|m| block_given? ? yield(m) : ""}
  20. end
  21. CGI.unescapeHTML(text)
  22. end
  23. end
  24. include RedCloth::Formatters::Base
  25. [:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
  26. define_method(m) do |opts|
  27. "#{opts[:text]}\n"
  28. end
  29. end
  30. [:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
  31. define_method(m) do |opts|
  32. opts[:block] = true
  33. "#{opts[:text]}"
  34. end
  35. end
  36. def hr(opts)
  37. "\n"
  38. end
  39. def acronym(opts)
  40. opts[:block] = true
  41. opts[:title] ? "#{opts[:text]}(#{opts[:title]})" : "#{opts[:text]}"
  42. end
  43. def caps(opts)
  44. "#{opts[:text]}"
  45. end
  46. # don't render deleted text
  47. def del(opts)
  48. ""
  49. end
  50. # simple list with dashes
  51. [:ol, :ul].each do |m|
  52. define_method("#{m}_open") do |opts|
  53. opts[:block] = true
  54. opts[:nest] > 1 ? "\n" : ""
  55. end
  56. define_method("#{m}_close") do |opts|
  57. ""
  58. end
  59. end
  60. def li_open(opts)
  61. @li_need_closing = true
  62. num = opts[:nest] - 1
  63. "#{" " * (num > 0 ? num : 0)}- #{opts[:text]}"
  64. end
  65. def li_close(opts=nil)
  66. # avoid multiple line breaks when closing multiple list items
  67. output = @li_need_closing ? "\n" : ""
  68. @li_need_closing = false
  69. output
  70. end
  71. def dl_open(opts)
  72. opts[:block] = true
  73. ""
  74. end
  75. def dl_close(opts=nil)
  76. ""
  77. end
  78. def dt(opts)
  79. "#{opts[:text]}:\n"
  80. end
  81. def dd(opts)
  82. " #{opts[:text]}\n"
  83. end
  84. # don't render tables
  85. [:td, :tr_open, :tr_close, :table_open, :table_close].each do |m|
  86. define_method(m) do |opts|
  87. ""
  88. end
  89. end
  90. # just add newlines before blockquotes
  91. [:bc_open, :bq_open].each do |m|
  92. define_method(m) do |opts|
  93. opts[:block] = true
  94. ""
  95. end
  96. end
  97. def bc_close(opts)
  98. "\n"
  99. end
  100. def bq_close(opts)
  101. ""
  102. end
  103. # render link name followed by <url>
  104. # uses !LINK_OPEN_TAG! and !LINK_CLOSE_TAG! as a way to identify
  105. # the < > otherwise they will be stripped when all html tags are stripped
  106. #
  107. def link(opts)
  108. "#{opts[:name]} !LINK_OPEN_TAG!#{opts[:href]}!LINK_CLOSE_TAG!"
  109. end
  110. # render image alternative text or title if not available
  111. def image(opts)
  112. "#{opts[:alt] || opts[:title]}"
  113. end
  114. # don't render footnotes
  115. [:footno, :fn].each do |m|
  116. define_method(m) do |opts|
  117. ""
  118. end
  119. end
  120. def snip(opts)
  121. opts[:text] + "\n"
  122. end
  123. # render unescaped quotes and special chars
  124. def quote1(opts)
  125. "'#{opts[:text]}'"
  126. end
  127. def quote2(opts)
  128. "\"#{opts[:text]}\""
  129. end
  130. def multi_paragraph_quote(opts)
  131. "\"#{opts[:text]}"
  132. end
  133. def ellipsis(opts)
  134. "#{opts[:text]}..."
  135. end
  136. {
  137. :emdash => "-",
  138. :endash => " - ",
  139. :arrow => "->",
  140. :trademark => "™",
  141. :registered => "®",
  142. :copyright => "©",
  143. :amp => "&",
  144. :gt => ">",
  145. :lt => "<",
  146. :br => "\n",
  147. :quot => "\"",
  148. :squot => "'",
  149. :apos => "'",
  150. }.each do |method, output|
  151. define_method(method) do |opts|
  152. output
  153. end
  154. end
  155. def dim(opts)
  156. opts[:text]
  157. end
  158. def entity(opts)
  159. unescape("&#{opts[:text]};")
  160. end
  161. # strip HTML tags
  162. def html(opts)
  163. strip_tags(opts[:text]) + "\n"
  164. end
  165. def html_block(opts)
  166. strip_tags(opts[:text])
  167. end
  168. def notextile(opts)
  169. if filter_html
  170. "#{opts[:text]}"
  171. else
  172. strip_tags(opts[:text])
  173. end
  174. end
  175. def inline_html(opts)
  176. if filter_html
  177. "#{opts[:text]}"
  178. else
  179. strip_tags(opts[:text])
  180. end
  181. end
  182. # unchanged
  183. def ignored_line(opts)
  184. opts[:text] + "\n"
  185. end
  186. private
  187. def strip_tags(text)
  188. Sanitizer.strip_tags(text)
  189. end
  190. def unescape(str)
  191. CGI.unescapeHTML(str.to_s)
  192. end
  193. # no escaping
  194. [:escape, :escape_pre, :escape_attribute].each do |m|
  195. define_method(m) do |text|
  196. text
  197. end
  198. end
  199. def after_transform(text)
  200. text.chomp!
  201. end
  202. def before_transform(text)
  203. end
  204. end
  205. end
  206. class TextileDoc
  207. def to_plain(*rules)
  208. apply_rules(rules)
  209. output = to(Formatters::Plain)
  210. output = Formatters::Plain::Sanitizer.strip_tags(output)
  211. # replace special link hooks with < and >
  212. # See #RedCloth::Formatters::Plain#link above
  213. output.gsub("!LINK_OPEN_TAG!", "<").gsub("!LINK_CLOSE_TAG!", ">")
  214. end
  215. end
  216. end