desc: web page for the Internet code: | # load the String#to_xhtml and String#to_inline_xhtml methods require 'erbook/to_xhtml' class String # Transforms this UTF-8 string into XML entities. def to_xml_entities unpack('U*').map! {|c| "&##{c};"}.join end # Transforms this string into a valid URI fragment. # See http://www.nmt.edu/tcc/help/pubs/xhtml/id-type.html def to_uri_fragment # remove XML tags from the input buf = 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 32 # ASCII character code for a single space end end.pack('U*').strip.gsub(/[[:space:]-]+/, '-') end end class Hash # Transforms this hash into a string of XML attribute key="value" pairs. def to_xml_atts inject([]) {|s,(k,v)| s << %( #{k}="#{v}") }.join end end module ERBook class Template # Protects the given content from the text-to-XHTML conversion process. def verbatim content ERB::Util.html_escape content end # Returns XHTML for a hyperlink to the given # URL of the given label and mouse-hover title. def hyperlink url, label = url, title = nil %{#{label}} end # Returns an tag that embeds the given image file. # # path:: path to the image file # format:: format of the image file (e.g. PNG, JPEG, GIF, etc.) # atts:: additional attributes for the tag # def embed_image_file path, format = path[/\w+/], atts = {} data = ERBook.base_64_encode File.read(path) embed_image_data data, format, atts end # Returns an tag that embeds the given raw image data. # # data:: raw image data # format:: format of the image file (e.g. PNG, JPEG, GIF, etc.) # atts:: additional attributes for the tag # def embed_image_data data, format, atts = {} atts[:src] = ERBook.base_64_embed_image_data(data, format) "" end end class Document::Node # Returns the user-defined title for this node's content. def title @title ||= args[0] end # Returns the user-defined indentifer for this node. def id @id ||= args[1] end ## # utility methods # def type_label #:nodoc: ERBook::PHRASES[type.to_s.capitalize] end # Returns the title of this node as XHTML. def title_xhtml title.to_s.to_inline_xhtml end # Returns the content of this node as XHTML. def content_xhtml content.to_s.to_xhtml end # Returns the content of this node as XHTML inside a
. def content_xhtml_div #:nodoc: %{
#{content_xhtml}
} end # Returns a hyperlink to this node containing its title. def title_link title = nil title || title_xhtml end # Returns a hyperlink to this node # containing its LaTeX-style index number. def index_link index end # Returns a hyperlink to this node containing its occurrence number. def number_link [type_label, number].compact.join(' ') end # Returns a hyperlink to this node containing # its ocurrence number and its title. def number_and_title_link #:nodoc: "#{number_link}.  #{title_link}" end # Returns a hyperlink to this node containing # its LaTeX-style index number and its title. def index_and_title_link #:nodoc: "#{index_link}  #{title_link}" end # Returns a navigation menu relative to this node. def navigation self.class.navigation( here_frag, (list_frag if defn['toc'] || defn['lof']), (prev_node.here_frag if prev_node), (next_node.here_frag if next_node) ) end LIST_SIGN = '≡' # identical to HERE_SIGN = '◎' # bullseye PREV_SIGN = '△' # white up-pointing triangle NEXT_SIGN = '▽' # white down-pointing triangle LIST_TEXT = ERBook::PHRASES['Reverse jump to listing'] HERE_TEXT = ERBook::PHRASES['Jump to this segment'] PREV_TEXT = ERBook::PHRASES['Jump to previous segment'] NEXT_TEXT = ERBook::PHRASES['Jump to next segment'] # Calculates a local navigation menu containing links # to the given URI fragments (which can be nil). def self.navigation here_frag, list_frag, prev_frag, next_frag list_link = list_frag ? %{#{LIST_SIGN}} : LIST_SIGN here_link = here_frag ? %{#{HERE_SIGN}} : HERE_SIGN prev_link = prev_frag ? %{#{PREV_SIGN}} : PREV_SIGN next_link = next_frag ? %{#{NEXT_SIGN}} : NEXT_SIGN %{} end # Returns a hyperlink to this node. # # @param [String] label # Optional label (may contain XHTML) for the hyperlink. # # If not specified, the title and designation of # this node will be used as the label instead. # def xref_link label = nil prefix = [type_label, index || number].compact.join(' ') title_text = if type == 'reference' prefix else [prefix, (%{"#{title}"} if title)].compact.join('. ') end title_xhtml = if label label else title_text end.to_s.to_inline_xhtml %{#{title_xhtml}} end ## # URI fragments # @@frags = [] # Returns a unique URI fragment for this node. def here_frag #:nodoc: unless defined? @here_frag salt = object_id.abs frag = (id || title || salt).to_s.to_uri_fragment # make it unique while @@frags.include? frag frag << '-' << (index || number || salt).to_s end @@frags << frag @here_frag = frag end @here_frag end # Returns the URI fragment for the location in the table # of contents / list of figures that points this node. def list_frag #:nodoc: @list_frag ||= "rev:#{here_frag}" end end # Encodes the given input in base64 format. def ERBook.base_64_encode input #:nodoc: [input].pack('m') end # Returns a string denoting embedded, base64 encoded data. def ERBook.base_64_embed data, format #:nodoc: "data:#{format.to_s.downcase};base64,#{data.tr("\n", '')}" end # Returns a string denoting embedded, base64 encoded image data. # # format:: format of the image data (e.g. PNG, JPEG, GIF, etc.) # def ERBook.base_64_embed_image_data data, format #:nodoc: base_64_embed data, "image/#{format}" end ## # admonition icons # ICONS_DIR = File.join(ERBook::FORMATS_DIR, 'xhtml.icons') ICON_DEFS = YAML.load_file File.join(ICONS_DIR, 'index.yaml') Icon = Struct.new(:origin, :path, :name, :format, :data) class Icon #:nodoc: # Returns a data URI containing embedded image data. def data_uri ERBook.base_64_embed_image_data self.data, self.format end # Returns a CSS url() containing embedded image data. def data_css %{url("#{data_uri}")} end # Returns a XML entity reference (to this icon's # embedded image data) ready for insertion into XML. def entity_xml "&#{entity_name};" end # Returns the name of the XML entity whose # value contains embedded image data. def entity_name "icon_#{name}" end # Returns an tag that renders the image # data embedded as an ENTITY in the html DOCTYPE. def to_xhtml atts = {} atts[:alt] ||= name atts[:src] = entity_xml atts[:class] = :icon "" end end ICON_BY_NAME = {} ICON_DEFS.each_pair do |name, path| format = File.extname(path).sub('.', '') origin = path[/^\w+/] path = File.join(ICONS_DIR, path) # make the path absolute data = base_64_encode File.open(path, 'rb') {|f| f.read } ICON_BY_NAME[name] = Icon.new(origin, path, name, format, data) end ICONS = ICON_BY_NAME.values end nodes: # object model node: toc: false lof: false depth: false index: false number: false silent: false inline: true output: <%= @node.content_xhtml %> # Structure header: &header toc: false lof: false depth: false index: false number: false silent: true inline: true output: <%= @node.content_xhtml %> header_outside_above: &header_insert toc: false lof: false depth: false index: false number: false silent: true inline: true output:
<%= @node.content_xhtml %>
header_inside_above: *header_insert header_inside_below: *header_insert header_outside_below: *header_insert footer: *header footer_outside_above: *header_insert footer_inside_above: *header_insert footer_inside_below: *header_insert footer_outside_below: *header_insert abstract: toc: false lof: false depth: false index: false number: false silent: true output: |
<%= @node.navigation %>

