ron 0.3 → 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ron.gemspec +39 -51
- metadata +50 -77
- data/COPYING +0 -21
- data/README.md +0 -133
- data/Rakefile +0 -94
- data/bin/ron +0 -130
- data/lib/ron.rb +0 -16
- data/lib/ron/document.rb +0 -289
- data/lib/ron/layout.html +0 -75
- data/lib/ron/roff.rb +0 -180
- data/man/markdown.5 +0 -1614
- data/man/markdown.5.ron +0 -881
- data/man/ron.1 +0 -226
- data/man/ron.1.ron +0 -158
- data/man/ron.5 +0 -210
- data/man/ron.5.ron +0 -154
- data/man/ron.7 +0 -201
- data/man/ron.7.ron +0 -133
- data/test/angle_bracket_syntax.html +0 -12
- data/test/angle_bracket_syntax.ron +0 -12
- data/test/basic_document.html +0 -3
- data/test/basic_document.ron +0 -4
- data/test/custom_title_document.html +0 -3
- data/test/custom_title_document.ron +0 -5
- data/test/definition_list_syntax.html +0 -21
- data/test/definition_list_syntax.ron +0 -18
- data/test/document_test.rb +0 -88
- data/test/ron_test.rb +0 -59
- data/test/titleless_document.html +0 -2
- data/test/titleless_document.ron +0 -2
data/bin/ron
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
## Usage: ron [ OPTIONS ] [ FILE ]
|
3
|
-
## ron --build FILE ...
|
4
|
-
## ron --install FILE ...
|
5
|
-
## ron --man FILE ...
|
6
|
-
## Convert ron FILE to roff man page or HTML and write to standard
|
7
|
-
## output. With no FILE, ron reads from standard input. The build,
|
8
|
-
## install, and man forms accept multiple FILE arguments.
|
9
|
-
##
|
10
|
-
## Modes:
|
11
|
-
## --pipe write to standard output (default behavior)
|
12
|
-
## -b, --build write to files instead of standard output
|
13
|
-
## -i, --install write to file in MAN_HOME or system man path
|
14
|
-
## -m, --man open man page like man(1)
|
15
|
-
##
|
16
|
-
## Formats:
|
17
|
-
## -r, --roff generate roff/man text; this is the default behavior
|
18
|
-
## -5, --html generate entire HTML page with layout
|
19
|
-
## -f, --fragment generate HTML fragment instead of entire HTML page
|
20
|
-
##
|
21
|
-
## Document attributes:
|
22
|
-
## --date=DATE published date in YYYY-MM-DD format;
|
23
|
-
## displayed bottom-center in footer
|
24
|
-
## --manual=NAME name of the manual this document belongs to;
|
25
|
-
## displayed top-center in header
|
26
|
-
## --organization=NAME publishing group, organization, or individual;
|
27
|
-
## displayed bottom-left in footer
|
28
|
-
##
|
29
|
-
## --help show this help message
|
30
|
-
##
|
31
|
-
require 'date'
|
32
|
-
require 'optparse'
|
33
|
-
|
34
|
-
formats = []
|
35
|
-
options = {}
|
36
|
-
build = false
|
37
|
-
install = false
|
38
|
-
man = false
|
39
|
-
groff = "groff -Wall -mtty-char -mandoc -Tascii"
|
40
|
-
pager = ENV['MANPAGER'] || ENV['PAGER'] || 'more'
|
41
|
-
|
42
|
-
def info(message, *args)
|
43
|
-
STDERR.puts message % args
|
44
|
-
end
|
45
|
-
|
46
|
-
def usage
|
47
|
-
puts File.read(__FILE__).
|
48
|
-
grep(/^##.*/).
|
49
|
-
map { |line| line.chomp[3..-1] }.
|
50
|
-
join("\n")
|
51
|
-
end
|
52
|
-
|
53
|
-
# parse command line options
|
54
|
-
ARGV.options do |option|
|
55
|
-
# modes
|
56
|
-
option.on("--pipe") { }
|
57
|
-
option.on("-b", "--build") { build = true }
|
58
|
-
option.on("-i", "--install") { install = true }
|
59
|
-
option.on("-m", "--man") { man = true }
|
60
|
-
|
61
|
-
# format options
|
62
|
-
option.on("-r", "--roff") { formats << 'roff' }
|
63
|
-
option.on("-5", "--html") { formats << 'html' }
|
64
|
-
option.on("-f", "--fragment") { formats << 'html_fragment' }
|
65
|
-
|
66
|
-
# manual attribute options
|
67
|
-
[:name, :section, :manual, :organization, :date].each do |option_attr|
|
68
|
-
option.on("--#{option_attr}=VALUE") { |val| options[option_attr] = val }
|
69
|
-
end
|
70
|
-
|
71
|
-
option.on_tail("--help") { usage ; exit }
|
72
|
-
option.parse!
|
73
|
-
end
|
74
|
-
|
75
|
-
if ARGV.empty? && STDIN.tty?
|
76
|
-
usage
|
77
|
-
exit
|
78
|
-
elsif ARGV.empty?
|
79
|
-
ARGV.push '-'
|
80
|
-
end
|
81
|
-
|
82
|
-
# turn the --date arg into a real date object
|
83
|
-
options[:date] &&= Date.strptime(options[:date], '%Y-%m-%d')
|
84
|
-
|
85
|
-
formats = ['roff'] if formats.empty?
|
86
|
-
formats.delete('html') if formats.include?('html_fragment')
|
87
|
-
pid = nil
|
88
|
-
|
89
|
-
begin
|
90
|
-
require 'ron'
|
91
|
-
rescue LoadError
|
92
|
-
$:.unshift File.dirname(__FILE__) + "../lib"
|
93
|
-
require 'ron'
|
94
|
-
end
|
95
|
-
|
96
|
-
wr = STDOUT
|
97
|
-
ARGV.each do |file|
|
98
|
-
doc = Ron.new(file, options) { file == '-' ? STDIN.read : File.read(file) }
|
99
|
-
|
100
|
-
# setup the man pipeline if the --man option was specified
|
101
|
-
if man && !build
|
102
|
-
rd, wr = IO.pipe
|
103
|
-
if pid = fork
|
104
|
-
rd.close
|
105
|
-
else
|
106
|
-
wr.close
|
107
|
-
STDIN.reopen rd
|
108
|
-
exec "#{groff} | #{pager}"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# write output for each format
|
113
|
-
formats.each do |format|
|
114
|
-
if build
|
115
|
-
path = doc.path_for(format)
|
116
|
-
info "building: #{path}" if build
|
117
|
-
output = doc.convert(format)
|
118
|
-
File.open(path, 'wb') { |f| f.puts(output) }
|
119
|
-
system "man #{path}" if man && format == 'roff'
|
120
|
-
else
|
121
|
-
output = doc.convert(format)
|
122
|
-
wr.puts(output)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
if pid
|
127
|
-
wr.close
|
128
|
-
Process.wait
|
129
|
-
end
|
130
|
-
end
|
data/lib/ron.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# Ron is a humane text format and toolchain for authoring manpages (and
|
2
|
-
# things that appear as manpages from a distance). Use it to build /
|
3
|
-
# install standard UNIX roff(7) formatted manpages or to generate
|
4
|
-
# beautiful HTML manpages.
|
5
|
-
module Ron
|
6
|
-
VERSION = '0.3'
|
7
|
-
|
8
|
-
require 'ron/document'
|
9
|
-
require 'ron/roff'
|
10
|
-
|
11
|
-
# Create a new Ron::Document for the given ron file. See
|
12
|
-
# Ron::Document.new for usage information.
|
13
|
-
def self.new(filename, attributes={}, &block)
|
14
|
-
Document.new(filename, attributes, &block)
|
15
|
-
end
|
16
|
-
end
|
data/lib/ron/document.rb
DELETED
@@ -1,289 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'nokogiri'
|
3
|
-
require 'rdiscount'
|
4
|
-
require 'ron/roff'
|
5
|
-
|
6
|
-
module Ron
|
7
|
-
# The Document class can be used to load and inspect a ron document
|
8
|
-
# and to convert a ron document into other formats, like roff or
|
9
|
-
# HTML.
|
10
|
-
#
|
11
|
-
# Ron files may optionally follow the naming convention:
|
12
|
-
# "<name>.<section>.ron". The <name> and <section> are used in
|
13
|
-
# generated documentation unless overridden by the information
|
14
|
-
# extracted from the document's name section.
|
15
|
-
class Document
|
16
|
-
attr_reader :path, :data
|
17
|
-
|
18
|
-
# The man pages name: usually a single word name of
|
19
|
-
# a program or filename; displayed along with the section in
|
20
|
-
# the left and right portions of the header as well as the bottom
|
21
|
-
# right section of the footer.
|
22
|
-
attr_accessor :name
|
23
|
-
|
24
|
-
# The man page's section: a string whose first character
|
25
|
-
# is numeric; displayed in parenthesis along with the name.
|
26
|
-
attr_accessor :section
|
27
|
-
|
28
|
-
# Single sentence description of the thing being described
|
29
|
-
# by this man page; displayed in the NAME section.
|
30
|
-
attr_accessor :tagline
|
31
|
-
|
32
|
-
# The manual this document belongs to; center displayed in
|
33
|
-
# the header.
|
34
|
-
attr_accessor :manual
|
35
|
-
|
36
|
-
# The name of the group, organization, or individual responsible
|
37
|
-
# for this document; displayed in the left portion of the footer.
|
38
|
-
attr_accessor :organization
|
39
|
-
|
40
|
-
# The date the document was published; center displayed in
|
41
|
-
# the document footer.
|
42
|
-
attr_accessor :date
|
43
|
-
|
44
|
-
# Create a Ron::Document given a path or with the data returned by
|
45
|
-
# calling the block. The document is loaded and preprocessed before
|
46
|
-
# the intialize method returns. The attributes hash may contain values
|
47
|
-
# for any writeable attributes defined on this class.
|
48
|
-
def initialize(path=nil, attributes={}, &block)
|
49
|
-
@path = path
|
50
|
-
@basename = path.to_s =~ /^-?$/ ? nil : File.basename(path)
|
51
|
-
@reader = block || Proc.new { |f| File.read(f) }
|
52
|
-
@data = @reader.call(path)
|
53
|
-
@name, @section, @tagline = nil
|
54
|
-
@manual, @organization, @date = nil
|
55
|
-
@fragment = preprocess
|
56
|
-
attributes.each { |attr_name,value| send("#{attr_name}=", value) }
|
57
|
-
end
|
58
|
-
|
59
|
-
# Generate a file basename of the form "<name>.<section>.<type>"
|
60
|
-
# for the given file extension. Uses the name and section from
|
61
|
-
# the source file path but falls back on the name and section
|
62
|
-
# defined in the document.
|
63
|
-
def basename(type=nil)
|
64
|
-
type = nil if ['', 'roff'].include?(type.to_s)
|
65
|
-
[path_name || @name, path_section || @section, type].
|
66
|
-
compact.join('.')
|
67
|
-
end
|
68
|
-
|
69
|
-
# Construct a path for a file near the source file. Uses the
|
70
|
-
# Document#basename method to generate the basename part and
|
71
|
-
# appends it to the dirname of the source document.
|
72
|
-
def path_for(type=nil)
|
73
|
-
if @basename
|
74
|
-
File.join(File.dirname(path), basename(type))
|
75
|
-
else
|
76
|
-
basename(type)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns the <name> part of the path, or nil when no path is
|
81
|
-
# available. This is used as the manual page name when the
|
82
|
-
# file contents do not include a name section.
|
83
|
-
def path_name
|
84
|
-
@basename[/^[^.]+/] if @basename
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns the <section> part of the path, or nil when
|
88
|
-
# no path is available.
|
89
|
-
def path_section
|
90
|
-
$1 if @basename.to_s =~ /\.(\d\w*)\./
|
91
|
-
end
|
92
|
-
|
93
|
-
# Returns the manual page name based first on the document's
|
94
|
-
# contents and then on the path name.
|
95
|
-
def name
|
96
|
-
@name || path_name
|
97
|
-
end
|
98
|
-
|
99
|
-
# Truthful when the name was extracted from the name section
|
100
|
-
# of the document.
|
101
|
-
def name?
|
102
|
-
!name.nil?
|
103
|
-
end
|
104
|
-
|
105
|
-
# Returns the manual page section based first on the document's
|
106
|
-
# contents and then on the path name.
|
107
|
-
def section
|
108
|
-
@section || path_section
|
109
|
-
end
|
110
|
-
|
111
|
-
# True when the section number was extracted from the name
|
112
|
-
# section of the document.
|
113
|
-
def section?
|
114
|
-
!section.nil?
|
115
|
-
end
|
116
|
-
|
117
|
-
# The date the man page was published. If not set explicitly,
|
118
|
-
# this is the file's modified time or, if no file is given,
|
119
|
-
# the current time.
|
120
|
-
def date
|
121
|
-
return @date if @date
|
122
|
-
return File.mtime(path) if File.exist?(path)
|
123
|
-
Time.now
|
124
|
-
end
|
125
|
-
|
126
|
-
# Convert the document to :roff, :html, or :html_fragment and
|
127
|
-
# return the result as a string.
|
128
|
-
def convert(format)
|
129
|
-
send "to_#{format}"
|
130
|
-
end
|
131
|
-
|
132
|
-
# Convert the document to roff and return the result as a string.
|
133
|
-
def to_roff
|
134
|
-
RoffFilter.new(
|
135
|
-
to_html_fragment,
|
136
|
-
name,
|
137
|
-
section,
|
138
|
-
tagline,
|
139
|
-
manual,
|
140
|
-
organization,
|
141
|
-
date
|
142
|
-
).to_s
|
143
|
-
end
|
144
|
-
|
145
|
-
# Convert the document to HTML and return the result as a string.
|
146
|
-
def to_html
|
147
|
-
layout_filter(to_html_fragment)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Convert the document to HTML and return the result
|
151
|
-
# as a string. The HTML does not include <html>, <head>,
|
152
|
-
# or <style> tags.
|
153
|
-
def to_html_fragment
|
154
|
-
buf = []
|
155
|
-
if name? && section?
|
156
|
-
buf << "<h2 id='NAME'>NAME</h2>"
|
157
|
-
buf << "<p><code>#{name}</code> -- #{tagline}</p>"
|
158
|
-
elsif tagline
|
159
|
-
buf << "<h1>#{[name, tagline].compact.join(' -- ')}</h1>"
|
160
|
-
end
|
161
|
-
buf << @fragment.to_s
|
162
|
-
buf.join("\n")
|
163
|
-
end
|
164
|
-
|
165
|
-
protected
|
166
|
-
# Parse the document and extract the name, section, and tagline
|
167
|
-
# from its contents. This is called while the object is being
|
168
|
-
# initialized.
|
169
|
-
def preprocess
|
170
|
-
[
|
171
|
-
:angle_quote_pre_filter,
|
172
|
-
:markdown_filter,
|
173
|
-
:angle_quote_post_filter,
|
174
|
-
:definition_list_filter
|
175
|
-
].inject(data) { |res,filter| send(filter, res) }
|
176
|
-
end
|
177
|
-
|
178
|
-
# Apply the standard HTML layout template.
|
179
|
-
def layout_filter(html)
|
180
|
-
template_file = File.dirname(__FILE__) + "/layout.html"
|
181
|
-
template = File.read(template_file)
|
182
|
-
eval("%Q{#{template}}", binding, template_file)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Convert special format unordered lists to definition lists.
|
186
|
-
def definition_list_filter(html)
|
187
|
-
doc = parse_html(html)
|
188
|
-
# process all unordered lists depth-first
|
189
|
-
doc.search('ul').to_a.reverse.each do |ul|
|
190
|
-
items = ul.search('li')
|
191
|
-
next if items.any? { |item| item.text.split("\n", 2).first !~ /:$/ }
|
192
|
-
|
193
|
-
ul.name = 'dl'
|
194
|
-
items.each do |item|
|
195
|
-
if item.child.name == 'p'
|
196
|
-
wrap = '<p></p>'
|
197
|
-
container = item.child
|
198
|
-
else
|
199
|
-
wrap = '<dd></dd>'
|
200
|
-
container = item
|
201
|
-
end
|
202
|
-
term, definition = container.inner_html.split(":\n", 2)
|
203
|
-
|
204
|
-
dt = item.before("<dt>#{term}</dt>").previous_sibling
|
205
|
-
dt['class'] = 'flush' if dt.content.length <= 7
|
206
|
-
|
207
|
-
item.name = 'dd'
|
208
|
-
container.swap(wrap.sub(/></, ">#{definition}<"))
|
209
|
-
end
|
210
|
-
end
|
211
|
-
doc
|
212
|
-
end
|
213
|
-
|
214
|
-
# Perform angle quote (<THESE>) post filtering.
|
215
|
-
def angle_quote_post_filter(html)
|
216
|
-
doc = parse_html(html)
|
217
|
-
# convert all angle quote vars nested in code blocks
|
218
|
-
# back to the original text
|
219
|
-
doc.search('code text()').each do |node|
|
220
|
-
next unless node.to_s.include?('var>')
|
221
|
-
new = node.document.create_text_node(
|
222
|
-
node.to_s.
|
223
|
-
gsub('<var>', '<').
|
224
|
-
gsub("</var>", '>')
|
225
|
-
)
|
226
|
-
node.replace(new)
|
227
|
-
end
|
228
|
-
doc
|
229
|
-
end
|
230
|
-
|
231
|
-
# Run markdown on the data and extract name, section, and
|
232
|
-
# tagline.
|
233
|
-
def markdown_filter(data)
|
234
|
-
html = Markdown.new(data).to_html
|
235
|
-
@tagline, html = html.split("</h1>\n", 2)
|
236
|
-
if html.nil?
|
237
|
-
html = @tagline
|
238
|
-
@tagline = nil
|
239
|
-
else
|
240
|
-
# grab name and section from title
|
241
|
-
@tagline.sub!('<h1>', '')
|
242
|
-
if @tagline =~ /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*--?\s*(.*)/
|
243
|
-
@name = $1
|
244
|
-
@section = $2
|
245
|
-
@tagline = $3
|
246
|
-
elsif @tagline =~ /([\w_.\[\]~+=@:-]+)\s+--\s+(.*)/
|
247
|
-
@name = $1
|
248
|
-
@tagline = $2
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
html.to_s
|
253
|
-
end
|
254
|
-
|
255
|
-
# Convert all <WORD> to <var>WORD</var> but only if WORD
|
256
|
-
# isn't an HTML tag.
|
257
|
-
def angle_quote_pre_filter(data)
|
258
|
-
data.gsub(/\<([^:.\/]+?)\>/) do |match|
|
259
|
-
contents = $1
|
260
|
-
tag, attrs = contents.split(' ', 2)
|
261
|
-
if attrs =~ /\/=/ ||
|
262
|
-
HTML.include?(tag.sub(/^\//, '')) ||
|
263
|
-
data.include?("</#{tag}>")
|
264
|
-
match.to_s
|
265
|
-
else
|
266
|
-
"<var>#{contents}</var>"
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
HTML = %w[
|
272
|
-
a abbr acronym b bdo big br cite code dfn
|
273
|
-
em i img input kbd label q samp select
|
274
|
-
small span strong sub sup textarea tt var
|
275
|
-
address blockquote div dl fieldset form
|
276
|
-
h1 h2 h3 h4 h5 h6 hr noscript ol p pre
|
277
|
-
table ul
|
278
|
-
].to_set
|
279
|
-
|
280
|
-
private
|
281
|
-
def parse_html(html)
|
282
|
-
if html.kind_of?(Nokogiri::HTML::DocumentFragment)
|
283
|
-
html
|
284
|
-
else
|
285
|
-
Nokogiri::HTML.fragment(html.to_s)
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
data/lib/ron/layout.html
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
5
|
-
<meta name='generator' value='Ron/v#{Ron::VERSION}'>
|
6
|
-
<title>#{name}(#{section})#{tagline ? " -- " + tagline : ''}</title>
|
7
|
-
<style type='text/css'>
|
8
|
-
body {margin:0}
|
9
|
-
#man, #man code, #man pre, #man tt, #man kbd, #man samp {
|
10
|
-
font-family:consolas,monospace;
|
11
|
-
font-size:16px;
|
12
|
-
line-height:1.3;
|
13
|
-
color:#343331;
|
14
|
-
background:#fff; }
|
15
|
-
#man { max-width:89ex; text-align:justify; margin:0 25px 25px 25px }
|
16
|
-
#man h1, #man h2, #man h3 { color:#232221;clear:left }
|
17
|
-
#man h1 { font-size:28px; margin:15px 0 30px 0; text-align:center }
|
18
|
-
#man h2 { font-size:18px; margin-bottom:0; margin-top:10px; line-height:1.3; }
|
19
|
-
#man h3 { font-size:16px; margin:0 0 0 4ex; }
|
20
|
-
#man p, #man ul, #man ol, #man dl, #man pre { margin:0 0 18px 0; }
|
21
|
-
#man pre {
|
22
|
-
color:#333231;
|
23
|
-
background:#edeceb;
|
24
|
-
padding:5px 7px;
|
25
|
-
margin:0px 0 20px 0;
|
26
|
-
border-left:2ex solid #ddd}
|
27
|
-
#man pre + h2, #man pre + h3 {
|
28
|
-
margin-top:22px;
|
29
|
-
}
|
30
|
-
#man h2 + pre, #man h3 + pre {
|
31
|
-
margin-top:5px;
|
32
|
-
}
|
33
|
-
#man > p, #man > ul, #man > ol, #man > dl, #man > pre { margin-left:8ex; }
|
34
|
-
#man dt { margin:0; clear:left }
|
35
|
-
#man dt.flush { float:left; width:8ex }
|
36
|
-
#man dd { margin:0 0 0 9ex }
|
37
|
-
#man code, #man strong, #man b { font-weight:bold; color:#131211; }
|
38
|
-
#man pre code { font-weight:normal; color:#232221; background:inherit }
|
39
|
-
#man em, var, u {
|
40
|
-
font-style:normal; color:#333231; border-bottom:1px solid #999; }
|
41
|
-
#man h1.man-title { display:none; }
|
42
|
-
#man ol.man, #man ol.man li { margin:2px 0 10px 0; padding:0;
|
43
|
-
float:left; width:33%; list-style-type:none;
|
44
|
-
text-transform:uppercase; font-size:18px; color:#999;
|
45
|
-
letter-spacing:1px;}
|
46
|
-
#man ol.man { width:100%; }
|
47
|
-
#man ol.man li.tl { text-align:left }
|
48
|
-
#man ol.man li.tc { text-align:center;letter-spacing:4px }
|
49
|
-
#man ol.man li.tr { text-align:right }
|
50
|
-
#man ol.man a { color:#999 }
|
51
|
-
#man ol.man a:hover { color:#333231 }
|
52
|
-
</style>
|
53
|
-
</head>
|
54
|
-
<body>
|
55
|
-
<div id='man'>
|
56
|
-
|
57
|
-
<h1 class='man-title'>#{name}(#{section})</h1>
|
58
|
-
|
59
|
-
<ol class='head man'>
|
60
|
-
<li class='tl'>#{name if section}#{"("+section+")" if name and section}</li>
|
61
|
-
<li class='tc'>#{manual}</li>
|
62
|
-
<li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
|
63
|
-
</ol>
|
64
|
-
|
65
|
-
#{html}
|
66
|
-
|
67
|
-
<ol class='foot man'>
|
68
|
-
<li class='tl'>#{organization}</li>
|
69
|
-
<li class='tc'>#{date.strftime('%B %Y')}</li>
|
70
|
-
<li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
|
71
|
-
</ol>
|
72
|
-
|
73
|
-
</div>
|
74
|
-
</body>
|
75
|
-
</html>
|