#-- # Copyright 2006 Suraj N. Kurapati # See the file named LICENSE for details. require 'cgi' require 'digest/md5' begin require 'rubygems' rescue LoadError end require 'coderay' require 'redcloth' class String # The content of these HTML tags will be preserved verbatim # when they are processed by Textile. By doing this, we # avoid unwanted Textile transformations, such as quotation # marks becoming curly ( ), in source code. PRESERVED_TAGS = %w[tt code pre] # Transforms this string into HTML. def to_html text = dup # escape preserved tags preserved = {} # escaped => original PRESERVED_TAGS.each do |tag| text.gsub! %r{(<#{tag}.*?>)(.*?)(</#{tag}>)}m do orig = $1 + CGI.escapeHTML(CGI.unescapeHTML($2)) + $3 esc = Digest::MD5.hexdigest(orig) preserved[esc] = orig esc end end # convert Textile into HTML html = text.redcloth # restore preserved tags preserved.each_pair do |esc, orig| html.gsub! %r{<p>#{esc}</p>|#{esc}}, orig end ## fix annoyances in Textile conversion # redcloth wraps indented text within <pre> tags html.gsub! %r{(<pre>)\s*<code>(.*?)\s*</code>\s*(</pre>)}m, '\1\2\3' html.gsub! %r{(<pre>)\s*<pre>(.*?)</pre>\s*(</pre>)}m, '\1\2\3' # redcloth wraps a single item within paragraph tags, which # prevents the item's HTML from being validly injected within # other block-level elements, such as headings (h1, h2, etc.) html.sub! %r{^<p>(.*)</p>$}m do |match| payload = $1 if payload =~ /<p>/ match else payload end end # redcloth adds <span> tags around acronyms html.gsub! %r{<span class="caps">([[:upper:][:digit:]]+)</span>}, '\1' html.coderay end # Returns the result of running this string through RedCloth. def redcloth RedCloth.new(self).to_html end # Adds syntax coloring to <code> elements in the given text. If the <code> # tag has an attribute lang="...", then that is considered the programming # language for which appropriate syntax coloring should be applied. # Otherwise, the programming language is assumed to be ruby. def coderay gsub %r{<(code)(.*?)>(.*?)</\1>}m do code = CGI.unescapeHTML $3 atts = $2 lang = if $2 =~ /lang=('|")(.*?)\1/i $2 else :ruby end tag = if code =~ /\n/ :pre else :code end html = CodeRay.scan(code, lang).html(:css => :style) %{<#{tag} class="code"#{atts}>#{html}</#{tag}>} end end # Transforms this string into a valid XHTML anchor (ID attribute). # See http://www.nmt.edu/tcc/help/pubs/xhtml/id-type.html def to_html_anchor # remove HTML tags from the input buf = self.gsub(/<.*?>/, '') # The first or only character must be a letter. buf.insert(0, 'a') unless buf[0,1] =~ /[[:alpha:]]/ # The remaining characters must be letters, # digits, hyphens (-), underscores (_), colons # (:), or periods (.) [or Unicode characters] buf.unpack('U*').map! do |code| if code > 0xFF or code.chr =~ /[[:alnum:]\-_:\.]/ code else ?_ end end.pack('U*') end end