<%= @node.type_label %>

<%= @node.content_xhtml_div %>
# Organization part: &latex toc: true lof: false depth: true index: true number: true silent: false output: |
<%= @node.navigation %>

<%= @node.type_label %> <%= @node.index_link %>
<%= @node.title_link %>

<%= @node.content_xhtml_div %>
chapter: *latex section: toc: true lof: false depth: true index: true number: true silent: false output: | <% depth = [6, @node.depth + 1].min %>
<%= @node.navigation %> class="title"> <%= @node.index_and_title_link %> > <%= @node.content_xhtml_div %>
paragraph: toc: true lof: false depth: false index: false number: false silent: false output: |
<%= @node.navigation %>

<%= @node.title_link %>

<%= @node.content_xhtml_div %>
# Admonitions tip: &admonition toc: false lof: true depth: true index: false number: true silent: false output: |
<%= @node.navigation %>

<%= @node.number_and_title_link %>

<%= ICON_BY_NAME[@node.type].to_xhtml %> <%= @node.content_xhtml %>
note: *admonition caution: *admonition warning: *admonition important: *admonition # Auxilary materials (formal blocks) # see http://www.sagehill.net/docbookxsl/FormalTitles.html figure: &formal toc: false lof: true depth: true index: false number: true silent: false output: |
<%= @node.navigation %>

