persie 0.0.1.alpha1 → 0.0.1.alpha.2
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.
- checksums.yaml +4 -4
- data/lib/persie/asciidoctor_ext/htmlbook.rb +82 -79
- data/lib/persie/asciidoctor_ext/spine_item_processor.rb +4 -1
- data/lib/persie/book.rb +9 -3
- data/lib/persie/builder.rb +14 -4
- data/lib/persie/builders/epub.rb +15 -289
- data/lib/persie/builders/mobi.rb +13 -11
- data/lib/persie/builders/multiple_htmls.rb +87 -0
- data/lib/persie/builders/pdf.rb +15 -14
- data/lib/persie/builders/{site.rb → single_html.rb} +21 -38
- data/lib/persie/chunkable.rb +255 -0
- data/lib/persie/cli.rb +10 -6
- data/lib/persie/generator.rb +8 -8
- data/lib/persie/gepub_ext.rb +88 -0
- data/lib/persie/server.rb +2 -2
- data/lib/persie/ui.rb +13 -13
- data/lib/persie/version.rb +1 -1
- data/spec/build_epub_cmd_spec.rb +70 -0
- data/spec/{build_pdf_command_spec.rb → build_pdf_cmd_spec.rb} +7 -3
- data/spec/new_cmd_spec.rb +78 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/{version_command_spec.rb → version_cmd_spec.rb} +0 -0
- data/templates/{Gemfile.txt → Gemfile.erb} +1 -1
- data/templates/stylesheets/html.css +1 -0
- metadata +15 -12
- data/spec/new_command_spec.rb +0 -57
- data/spec/pdf_builder_spec.rb +0 -39
- data/templates/stylesheets/site.css +0 -1
data/lib/persie/builders/mobi.rb
CHANGED
@@ -9,20 +9,22 @@ module Persie
|
|
9
9
|
|
10
10
|
# Builds mobi.
|
11
11
|
def build
|
12
|
-
|
12
|
+
info '=== Build mobi ' << '=' * 57
|
13
13
|
|
14
|
+
self.before_build
|
14
15
|
self.check_dependency
|
15
16
|
check_sample
|
16
17
|
self.check_epub
|
17
18
|
self.generate_mobi
|
19
|
+
self.after_build
|
18
20
|
|
19
|
-
|
21
|
+
info END_LINE
|
20
22
|
end
|
21
23
|
|
22
24
|
def check_dependency
|
23
25
|
unless Dependency.kindlegen_installed?
|
24
|
-
|
25
|
-
|
26
|
+
error 'kindlegen not installed, termineted!'
|
27
|
+
info END_LINE
|
26
28
|
exit 41
|
27
29
|
end
|
28
30
|
end
|
@@ -31,8 +33,8 @@ module Persie
|
|
31
33
|
def check_epub
|
32
34
|
unless File.exist? self.epub_path
|
33
35
|
sample = sample? ? 'sample ' : nil
|
34
|
-
|
35
|
-
|
36
|
+
error "Please generate #{sample}ePub first"
|
37
|
+
info END_LINE
|
36
38
|
exit 42
|
37
39
|
end
|
38
40
|
end
|
@@ -40,7 +42,7 @@ module Persie
|
|
40
42
|
# Generates mobi file.
|
41
43
|
def generate_mobi
|
42
44
|
FileUtils.chdir File.dirname(self.epub_path) do
|
43
|
-
|
45
|
+
info 'Converting to mobi...'
|
44
46
|
|
45
47
|
system "kindlegen -c2 #{self.epub_path(true)}"
|
46
48
|
|
@@ -49,11 +51,11 @@ module Persie
|
|
49
51
|
prepare_directory(self.mobi_path)
|
50
52
|
FileUtils.mv(mobi_file, self.mobi_path)
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
+
confirm ' mobi file created'
|
55
|
+
info " Location: #{self.mobi_path(true)}"
|
54
56
|
else
|
55
|
-
|
56
|
-
|
57
|
+
error ' Can not create mobi'
|
58
|
+
info END_LINE
|
57
59
|
exit 43
|
58
60
|
end
|
59
61
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative '../builder'
|
4
|
+
require_relative '../chunkable'
|
5
|
+
|
6
|
+
module Persie
|
7
|
+
class MultipleHTMLs < Builder
|
8
|
+
|
9
|
+
include Chunkable
|
10
|
+
|
11
|
+
def initialize(book, options = {})
|
12
|
+
super
|
13
|
+
@spine_items = []
|
14
|
+
@spine_item_titles = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Builds multiple HTML files.
|
18
|
+
def build
|
19
|
+
info '=== Build Mutiple HTML ' << '=' * 49
|
20
|
+
|
21
|
+
self.before_build
|
22
|
+
self.check_sample
|
23
|
+
self.convert_to_single_html
|
24
|
+
self.generate_spine_items
|
25
|
+
self.chunk
|
26
|
+
self.copy_images
|
27
|
+
self.after_build
|
28
|
+
|
29
|
+
info 'Location: builds/html/multiple/'
|
30
|
+
info END_LINE
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def copy_images
|
36
|
+
images_dir = File.join @book.builds_dir, 'html', 'multiple', 'images'
|
37
|
+
|
38
|
+
# remove previous images first
|
39
|
+
# QUSTION: is this necessary?
|
40
|
+
FileUtils.rm_r(images_dir) if File.directory?(images_dir)
|
41
|
+
|
42
|
+
info 'Copy images...'
|
43
|
+
FileUtils.cp_r "#{@book.images_dir}/.", images_dir
|
44
|
+
confirm ' Done'
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def adoc_custom_attributes
|
50
|
+
{
|
51
|
+
'imagesdir' => 'images',
|
52
|
+
'ebook-format' => 'html',
|
53
|
+
'multiple-pages' => true,
|
54
|
+
'outfilesuffix' => '.html'
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Assembles payloads for Liquid to render.
|
59
|
+
def assemble_payloads
|
60
|
+
attrs = @document.attributes
|
61
|
+
custom = {
|
62
|
+
'title' => attrs['doctitle'],
|
63
|
+
'generator' => "persie #{::Persie::VERSION}"
|
64
|
+
}
|
65
|
+
|
66
|
+
attrs.merge(custom)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Renders ERb layouts of `single' or `multiple'.
|
70
|
+
def render_layout_of(format, payloads)
|
71
|
+
unless ['single', 'multipe'].include? format
|
72
|
+
error "ONLY can render layout for `single' or `multiple'"
|
73
|
+
info END_LINE
|
74
|
+
exit 53
|
75
|
+
end
|
76
|
+
|
77
|
+
# Site templates stored in `themes/site/' folder
|
78
|
+
path = File.join @book.themes_dir, 'site', "#{format}.html.liquid"
|
79
|
+
|
80
|
+
return nil unless File.exist? path
|
81
|
+
|
82
|
+
tpl = ::Liquid::Template.parse File.read(path)
|
83
|
+
tpl.render(payloads)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
data/lib/persie/builders/pdf.rb
CHANGED
@@ -11,16 +11,17 @@ module Persie
|
|
11
11
|
|
12
12
|
# Builds PDF.
|
13
13
|
def build
|
14
|
-
|
14
|
+
info '=== Build PDF ' << '=' * 58
|
15
15
|
|
16
|
+
self.before_build
|
16
17
|
self.check_dependency
|
17
18
|
self.check_sample
|
18
|
-
|
19
19
|
self.convert_to_html
|
20
20
|
self.restart_page_number
|
21
21
|
self.convert_to_pdf
|
22
|
+
self.after_build
|
22
23
|
|
23
|
-
|
24
|
+
info END_LINE
|
24
25
|
|
25
26
|
nil
|
26
27
|
end
|
@@ -28,8 +29,8 @@ module Persie
|
|
28
29
|
# Checks dependency.
|
29
30
|
def check_dependency
|
30
31
|
unless Dependency.prince_installed?
|
31
|
-
|
32
|
-
|
32
|
+
error 'Error: PrinceXML not installed'
|
33
|
+
info END_LINE
|
33
34
|
exit 22
|
34
35
|
end
|
35
36
|
end
|
@@ -54,13 +55,13 @@ module Persie
|
|
54
55
|
|
55
56
|
# Converts AsciiDoc document to HTML, and writes to a file.
|
56
57
|
def convert_to_html
|
57
|
-
|
58
|
+
info 'Converting to HTML...'
|
58
59
|
html = @document.convert
|
59
60
|
prepare_directory(self.html_path)
|
60
61
|
File.write(self.html_path, html)
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
confirm ' HTMl file created'
|
63
|
+
info " Location: #{self.html_path(true)}"
|
64
|
+
info '' # new line
|
64
65
|
end
|
65
66
|
|
66
67
|
# Restart PDF page number.
|
@@ -81,15 +82,15 @@ module Persie
|
|
81
82
|
|
82
83
|
# Converts HTML to PDF with PrinceXML.
|
83
84
|
def convert_to_pdf
|
84
|
-
|
85
|
+
info 'Converting to PDF...'
|
85
86
|
prepare_directory(self.pdf_path)
|
86
87
|
system "prince #{self.html_path} -o #{self.pdf_path}"
|
87
88
|
if $?.to_i == 0
|
88
|
-
|
89
|
-
|
89
|
+
confirm ' PDF file created'
|
90
|
+
info " Location: #{self.pdf_path(true)}"
|
90
91
|
else
|
91
|
-
|
92
|
-
|
92
|
+
error ' Error: Cannot create PDF with PrinceXML'
|
93
|
+
info END_LINE
|
93
94
|
exit 23
|
94
95
|
end
|
95
96
|
end
|
@@ -3,43 +3,33 @@ require 'liquid'
|
|
3
3
|
require_relative '../builder'
|
4
4
|
|
5
5
|
module Persie
|
6
|
-
class
|
6
|
+
class SingleHTML < Builder
|
7
7
|
|
8
8
|
def initialize(book, options = {})
|
9
9
|
super
|
10
10
|
end
|
11
11
|
|
12
|
-
# Builds
|
12
|
+
# Builds single HTML file.
|
13
13
|
def build
|
14
|
-
|
14
|
+
info '=== Build Single HTML ' << '=' * 50
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self.build_single
|
16
|
+
self.before_build
|
17
|
+
self.check_sample
|
18
|
+
self.generate_html
|
19
|
+
self.after_build
|
22
20
|
|
23
|
-
|
21
|
+
info END_LINE
|
24
22
|
|
25
23
|
nil
|
26
24
|
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@ui.warning 'Not Implemented!'
|
32
|
-
end
|
33
|
-
|
34
|
-
# Builds single file website.
|
35
|
-
def build_single
|
36
|
-
@ui.warning "Single page\n"
|
37
|
-
|
38
|
-
html_path = File.join(@book.builds_dir, 'site', 'single' ,'index.html')
|
26
|
+
# Generates single HTML file.
|
27
|
+
def generate_html
|
28
|
+
html_path = File.join(@book.builds_dir, 'html', 'single' ,'index.html')
|
39
29
|
prepare_directory(html_path)
|
40
30
|
|
41
31
|
html = @document.convert
|
42
|
-
content =
|
32
|
+
content = render_layout assemble_payloads(html)
|
43
33
|
|
44
34
|
if content.nil?
|
45
35
|
File.write(html_path, html)
|
@@ -48,11 +38,11 @@ module Persie
|
|
48
38
|
end
|
49
39
|
|
50
40
|
if File.exist? html_path
|
51
|
-
|
52
|
-
|
41
|
+
confirm 'HTML created'
|
42
|
+
info "Location: builds/html/single/index.html"
|
53
43
|
else
|
54
|
-
|
55
|
-
|
44
|
+
error 'Cannot create HTML'
|
45
|
+
info END_LINE
|
56
46
|
exit 52
|
57
47
|
end
|
58
48
|
end
|
@@ -61,8 +51,8 @@ module Persie
|
|
61
51
|
|
62
52
|
def adoc_custom_attributes
|
63
53
|
{
|
64
|
-
'ebook-format' => '
|
65
|
-
'single-page' =>
|
54
|
+
'ebook-format' => 'html',
|
55
|
+
'single-page' => true,
|
66
56
|
'outfilesuffix' => '.html'
|
67
57
|
}
|
68
58
|
end
|
@@ -89,16 +79,9 @@ module Persie
|
|
89
79
|
attrs.merge(custom)
|
90
80
|
end
|
91
81
|
|
92
|
-
# Renders ERb layouts
|
93
|
-
def
|
94
|
-
|
95
|
-
@ui.error "ONLY can render layout for `single' or `multiple'"
|
96
|
-
@ui.info END_LINE
|
97
|
-
exit 53
|
98
|
-
end
|
99
|
-
|
100
|
-
# Site templates stored in `themes/site/' folder
|
101
|
-
path = File.join @book.themes_dir, 'site', "#{format}.html.liquid"
|
82
|
+
# Renders ERb layouts.
|
83
|
+
def render_layout(payloads)
|
84
|
+
path = File.join @book.themes_dir, 'html', 'single.html.liquid'
|
102
85
|
|
103
86
|
return nil unless File.exist? path
|
104
87
|
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Persie
|
4
|
+
module Chunkable
|
5
|
+
|
6
|
+
# these are not using `include' directive
|
7
|
+
SPECIAL_SPINE_ITEMS = ['cover', 'titlepage', 'nav']
|
8
|
+
|
9
|
+
# Gets/Sets spine items.
|
10
|
+
attr_accessor :spine_items
|
11
|
+
|
12
|
+
# Gets/Sets spine items's titles.
|
13
|
+
attr_accessor :spine_item_titles
|
14
|
+
|
15
|
+
# Converts to single HTML file.
|
16
|
+
def convert_to_single_html
|
17
|
+
info 'Converting to HTML...'
|
18
|
+
format = @document.attr('ebook-format')
|
19
|
+
html = @document.convert
|
20
|
+
prepare_directory self.html_path(format)
|
21
|
+
File.write self.html_path(format), html
|
22
|
+
confirm ' HTMl file created'
|
23
|
+
info " Location: #{self.html_path(format, true)}\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generates spine items.
|
27
|
+
def generate_spine_items
|
28
|
+
register_spine_item_processor
|
29
|
+
|
30
|
+
# Re-load the master file
|
31
|
+
doc = ::Asciidoctor.load_file(@book.master_file, adoc_options)
|
32
|
+
@spine_items.concat SPECIAL_SPINE_ITEMS
|
33
|
+
@spine_items.concat doc.references['spine_items']
|
34
|
+
|
35
|
+
# no need cover page and titlepage in HTML format
|
36
|
+
@spine_items.shift(2) if @document.attr('ebook-format') == 'html'
|
37
|
+
|
38
|
+
@spine_items
|
39
|
+
end
|
40
|
+
|
41
|
+
# Chucks single HTML file to multiple HTML files.
|
42
|
+
def chunk
|
43
|
+
info 'Chunking files...'
|
44
|
+
|
45
|
+
format = @document.attr('ebook-format')
|
46
|
+
content = File.read self.html_path(format)
|
47
|
+
root = ::Nokogiri::HTML(content)
|
48
|
+
|
49
|
+
# Adjust spint items
|
50
|
+
@has_cover = root.css('div[data-type="cover"]').size > 0
|
51
|
+
@has_toc = root.css('nav[data-type="toc"]').size > 0
|
52
|
+
self.spine_items.delete('cover') unless @has_cover
|
53
|
+
self.spine_items.delete('toc') unless @has_toc
|
54
|
+
|
55
|
+
correct_nav_href(root)
|
56
|
+
|
57
|
+
top_level_sections = resolve_top_level_sections(root)
|
58
|
+
|
59
|
+
# stupid check, incase of something went wrong
|
60
|
+
unless top_level_sections.count == self.spine_items.count
|
61
|
+
error ' Count of sections DO NOT equal to spine items count.'
|
62
|
+
error ' Terminated!'
|
63
|
+
if @options.debug?
|
64
|
+
info 'sections count: ' + top_level_sections.count
|
65
|
+
info 'spine_items: ' + self.spine_items.inspect
|
66
|
+
end
|
67
|
+
info '=' * 72
|
68
|
+
exit 31
|
69
|
+
end
|
70
|
+
|
71
|
+
sep = '<body data-type="book">'
|
72
|
+
before = content.split(sep).first
|
73
|
+
after = %(</body>\n</html>)
|
74
|
+
|
75
|
+
# Collect the first h1 heading first
|
76
|
+
top_level_sections.each_with_index do |node, i|
|
77
|
+
title = if (i == 0 && @has_cover) # cover page don't have title
|
78
|
+
@document.attr('cover-page-title', 'Cover')
|
79
|
+
else
|
80
|
+
node.css('h1:first-of-type').first.inner_text
|
81
|
+
end
|
82
|
+
@spine_item_titles << title
|
83
|
+
end
|
84
|
+
|
85
|
+
top_level_sections.each_with_index do |node, i|
|
86
|
+
# Footnotes
|
87
|
+
footnotes_div = generate_footnotes(node)
|
88
|
+
|
89
|
+
# Write to chunked file
|
90
|
+
ext = @document.attr('outfilesuffix', '.html')
|
91
|
+
to_dir = if format == 'epub'
|
92
|
+
@tmp_dir
|
93
|
+
else
|
94
|
+
File.join @book.builds_dir, 'html', 'multiple'
|
95
|
+
end
|
96
|
+
path = File.join(to_dir, "#{self.spine_items[i]}#{ext}")
|
97
|
+
prepare_directory(path)
|
98
|
+
combined = [before, sep, node.to_xhtml, footnotes_div, after] * "\n"
|
99
|
+
|
100
|
+
# use only when building multiple html files
|
101
|
+
layout = File.join @book.themes_dir, 'html', 'multiple.html.liquid'
|
102
|
+
|
103
|
+
chunked_content = if format == 'epub'
|
104
|
+
combined
|
105
|
+
elsif (format == 'html' && !File.exist?(layout))
|
106
|
+
combined
|
107
|
+
else
|
108
|
+
require 'liquid'
|
109
|
+
tpl = ::Liquid::Template.parse File.read(layout)
|
110
|
+
|
111
|
+
if i == 0
|
112
|
+
prev_url = nil
|
113
|
+
prev_title = nil
|
114
|
+
else
|
115
|
+
prev_url = "#{@spine_items[i - 1]}#{ext}"
|
116
|
+
prev_title = @spine_item_titles[i - 1]
|
117
|
+
end
|
118
|
+
|
119
|
+
if (j = i + 1) == @spine_items.count
|
120
|
+
next_url = nil
|
121
|
+
next_title = nil
|
122
|
+
else
|
123
|
+
next_url = "#{@spine_items[j]}#{ext}"
|
124
|
+
next_title = @spine_item_titles[j]
|
125
|
+
end
|
126
|
+
|
127
|
+
payloads = assemble_payloads.merge 'page_title' => @spine_item_titles[i],
|
128
|
+
'content' => node.to_xhtml,
|
129
|
+
'footnotes' => footnotes_div,
|
130
|
+
'prev_url' => prev_url,
|
131
|
+
'next_url' => next_url,
|
132
|
+
'prev_title' => prev_title,
|
133
|
+
'next_title' => next_title
|
134
|
+
|
135
|
+
tpl.render(payloads)
|
136
|
+
end
|
137
|
+
|
138
|
+
File.write path, chunked_content
|
139
|
+
end
|
140
|
+
|
141
|
+
confirm " Done"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Gets XHTML file path.
|
145
|
+
def html_path(format, relative = false)
|
146
|
+
name = sample? ? "#{@book.slug}-sample" : @book.slug
|
147
|
+
path = File.join('tmp', format, "#{name}.html")
|
148
|
+
return path if relative
|
149
|
+
|
150
|
+
File.join(@book.base_dir, path)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# Corrects navigation items' href.
|
156
|
+
#
|
157
|
+
# Example:
|
158
|
+
# href="#id" => href="path.xhtml#id"
|
159
|
+
def correct_nav_href(node)
|
160
|
+
# return early if no table of contents
|
161
|
+
return nil unless (ols = node.css('nav[data-type="toc"]> ol')).size > 0
|
162
|
+
|
163
|
+
spine_items_dup = self.spine_items.dup
|
164
|
+
SPECIAL_SPINE_ITEMS.each { |i| spine_items_dup.delete(i) }
|
165
|
+
|
166
|
+
ext = @document.attr('outfilesuffix', '.html')
|
167
|
+
top_level_lis = ols.first.css('> li')
|
168
|
+
j = 0
|
169
|
+
top_level_lis.each do |li|
|
170
|
+
if li['data-type'] == 'part'
|
171
|
+
first_a = li.css('> a').first
|
172
|
+
first_a_href = first_a['href']
|
173
|
+
first_a['href'] = "#{spine_items_dup[j]}#{ext}#{first_a_href}"
|
174
|
+
if (li_ols = li.css('> ol')).size > 0
|
175
|
+
li_ol = li_ols.first
|
176
|
+
li_ol.css('> li').each do |lli|
|
177
|
+
j += 1
|
178
|
+
lli.css('a').each do |a|
|
179
|
+
old_href = a['href']
|
180
|
+
a['href'] = "#{spine_items_dup[j]}#{ext}#{old_href}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
j += 1
|
184
|
+
end
|
185
|
+
else
|
186
|
+
li.css('a').each do |a|
|
187
|
+
old_href = a['href']
|
188
|
+
a['href'] = "#{spine_items_dup[j]}#{ext}#{old_href}"
|
189
|
+
end
|
190
|
+
j += 1
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Resolves top level sections.
|
196
|
+
#
|
197
|
+
# When there are parts, takes sections within each part out.
|
198
|
+
def resolve_top_level_sections(node)
|
199
|
+
if (parts = node.css('body > div[data-type="part"]')).size > 0
|
200
|
+
parts.each do |part|
|
201
|
+
sections = part.css('> section')
|
202
|
+
sections.each do |sect|
|
203
|
+
part.delete sect
|
204
|
+
end
|
205
|
+
part.add_next_sibling(sections)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# return a nodeset, contains all top level sections
|
210
|
+
node.css('body > *')
|
211
|
+
end
|
212
|
+
|
213
|
+
# Generates footnotes for one node.
|
214
|
+
def generate_footnotes(node)
|
215
|
+
footnotes_div = nil
|
216
|
+
footnotes = node.css('span[data-type="footnote"]')
|
217
|
+
if footnotes.length > 0
|
218
|
+
footnotes_div = generate_footnotes_div(footnotes)
|
219
|
+
replace_footnote_with_sup(footnotes)
|
220
|
+
end
|
221
|
+
|
222
|
+
footnotes_div
|
223
|
+
end
|
224
|
+
|
225
|
+
# Generate a footnotes div element.
|
226
|
+
def generate_footnotes_div(footnotes)
|
227
|
+
epub_type = if @document.attr('ebook-format') == 'epub'
|
228
|
+
%( epub:type="footnote")
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
result = ['<div class="footnotes">']
|
233
|
+
result << '<ol>'
|
234
|
+
footnotes.each_with_index do |fn, i|
|
235
|
+
index = i + 1
|
236
|
+
ref = %( <a href="#fn-ref-#{index}">↩</a>)
|
237
|
+
result << %(<li id="fn-#{index}"#{epub_type}>#{fn.inner_html}#{ref}</li>)
|
238
|
+
end
|
239
|
+
result << '</ol>'
|
240
|
+
result << '</div>'
|
241
|
+
|
242
|
+
result * "\n"
|
243
|
+
end
|
244
|
+
|
245
|
+
def replace_footnote_with_sup(footnotes)
|
246
|
+
footnotes.each_with_index do |fn, i|
|
247
|
+
index = i + 1
|
248
|
+
fn.replace(%(<sup><a id="fn-ref-#{index}" href="#fn-#{index}">#{index}</a></sup>))
|
249
|
+
end
|
250
|
+
|
251
|
+
nil
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
end
|