Class | RDoc::Markup::ToHtml |
In: |
markup/to_html.rb
|
Parent: | RDoc::Markup::Formatter |
# File markup/to_html.rb, line 21 21: def initialize 22: super 23: 24: # external hyperlinks 25: @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK) 26: 27: # and links of the form <text>[<url>] 28: @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK) 29: 30: init_tags 31: end
# File markup/to_html.rb, line 175 175: def accept_blank_line(am, fragment) 176: # @res << annotate("<p />") << "\n" 177: end
# File markup/to_html.rb, line 179 179: def accept_heading(am, fragment) 180: @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) 181: end
# File markup/to_html.rb, line 156 156: def accept_list_end(am, fragment) 157: if tag = @in_list_entry.pop 158: @res << annotate(tag) << "\n" 159: end 160: @res << html_list_name(fragment.type, false) << "\n" 161: end
# File markup/to_html.rb, line 163 163: def accept_list_item(am, fragment) 164: if tag = @in_list_entry.last 165: @res << annotate(tag) << "\n" 166: end 167: 168: @res << list_item_start(am, fragment) 169: 170: @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" 171: 172: @in_list_entry[-1] = list_end_for(fragment.type) 173: end
# File markup/to_html.rb, line 151 151: def accept_list_start(am, fragment) 152: @res << html_list_name(fragment.type, true) << "\n" 153: @in_list_entry.push false 154: end
# File markup/to_html.rb, line 133 133: def accept_paragraph(am, fragment) 134: @res << annotate("<p>") + "\n" 135: @res << wrap(convert_flow(am.flow(fragment.txt))) 136: @res << annotate("</p>") + "\n" 137: end
# File markup/to_html.rb, line 145 145: def accept_rule(am, fragment) 146: size = fragment.param 147: size = 10 if size > 10 148: @res << "<hr size=\"#{size}\"></hr>" 149: end
# File markup/to_html.rb, line 139 139: def accept_verbatim(am, fragment) 140: @res << annotate("<pre>") + "\n" 141: @res << CGI.escapeHTML(fragment.txt) 142: @res << annotate("</pre>") << "\n" 143: end
Given an HTML tag, decorate it with class information and the like if required. This is a no-op in the base class, but is overridden in HTML output classes that implement style sheets.
# File markup/to_html.rb, line 117 117: def annotate(tag) 118: tag 119: end
# File markup/to_html.rb, line 237 237: def convert_flow(flow) 238: res = "" 239: 240: flow.each do |item| 241: case item 242: when String 243: res << convert_string(item) 244: when RDoc::Markup::AttrChanger 245: off_tags(res, item) 246: on_tags(res, item) 247: when RDoc::Markup::Special 248: res << convert_special(item) 249: else 250: raise "Unknown flow element: #{item.inspect}" 251: end 252: end 253: 254: res 255: end
# File markup/to_html.rb, line 303 303: def convert_heading(level, flow) 304: res = 305: annotate("<h#{level}>") + 306: convert_flow(flow) + 307: annotate("</h#{level}>\n") 308: end
# File markup/to_html.rb, line 290 290: def convert_special(special) 291: handled = false 292: RDoc::Markup::Attribute.each_name_of(special.type) do |name| 293: method_name = "handle_special_#{name}" 294: if self.respond_to? method_name 295: special.text = send(method_name, special) 296: handled = true 297: end 298: end 299: raise "Unhandled special: #{special}" unless handled 300: special.text 301: end
some of these patterns are taken from SmartyPants...
# File markup/to_html.rb, line 260 260: def convert_string(item) 261: CGI.escapeHTML(item). 262: 263: # convert -- to em-dash, (-- to en-dash) 264: gsub(/---?/, '—'). #gsub(/--/, '–'). 265: 266: # convert ... to elipsis (and make sure .... becomes .<elipsis>) 267: gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…'). 268: 269: # convert single closing quote 270: gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1’'). 271: gsub(%r{\'(?=\W|s\b)}, '’'). 272: 273: # convert single opening quote 274: gsub(/'/, '‘'). 275: 276: # convert double closing quote 277: gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1”'). 278: 279: # convert double opening quote 280: gsub(/'/, '“'). 281: 282: # convert copyright 283: gsub(/\(c\)/, '©'). 284: 285: # convert and registered trademark 286: gsub(/\(r\)/, '®') 287: 288: end
Generate a hyperlink for url, labeled with text. Handle the special cases for img: and link: described under handle_special_HYPEDLINK
# File markup/to_html.rb, line 37 37: def gen_url(url, text) 38: if url =~ /([A-Za-z]+):(.*)/ then 39: type = $1 40: path = $2 41: else 42: type = "http" 43: path = url 44: url = "http://#{url}" 45: end 46: 47: if type == "link" then 48: url = if path[0, 1] == '#' then # is this meaningful? 49: path 50: else 51: RDoc::Generator.gen_url @from_path, path 52: end 53: end 54: 55: if (type == "http" or type == "link") and 56: url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then 57: "<img src=\"#{url}\" />" 58: 59: elsif type == "link" 60: "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>" 61: else 62: "<a href=\"#{url}\" target=\"_top\">#{text.sub(%r{^#{type}:/*}, '')}</a>" 63: end 64: end
And we‘re invoked with a potential external hyperlink mailto: just gets inserted. http: links are checked to see if they reference an image. If so, that image gets inserted using an <img> tag. Otherwise a conventional <a href> is used. We also support a special type of hyperlink, link:, which is a reference to a local file whose path is relative to the —op directory.
# File markup/to_html.rb, line 74 74: def handle_special_HYPERLINK(special) 75: url = special.text 76: gen_url url, url 77: end
Here‘s a hypedlink where the label is different to the URL
<label>[url] or {long label}[url]
# File markup/to_html.rb, line 83 83: def handle_special_TIDYLINK(special) 84: text = special.text 85: 86: return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/ 87: 88: label = $1 89: url = $2 90: gen_url url, label 91: end
# File markup/to_html.rb, line 310 310: def html_list_name(list_type, is_open_tag) 311: tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}") 312: annotate(tags[ is_open_tag ? 0 : 1]) 313: end
Set up the standard mapping of attributes to HTML tags
# File markup/to_html.rb, line 96 96: def init_tags 97: @attr_tags = [ 98: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"), 99: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"), 100: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"), 101: ] 102: end
# File markup/to_html.rb, line 343 343: def list_end_for(fragment_type) 344: case fragment_type 345: when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then 346: "</li>" 347: when :LABELED then 348: "</dd>" 349: when :NOTE then 350: "</td></tr>" 351: else 352: raise "Invalid list type" 353: end 354: end
# File markup/to_html.rb, line 315 315: def list_item_start(am, fragment) 316: case fragment.type 317: when :BULLET, :NUMBER then 318: annotate("<li>") 319: 320: when :UPPERALPHA then 321: annotate("<li type=\"A\">") 322: 323: when :LOWERALPHA then 324: annotate("<li type=\"a\">") 325: 326: when :LABELED then 327: annotate("<dt>") + 328: convert_flow(am.flow(fragment.param)) + 329: annotate("</dt>") + 330: annotate("<dd>") 331: 332: when :NOTE then 333: annotate("<tr>") + 334: annotate("<td valign=\"top\">") + 335: convert_flow(am.flow(fragment.param)) + 336: annotate("</td>") + 337: annotate("<td>") 338: else 339: raise "Invalid list type" 340: end 341: end
# File markup/to_html.rb, line 226 226: def off_tags(res, item) 227: attr_mask = item.turn_off 228: return if attr_mask.zero? 229: 230: @attr_tags.reverse_each do |tag| 231: if attr_mask & tag.bit != 0 232: res << annotate(tag.off) 233: end 234: end 235: end
# File markup/to_html.rb, line 215 215: def on_tags(res, item) 216: attr_mask = item.turn_on 217: return if attr_mask.zero? 218: 219: @attr_tags.each do |tag| 220: if attr_mask & tag.bit != 0 221: res << annotate(tag.on) 222: end 223: end 224: end
Here‘s the client side of the visitor pattern
# File markup/to_html.rb, line 124 124: def start_accepting 125: @res = "" 126: @in_list_entry = [] 127: end
This is a higher speed (if messier) version of wrap
# File markup/to_html.rb, line 186 186: def wrap(txt, line_len = 76) 187: res = "" 188: sp = 0 189: ep = txt.length 190: while sp < ep 191: # scan back for a space 192: p = sp + line_len - 1 193: if p >= ep 194: p = ep 195: else 196: while p > sp and txt[p] != ?\s 197: p -= 1 198: end 199: if p <= sp 200: p = sp + line_len 201: while p < ep and txt[p] != ?\s 202: p += 1 203: end 204: end 205: end 206: res << txt[sp...p] << "\n" 207: sp = p 208: sp += 1 while sp < ep and txt[sp] == ?\s 209: end 210: res 211: end