<%= @node.number_and_title_link %>

<%= @node.content_xhtml_div %>
table: *formal example: *formal equation: *formal procedure: *formal # cross-references xref: toc: false lof: false depth: true index: false number: false silent: false inline: true output: | <%= query, label = @node.args scope = @nodes.select {|n| n.content } target = scope.find {|n| n.id == query } || # id has 1st priority scope.find {|n| n.title == query } if target target.xref_link label else raise ArgumentError, "invalid cross-reference to #{@node.title.inspect}", @node.trace end %> # Bibliography reference: toc: false lof: false depth: false index: false number: true silent: true inline: true cite: toc: false lof: false depth: false index: false number: false silent: false inline: true output: | <%= target = @nodes_by_type['reference'].find {|n| n.title == @node.title } if target '[%s]' % [ target.xref_link(target.number), *@node.args[1..-1] ].join(', ') else raise ArgumentError, "invalid citation for #{@node.title.inspect}", @node.trace end %> output: | <% # set default parameter values $title = '$title' unless defined? $title $subtitle = '$subtitle' unless defined? $subtitle $authors = {'$authors' => nil} unless defined? $authors $date = Time.now.strftime("%d %B %Y") unless defined? $date abstract = @nodes_by_type['abstract'].first references = @nodes_by_type['reference'] if references.empty? references = nil end # compute table of contents toc_builder = lambda do |n| nested = n.children.inject('') {|s,c| s << toc_builder[c] } nested = nil if nested.empty? if n.defn['toc'] entry = '%s%s' % [ (n.index.to_s + '  ' if n.index), n.list_frag, n.here_frag, n.title.to_s.to_inline_xhtml ] %{
  • #{entry}#{"
      #{nested}
    " if nested}
  • } else nested end.to_s end toc = @roots.inject('') {|s,n| s << toc_builder[n] } # compute list of figures lof_enums = {} # type => nodes @format['nodes'].each_pair do |name, info| if info['lof'] nodes = @nodes_by_type[name] lof_enums[name] = nodes unless nodes.empty? end end lof_sections = [] lof = lof_enums.sort.map do |type, nodes| nested = nodes.map do |n| %{
  • #{n.title.to_s.to_inline_xhtml}
  • } end label = ERBook::PHRASES[type.capitalize << 's'] lof_sections << label %{#{ERBook::Document::Node.navigation label, "rev:#{label}", nil, nil}

    #{label}

      #{nested}
    } end %> "<%= icon.data_uri %>"> <% end %> ]> <% if $title %> <%= $title.to_s.to_inline_xhtml %> <% end %> <% if $authors %> <% end %> <% if $date %> <% end %> <% if $feeds %> <% $feeds.each_pair do |url, fmt| %> <% end %> <% end %> <% @format['styles'].each do |style| %> <% style.each_pair do |media, code| %> <% end %> <% end %>
    <% # menu of links to link sections nav = [ (ERBook::PHRASES['Abstract'] if abstract), ERBook::PHRASES['Contents'], lof_sections, (ERBook::PHRASES['References'] if references) ].flatten.compact if nav.length > 1 %> <% end %>


    <%= node = @nodes_by_type['header_outside_above'].first and node.output %> <%= node = @nodes_by_type['header_outside_below'].first and node.output %> <%= abstract.output if abstract %>


    <% unless toc.empty? %>
    <%= ERBook::Document::Node.navigation 'Contents', 'rev:Contents', nil, nil %>

    <%= ERBook::PHRASES['Contents'] %>

    <% end %> <% unless lof.empty? %>
    <%= lof %>
    <% end %>


    <%= @content %>
    <% if references %>
    <%= ERBook::Document::Node.navigation 'References', 'rev:References', nil, nil %>

    <%= ERBook::PHRASES['References'] %>

      <% references.each do |n| %>
    1. <%= n.content_xhtml %>
    2. <% end %>
    <% end %>


    <%= node = @nodes_by_type['footer_outside_above'].first and node.output %> <%= node = @nodes_by_type['footer_outside_below'].first and node.output %>
    # definition of font families for use in the CSS styles fonts: serif: Constantia, "Book Antiqua", "URW Bookman L", serif sans-serif: Calibri, Verdana, sans-serif monospace: Monaco, Consolas, "Lucida Console", monospace styles: - all: | /* general */ body { color : #000000; background-color : #FFFFFF; line-height : 1.5em; font-family : <%= @format['fonts']['sans-serif'] %>; } /* emphasis */ blockquote { color : #333; font-style : italic; } em { font-family : <%= @format['fonts']['serif'] %>; font-size : 105%; } hr { height : 0; border : 0; border-top : thin dashed #000000; } /* source code */ tt, code, pre { font-family : <%= @format['fonts']['monospace'] %>; font-size : 100%; /* appears like "size: small" otherwise */ } /* hyperlinks */ a > img { border : none; } a img { _border : none; /* for IE6 */ } /* headings */ h1, h2, h3, h4, h5, h6, .title { font-weight : lighter; font-family : <%= @format['fonts']['serif'] %>; } #lof h1, #lof h2, #lof h3, #lof h4, #lof h5, #lof h6 { margin-top : 1.25em; } #content h1, #content h2, #content h3, #content h4, #content h5, #content h6 { margin-top : 2.5em; line-height : 1.25em; } #content h1 { font-size : 2.0em; } #content h2 { font-size : 1.8em; } #content h3 { font-size : 1.6em; } #content h4 { font-size : 1.4em; } #content h5 { font-size : 1.2em; } #content h6 { font-size : 1.0em; } /* tables */ table { border-collapse : collapse; /* no spacing between cell borders */ margin : auto; /* center horizontally */ margin-top : 1em; } th, td { padding : 1em; border : 1px solid #bbb; vertical-align : top; background-color : #FFFFFF; _background-color : #FFFFFF; /* for IE6 */ } th { background-color : #F5F5F5; } /* document structure */ #header { text-align : center; } .logo, .header_outside_above, #header, .header_outside_below { margin-bottom : 5em; } .footer_outside_above, #footer, .footer_outside_below { margin-top : 5em; } #header .header_inside_above, #footer .footer_inside_above { margin-bottom : 4em; } #header .header_inside_below, #footer .footer_inside_below { margin-top : 4em; } #Abstract { margin-bottom : 5em; } #toc li { list-style-type : none; } #toc li ul { padding-bottom : 1em; border-left : thick solid #F5F5F5; _border-left : none; /* for IE6 */ } #toc li ul:hover { border-color : #DCDCDC; } #toc > ul { padding-left : 1em; } #bib { margin-top : 5em; } #footer { border-top : thick dotted #DCDCDC; padding-top : 1em; text-align : center; } #footer-credits { margin-top : 2em; margin-bottom : 2em; } /* document nodes */ .part > .title, .chapter > .title { padding-bottom : 0.5em; } .part > .title > big, .chapter > .title > big { display : block; margin-top : 0.25em; } .part .title big, .chapter .title big { _display : block; /* for IE6 */ _margin-top : 0.25em; /* for IE6 */ _margin-bottom : 0.75em; /* for IE6 */ } .paragraph > .title, .tip > .title, .note > .title, .caution > .title, .warning > .title, .important > .title, .figure > .title, .table > .title, .example > .title, .equation > .title, .procedure > .title { font-size : large; margin-top : 2em; } .paragraph .title, .tip .title, .note .title, .caution .title, .warning .title, .important .title, .figure .title, .table .title, .example .title, .equation .title, .procedure .title { _font-size : large; /* for IE6 */ _font-weight : bold; /* large is not bold in IE6 */ _margin-top : 2em; /* for IE6 */ } .tip , .note , .caution , .warning , .important, .figure , .table , .example , .equation , .procedure { margin : 3em; } .tip .nav, .note .nav, .caution .nav, .warning .nav, .important .nav, .figure .nav, .table .nav, .example .nav, .equation .nav, .procedure .nav { margin-left : -7.65em; } .tip > .content, .note > .content, .caution > .content, .warning > .content, .important > .content, .figure > .content, .table > .content, .example > .content, .equation > .content, .procedure > .content { min-height : 48px; /* 48px is height of icon */ } .tip > .content > .icon, .note > .content > .icon, .caution > .content > .icon, .warning > .content > .icon, .important > .content > .icon { float : left; margin : 0 1em 1em 0; /* top right bottom left */ } .tip .content .icon, .note .content .icon, .caution .content .icon, .warning .content .icon, .important .content .icon { _display : none; /* IE6 cannot display embedded images */ } .figure > .title { text-align : center; } .figure .title { _text-align : center; /* for IE6 */ } .figure > .content img { display : block; margin : auto; } .figure .content img { _display : block; /* for IE6 */ _margin : auto; /* for IE6 */ } - screen: | /* general */ body { margin : 0; padding : 0; padding-left : 10em; /* room for local navigation menus */ padding-right : 10em; /* keep #body visually centered */ background-color : #808080; } /* the actual content of the element */ #body { margin : auto; max-width : 38em; background-color : #FFFFFF; border-left : 0.35em solid #696969; border-right : 0.35em solid #696969; padding : 2em; } /* source code */ tt { background-color : #F0F8FF; } pre { cursor : text; line-height : normal; border : 1px solid #b1d827; background-color : #F5FFDF; padding : 1em; /* fit container to content; no overflow! */ display : inline-block; margin : 0; min-width : 92.5%; } /* output of the syntax coloring library */ pre.code { background-color : #FFFFE8; border-color : #EEDD88; } /* hyperlinks */ a { text-decoration : none; } a:hover { text-decoration : underline; } a:link { color : #0038E1; /* maximum saturation of RoyalBlue */ } a:visited { color : #800080; } #toc a:target:after, #lof a:target:after, #nav a:target:after { content : "★"; /* ★ */ color : #FF0000; font-size : x-large; padding-left : 0.125em; } /* document structure */ /* mini navigation menu beside every block node */ .nav { width : 4em; float : left; /* detach from main content */ margin-left : -6.075em; font-size : 1.9em; letter-spacing : 0.25em; text-align : center; color : #696969; /* same colors to hide non-links */ background-color : #696969; padding : 0.5em; margin-top : -0.5em; } .nav:target, .nav:hover { color : #333; /* same colors to hide non-links */ background-color : #333; } .nav a:link, .nav a:visited { color : #C0C0C0; } .nav a:hover, #nav a:target:after { color : #9ACD32; text-decoration : none; } /* global navigation menu at the top of the page */ #nav { font-size : 1em; letter-spacing : 0; width : 8.5em; margin-left : -11.5em; } #nav li { list-style-type : none; } - print: | /* source code */ tt { font-weight : normal; } pre { border : none; } /* headings */ h1, h2, h3, h4, h5, h6 { font-weight : normal; clear : both; } /* hyperlinks */ /* blend all hyperlinks with normal text */ a:link, a:visited { color : #000000; text-decoration : none; } /* emphasize external and cross-reference hyperlinks */ a[href]:not([href^="#"]):link, a[href]:not([href^="#"]):visited, a.xref[title]:link, a.xref[title]:visited { color : #0000FF; text-decoration : underline; font-weight : bolder; } /* show URL of destination for external hyperlinks */ a[href]:not([href^="#"]):after { content : " " attr(href); font-family : <%= @format['fonts']['monospace'] %>; } /* show name of destination for cross-references */ a.xref[title]:after { content : " " attr(title); } a:after { font-weight : normal; font-size : smaller; } /* document structure */ #nav, .nav, #lof { display : none; } #toc, .part, .chapter, #bib { page-break-before : always; } /* document nodes */ .part > .title > big, .chapter > .title > big { padding-bottom : 0.5em; } .part .title big, .chapter .title big { _padding-bottom : 0.5em; /* for IE6 */ }