lib/ronn/document.rb in ronn-0.5 vs lib/ronn/document.rb in ronn-0.6.0
- old
+ new
@@ -1,9 +1,11 @@
require 'set'
+require 'cgi'
require 'hpricot'
require 'rdiscount'
require 'ronn/roff'
+require 'ronn/template'
module Ronn
# The Document class can be used to load and inspect a ronn document
# and to convert a ronn document into other formats, like roff or
# HTML.
@@ -39,10 +41,13 @@
# The date the document was published; center displayed in
# the document footer.
attr_accessor :date
+ # Array of style modules to apply to the document.
+ attr_accessor :styles
+
# Create a Ronn::Document given a path or with the data returned by
# calling the block. The document is loaded and preprocessed before
# the intialize method returns. The attributes hash may contain values
# for any writeable attributes defined on this class.
def initialize(path=nil, attributes={}, &block)
@@ -51,10 +56,12 @@
@reader = block || Proc.new { |f| File.read(f) }
@data = @reader.call(path)
@name, @section, @tagline = nil
@manual, @organization, @date = nil
@fragment = preprocess
+ @styles = %w[man]
+
attributes.each { |attr_name,value| send("#{attr_name}=", value) }
end
# Generate a file basename of the form "<name>.<section>.<type>"
# for the given file extension. Uses the name and section from
@@ -121,20 +128,35 @@
return @date if @date
return File.mtime(path) if File.exist?(path)
Time.now
end
+ # Retrieve a list of top-level section headings in the document and return
+ # as an array of +[id, text]+ tuples, where +id+ is the element's generated
+ # id and +text+ is the inner text of the heading element.
+ def section_heads
+ parse_html(to_html_fragment).search('h2[@id]').map do |heading|
+ [heading.attributes['id'], heading.inner_text]
+ end
+ end
+
+ # Styles to insert in the generated HTML output. This is a simple Array of
+ # string module names or file paths.
+ def styles=(styles)
+ @styles = (%w[man] + styles).uniq
+ end
+
# Convert the document to :roff, :html, or :html_fragment and
# return the result as a string.
def convert(format)
send "to_#{format}"
end
# Convert the document to roff and return the result as a string.
def to_roff
RoffFilter.new(
- to_html_fragment,
+ to_html_fragment(wrap_class=nil),
name,
section,
tagline,
manual,
organization,
@@ -142,48 +164,85 @@
).to_s
end
# Convert the document to HTML and return the result as a string.
def to_html
- layout_filter(to_html_fragment)
+ if layout = ENV['RONN_LAYOUT']
+ if !File.exist?(layout_path = File.expand_path(layout))
+ warn "warn: can't find #{layout}, using default layout."
+ layout_path = nil
+ end
+ end
+
+ template = Ronn::Template.new(self)
+ template.render(layout_path || 'default')
end
# Convert the document to HTML and return the result
# as a string. The HTML does not include <html>, <head>,
# or <style> tags.
- def to_html_fragment
+ def to_html_fragment(wrap_class='mp')
+ wrap_class = nil if wrap_class.to_s.empty?
buf = []
+ buf << "<div class='#{wrap_class}'>" if wrap_class
if name? && section?
buf << "<h2 id='NAME'>NAME</h2>"
- buf << "<p><code>#{name}</code> -- #{tagline}</p>"
+ buf << "<p><code>#{name}</code> - #{tagline}</p>"
elsif tagline
- buf << "<h1>#{[name, tagline].compact.join(' -- ')}</h1>"
+ buf << "<h1>#{[name, tagline].compact.join(' - ')}</h1>"
end
buf << @fragment.to_s
+ buf << "</div>" if wrap_class
buf.join("\n")
end
protected
+ # The preprocessed markdown source text.
+ attr_reader :markdown
+
# Parse the document and extract the name, section, and tagline
# from its contents. This is called while the object is being
# initialized.
def preprocess
[
+ :heading_anchor_pre_filter,
:angle_quote_pre_filter,
:markdown_filter,
:angle_quote_post_filter,
- :definition_list_filter
+ :definition_list_filter,
+ :heading_anchor_filter,
+ :annotate_bare_links_filter
].inject(data) { |res,filter| send(filter, res) }
end
- # Apply the standard HTML layout template.
- def layout_filter(html)
- template_file = File.dirname(__FILE__) + "/layout.html"
- template = File.read(template_file)
- eval("%Q{#{template}}", binding, template_file)
+ # Add a 'data-bare-link' attribute to hyperlinks
+ # whose text labels are the same as their href URLs.
+ def annotate_bare_links_filter(html)
+ doc = parse_html(html)
+ doc.search('a[@href]').each do |node|
+ href = node.attributes['href']
+ text = node.inner_text
+
+ if href == text ||
+ href[0] == ?# ||
+ CGI.unescapeHTML(href) == "mailto:#{CGI.unescapeHTML(text)}"
+ then
+ node.set_attribute('data-bare-link', 'true')
+ end
+ end
+ doc
end
+ # Add URL anchors to all HTML heading elements.
+ def heading_anchor_filter(html)
+ doc = parse_html(html)
+ doc.search('h1|h2|h3|h4|h5|h6').not('[@id]').each do |heading|
+ heading.set_attribute('id', heading.inner_text.gsub(/\W+/, '-'))
+ end
+ doc
+ end
+
# Convert special format unordered lists to definition lists.
def definition_list_filter(html)
doc = parse_html(html)
# process all unordered lists depth-first
doc.search('ul').to_a.reverse.each do |ul|
@@ -228,23 +287,24 @@
end
# Run markdown on the data and extract name, section, and
# tagline.
def markdown_filter(data)
+ @markdown = data
html = Markdown.new(data).to_html
@tagline, html = html.split("</h1>\n", 2)
if html.nil?
html = @tagline
@tagline = nil
else
# grab name and section from title
@tagline.sub!('<h1>', '')
- if @tagline =~ /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*--?\s*(.*)/
+ if @tagline =~ /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*-+\s*(.*)/
@name = $1
@section = $2
@tagline = $3
- elsif @tagline =~ /([\w_.\[\]~+=@:-]+)\s+--\s+(.*)/
+ elsif @tagline =~ /([\w_.\[\]~+=@:-]+)\s+-+\s+(.*)/
@name = $1
@tagline = $2
end
end
@@ -263,9 +323,23 @@
match.to_s
else
"<var>#{contents}</var>"
end
end
+ end
+
+ # Add [id]: #ANCHOR elements to the markdown source text for all sections.
+ # This lets us use the [SECTION-REF][] syntax
+ def heading_anchor_pre_filter(data)
+ first = true
+ data.split("\n").grep(/^[#]{2,5} +[\w '-]+[# ]*$/).each do |line|
+ data << "\n\n" if first
+ first = false
+ title = line.gsub(/[^\w -]/, '').strip
+ anchor = title.gsub(/\W+/, '-').gsub(/(^-+|-+$)/, '')
+ data << "[#{title}]: ##{anchor} \"#{title}\"\n"
+ end
+ data
end
HTML = %w[
a abbr acronym b bdo big br cite code dfn
em i img input kbd label q samp select