asciidoctor-multipage 0.0.1
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 +7 -0
- data/lib/asciidoctor-multipage.rb +561 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b64a5fbb964951f440f2fa2c0d49cb1df7ace58be958a0e1f4ce280dca9e4591
|
4
|
+
data.tar.gz: 51c273c008afa11caad90d1b808e65919e2764f092891cd6d149934dcce322ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fc96577284cc71802f971196a99536f2ba3fc87dc97dad4833f40722cb8844819ca5b9132e2f1e63adf16701a9a987d1a14e89c76944b264ff8438f63775ec3e
|
7
|
+
data.tar.gz: dfee8f462b3cb3d81c9384f04e96fbdcb7c9c621ebdda69152d8dabd8fe55597d34315794aa0065d4d30edc07cf71e02abf3b5433191249e74e9c512ab6f697c
|
@@ -0,0 +1,561 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'asciidoctor/converter/html5'
|
4
|
+
|
5
|
+
class Asciidoctor::AbstractBlock
|
6
|
+
# Allow navigation links HTML to be saved and retrieved
|
7
|
+
attr_accessor :nav_links
|
8
|
+
end
|
9
|
+
|
10
|
+
class Asciidoctor::AbstractNode
|
11
|
+
# Is this node (self) of interest when generating a TOC for node?
|
12
|
+
def related_to?(node)
|
13
|
+
return true if self.level == 0
|
14
|
+
node_tree = []
|
15
|
+
current = node
|
16
|
+
while current.class != Asciidoctor::Document
|
17
|
+
node_tree << current
|
18
|
+
current = current.parent
|
19
|
+
end
|
20
|
+
if node_tree.include?(self) ||
|
21
|
+
node_tree.include?(self.parent)
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
# If this is a leaf page, include all child sections in TOC
|
25
|
+
if node.mplevel == :leaf
|
26
|
+
self_tree = []
|
27
|
+
current = self
|
28
|
+
while current && current.level >= node.level
|
29
|
+
self_tree << current
|
30
|
+
current = current.parent
|
31
|
+
end
|
32
|
+
return true if self_tree.include?(node)
|
33
|
+
end
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Asciidoctor::Document
|
39
|
+
# Allow writing to the :catalog attribute in order to duplicate refs list to
|
40
|
+
# new pages
|
41
|
+
attr_writer :catalog
|
42
|
+
|
43
|
+
# Allow the section type to be saved (for when a Section becomes a Document)
|
44
|
+
attr_accessor :mplevel
|
45
|
+
|
46
|
+
# Allow the current Document to be marked as processed by this extension
|
47
|
+
attr_accessor :processed
|
48
|
+
|
49
|
+
# Allow saving of section number for use later. This is necessary for when a
|
50
|
+
# branch or leaf Section becomes a Document during chunking and ancestor
|
51
|
+
# nodes are no longer accessible.
|
52
|
+
attr_writer :sectnum
|
53
|
+
|
54
|
+
# Override the AbstractBlock sections?() check to enable the Table Of
|
55
|
+
# Contents. This extension may generate short pages that would normally have
|
56
|
+
# no need for a TOC. However, we override the Html5Converter outline() in
|
57
|
+
# order to generate a custom TOC for each page with entries that span the
|
58
|
+
# entire document.
|
59
|
+
def sections?
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the saved section number for this Document object (which was
|
64
|
+
# originally a Section)
|
65
|
+
def sectnum(delimiter = nil, append = nil)
|
66
|
+
@sectnum
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Asciidoctor::Section
|
71
|
+
# Allow the section type (:root, :branch, :leaf) to be saved for each section
|
72
|
+
attr_accessor :mplevel
|
73
|
+
|
74
|
+
# Extend sectnum() to use the Document's saved sectnum. Document objects
|
75
|
+
# normally do not have sectnums, but here Documents are generated from
|
76
|
+
# Sections. The sectnum is saved in section() below.
|
77
|
+
def sectnum(delimiter = '.', append = nil)
|
78
|
+
append ||= (append == false ? '' : delimiter)
|
79
|
+
if @level == 1
|
80
|
+
%(#{@number}#{append})
|
81
|
+
elsif @level > 1
|
82
|
+
if @parent.class == Asciidoctor::Section ||
|
83
|
+
(@mplevel && @parent.class == Asciidoctor::Document)
|
84
|
+
%(#{@parent.sectnum(delimiter)}#{@number}#{append})
|
85
|
+
else
|
86
|
+
%(#{@number}#{append})
|
87
|
+
end
|
88
|
+
else # @level == 0
|
89
|
+
%(#{Asciidoctor::Helpers.int_to_roman @number}#{append})
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class MultipageHtml5Converter < Asciidoctor::Converter::Html5Converter
|
95
|
+
include Asciidoctor
|
96
|
+
include Asciidoctor::Converter
|
97
|
+
include Asciidoctor::Writer
|
98
|
+
|
99
|
+
register_for 'multipage_html5'
|
100
|
+
|
101
|
+
attr_accessor :pages
|
102
|
+
|
103
|
+
def initialize(backend, opts = {})
|
104
|
+
@xml_mode = false
|
105
|
+
@void_element_slash = nil
|
106
|
+
super
|
107
|
+
@stylesheets = Stylesheets.instance
|
108
|
+
@pages = []
|
109
|
+
end
|
110
|
+
|
111
|
+
# Add navigation links to the page (from nav_links)
|
112
|
+
def add_nav_links(page)
|
113
|
+
block = Asciidoctor::Block.new(parent = page,
|
114
|
+
:paragraph,
|
115
|
+
opts = {:source => page.nav_links})
|
116
|
+
block.add_role('nav-footer')
|
117
|
+
page << block
|
118
|
+
end
|
119
|
+
|
120
|
+
def convert(node, transform = nil, opts = {})
|
121
|
+
transform ||= node.node_name
|
122
|
+
opts.empty? ? (send transform, node) : (send transform, node, opts)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Process Document (either the original full document or a processed page)
|
126
|
+
def document(node)
|
127
|
+
if node.processed
|
128
|
+
# This node can now be handled by Html5Converter.
|
129
|
+
super
|
130
|
+
else
|
131
|
+
# This node is the original full document which has not yet been
|
132
|
+
# processed; this is the entry point for the extension.
|
133
|
+
|
134
|
+
# Turn off extensions to avoid running them twice.
|
135
|
+
# FIXME: DocinfoProcessor, InlineMacroProcessor, and Postprocessor
|
136
|
+
# extensions should be retained. Is this possible with the API?
|
137
|
+
#Asciidoctor::Extensions.unregister_all
|
138
|
+
|
139
|
+
# Check toclevels and multipage-level attributes
|
140
|
+
mplevel = node.document.attr('multipage-level', 1).to_i
|
141
|
+
toclevels = node.document.attr('toclevels', 2).to_i
|
142
|
+
if toclevels < mplevel
|
143
|
+
logger.warn 'toclevels attribute should be >= multipage-level'
|
144
|
+
end
|
145
|
+
if mplevel < 0
|
146
|
+
logger.warn 'multipage-level attribute must be >= 0'
|
147
|
+
mplevel = 0
|
148
|
+
end
|
149
|
+
node.document.set_attribute('multipage-level', mplevel.to_s)
|
150
|
+
|
151
|
+
# Set multipage chunk types
|
152
|
+
set_multipage_attrs(node)
|
153
|
+
|
154
|
+
# Set the "id" attribute for the Document, using the "docname", which is
|
155
|
+
# based on the file name. Then register the document ID using the
|
156
|
+
# document title. This allows cross-references to refer to (1) the
|
157
|
+
# top-level document itself or (2) anchors in top-level content (blocks
|
158
|
+
# that are specified before any sections).
|
159
|
+
node.id = node.attributes['docname']
|
160
|
+
node.register(:refs, [node.id,
|
161
|
+
(Inline.new(parent = node,
|
162
|
+
context = :anchor,
|
163
|
+
text = node.doctitle,
|
164
|
+
opts = {:type => :ref,
|
165
|
+
:id => node.id})),
|
166
|
+
node.doctitle])
|
167
|
+
|
168
|
+
# Generate navigation links for all pages
|
169
|
+
generate_nav_links(node)
|
170
|
+
|
171
|
+
# Create and save a skeleton document for generating the TOC lists.
|
172
|
+
@@full_outline = new_outline_doc(node)
|
173
|
+
# Save the document catalog to use for each part/chapter page.
|
174
|
+
@catalog = node.catalog
|
175
|
+
|
176
|
+
# Retain any book intro blocks, delete others, and add a list of sections
|
177
|
+
# for the book landing page.
|
178
|
+
parts_list = Asciidoctor::List.new(node, :ulist)
|
179
|
+
node.blocks.delete_if do |block|
|
180
|
+
if block.context == :section
|
181
|
+
part = block
|
182
|
+
part.convert
|
183
|
+
text = %(<<#{part.id},#{part.captioned_title}>>)
|
184
|
+
if desc = block.attr('desc') then text << %( – #{desc}) end
|
185
|
+
parts_list << Asciidoctor::ListItem.new(parts_list, text)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
node << parts_list
|
189
|
+
|
190
|
+
# Add navigation links
|
191
|
+
add_nav_links(node)
|
192
|
+
|
193
|
+
# Mark page as processed and return converted result
|
194
|
+
node.processed = true
|
195
|
+
node.convert
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Process Document in embeddable mode (either the original full document or a
|
200
|
+
# processed page)
|
201
|
+
def embedded(node)
|
202
|
+
if node.processed
|
203
|
+
# This node can now be handled by Html5Converter.
|
204
|
+
super
|
205
|
+
else
|
206
|
+
# This node is the original full document which has not yet been
|
207
|
+
# processed; it can be handled by document().
|
208
|
+
document(node)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Generate navigation links for all pages in document; save HTML to nav_links
|
213
|
+
def generate_nav_links(doc)
|
214
|
+
pages = doc.find_by(context: :section) {|section|
|
215
|
+
[:root, :branch, :leaf].include?(section.mplevel)}
|
216
|
+
pages.insert(0, doc)
|
217
|
+
pages.each do |page|
|
218
|
+
page_index = pages.find_index(page)
|
219
|
+
links = []
|
220
|
+
if page.mplevel != :root
|
221
|
+
previous_page = pages[page_index-1]
|
222
|
+
parent_page = page.parent
|
223
|
+
home_page = doc
|
224
|
+
# NOTE, there are some non-breaking spaces (U+00A0) below.
|
225
|
+
if previous_page != parent_page
|
226
|
+
links << %(← Previous: <<#{previous_page.id}>>)
|
227
|
+
end
|
228
|
+
links << %(↑ Up: <<#{parent_page.id}>>)
|
229
|
+
links << %(⌂ Home: <<#{home_page.id}>>) if home_page != parent_page
|
230
|
+
end
|
231
|
+
if page_index != pages.length-1
|
232
|
+
next_page = pages[page_index+1]
|
233
|
+
links << %(Next: <<#{next_page.id}>> →)
|
234
|
+
end
|
235
|
+
block = Asciidoctor::Block.new(parent = doc,
|
236
|
+
context = :paragraph,
|
237
|
+
opts = {:source => links.join(' | '),
|
238
|
+
:subs => :default})
|
239
|
+
page.nav_links = block.content
|
240
|
+
end
|
241
|
+
return
|
242
|
+
end
|
243
|
+
|
244
|
+
# Generate the actual HTML outline for the TOC. This method is analogous to
|
245
|
+
# Html5Converter outline().
|
246
|
+
def generate_outline(node, opts = {})
|
247
|
+
# This is the same as Html5Converter outline()
|
248
|
+
return unless node.sections?
|
249
|
+
sectnumlevels = opts[:sectnumlevels] || (node.document.attr 'sectnumlevels', 3).to_i
|
250
|
+
toclevels = opts[:toclevels] || (node.document.attr 'toclevels', 2).to_i
|
251
|
+
sections = node.sections
|
252
|
+
result = [%(<ul class="sectlevel#{sections[0].level}">)]
|
253
|
+
sections.each do |section|
|
254
|
+
slevel = section.level
|
255
|
+
if section.caption
|
256
|
+
stitle = section.captioned_title
|
257
|
+
elsif section.numbered && slevel <= sectnumlevels
|
258
|
+
stitle = %(#{section.sectnum} #{section.title})
|
259
|
+
else
|
260
|
+
stitle = section.title
|
261
|
+
end
|
262
|
+
stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '<a'
|
263
|
+
|
264
|
+
# But add a special style for current page in TOC
|
265
|
+
if section.id == opts[:page_id]
|
266
|
+
stitle = %(<span class="toc-current">#{stitle}</span>)
|
267
|
+
end
|
268
|
+
|
269
|
+
# And we also need to find the parent page of the target node
|
270
|
+
current = section
|
271
|
+
until current.mplevel != :content
|
272
|
+
current = current.parent
|
273
|
+
end
|
274
|
+
parent_chapter = current
|
275
|
+
|
276
|
+
# If the target is the top-level section of the parent page, there is no
|
277
|
+
# need to include the anchor.
|
278
|
+
if parent_chapter.id == section.id
|
279
|
+
link = %(#{parent_chapter.id}.html)
|
280
|
+
else
|
281
|
+
link = %(#{parent_chapter.id}.html##{section.id})
|
282
|
+
end
|
283
|
+
result << %(<li><a href="#{link}">#{stitle}</a>)
|
284
|
+
|
285
|
+
# Finish in a manner similar to Html5Converter outline()
|
286
|
+
if slevel < toclevels &&
|
287
|
+
(child_toc_level = generate_outline section,
|
288
|
+
:toclevels => toclevels,
|
289
|
+
:secnumlevels => sectnumlevels,
|
290
|
+
:page_id => opts[:page_id])
|
291
|
+
result << child_toc_level
|
292
|
+
end
|
293
|
+
result << '</li>'
|
294
|
+
end
|
295
|
+
result << '</ul>'
|
296
|
+
result.join LF
|
297
|
+
end
|
298
|
+
|
299
|
+
# Include chapter pages in cross-reference links. This method overrides for
|
300
|
+
# the :xref node type only.
|
301
|
+
def inline_anchor(node)
|
302
|
+
if node.type == :xref
|
303
|
+
# This is the same as super...
|
304
|
+
if (path = node.attributes['path'])
|
305
|
+
attrs = (append_link_constraint_attrs node, node.role ? [%( class="#{node.role}")] : []).join
|
306
|
+
text = node.text || path
|
307
|
+
else
|
308
|
+
attrs = node.role ? %( class="#{node.role}") : ''
|
309
|
+
unless (text = node.text)
|
310
|
+
refid = node.attributes['refid']
|
311
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
|
312
|
+
text = (ref.xreftext node.attr('xrefstyle')) || %([#{refid}])
|
313
|
+
else
|
314
|
+
text = %([#{refid}])
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# But we also need to find the parent page of the target node.
|
320
|
+
current = node.document.catalog[:refs][node.attributes['refid']]
|
321
|
+
until current.respond_to?(:mplevel) && current.mplevel != :content
|
322
|
+
return %(<a href="#{node.target}"#{attrs}>#{text}</a>) if !current
|
323
|
+
current = current.parent
|
324
|
+
end
|
325
|
+
parent_page = current
|
326
|
+
|
327
|
+
# If the target is the top-level section of the parent page, there is no
|
328
|
+
# need to include the anchor.
|
329
|
+
if "##{parent_page.id}" == node.target
|
330
|
+
target = "#{parent_page.id}.html"
|
331
|
+
else
|
332
|
+
target = "#{parent_page.id}.html#{node.target}"
|
333
|
+
end
|
334
|
+
|
335
|
+
%(<a href="#{target}"#{attrs}>#{text}</a>)
|
336
|
+
else
|
337
|
+
# Other anchor types can be handled as normal.
|
338
|
+
super
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# From node, create a skeleton document that will be used to generate the
|
343
|
+
# TOC. This is first used to create a full skeleton (@@full_outline) from the
|
344
|
+
# original document (for_page=nil). Then it is used for each individual page
|
345
|
+
# to create a second skeleton from the first. In this way, TOC entries are
|
346
|
+
# included that are not part of the current page, or excluded if not
|
347
|
+
# applicable for the current page.
|
348
|
+
def new_outline_doc(node, new_parent:nil, for_page:nil)
|
349
|
+
if node.class == Document
|
350
|
+
new_document = Document.new([])
|
351
|
+
new_document.mplevel = node.mplevel
|
352
|
+
new_document.id = node.id
|
353
|
+
new_document.set_attr('sectnumlevels', node.attr(:sectnumlevels))
|
354
|
+
new_document.set_attr('toclevels', node.attr(:toclevels))
|
355
|
+
new_parent = new_document
|
356
|
+
node.sections.each do |section|
|
357
|
+
new_outline_doc(section, new_parent: new_parent,
|
358
|
+
for_page: for_page)
|
359
|
+
end
|
360
|
+
# Include the node if either (1) we are creating the full skeleton from the
|
361
|
+
# original document or (2) the node is applicable to the current page.
|
362
|
+
elsif !for_page ||
|
363
|
+
node.related_to?(for_page)
|
364
|
+
new_section = Section.new(parent = new_parent,
|
365
|
+
level = node.level,
|
366
|
+
numbered = node.numbered)
|
367
|
+
new_section.id = node.id
|
368
|
+
new_section.caption = node.caption
|
369
|
+
new_section.title = node.title
|
370
|
+
new_section.mplevel = node.mplevel
|
371
|
+
new_parent << new_section
|
372
|
+
new_parent.sections.last.number = node.number
|
373
|
+
new_parent = new_section
|
374
|
+
node.sections.each do |section|
|
375
|
+
new_outline_doc(section, new_parent: new_parent,
|
376
|
+
for_page: for_page)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
return new_document
|
380
|
+
end
|
381
|
+
|
382
|
+
# Override Html5Converter outline() to return a custom TOC outline
|
383
|
+
def outline(node, opts = {})
|
384
|
+
doc = node.document
|
385
|
+
# Find this node in the @@full_outline skeleton document
|
386
|
+
page_node = @@full_outline.find_by(id: node.id).first
|
387
|
+
# Create a skeleton document for this particular page
|
388
|
+
custom_outline_doc = new_outline_doc(@@full_outline, for_page: page_node)
|
389
|
+
opts[:page_id] = node.id
|
390
|
+
# Generate an extra TOC entry for the root page. Add additional styling if
|
391
|
+
# the current page is the root page.
|
392
|
+
root_file = %(#{doc.attr('docname')}#{doc.attr('outfilesuffix')})
|
393
|
+
root_link = %(<a href="#{root_file}">#{doc.doctitle}</a>)
|
394
|
+
classes = ['toc-root']
|
395
|
+
classes << 'toc-current' if node.id == doc.attr('docname')
|
396
|
+
root = %(<span class="#{classes.join(' ')}">#{root_link}</span>)
|
397
|
+
# Create and return the HTML
|
398
|
+
%(<p>#{root}</p>#{generate_outline(custom_outline_doc, opts)})
|
399
|
+
end
|
400
|
+
|
401
|
+
# Change node parent to new parent recursively
|
402
|
+
def reparent(node, parent)
|
403
|
+
node.parent = parent
|
404
|
+
if node.context == :dlist
|
405
|
+
node.find_by(context: :list_item).each do |block|
|
406
|
+
reparent(block, node)
|
407
|
+
end
|
408
|
+
else
|
409
|
+
node.blocks.each do |block|
|
410
|
+
reparent(block, node)
|
411
|
+
if block.context == :table
|
412
|
+
block.columns.each do |col|
|
413
|
+
col.parent = col.parent
|
414
|
+
end
|
415
|
+
block.rows.body.each do |row|
|
416
|
+
row.each do |cell|
|
417
|
+
cell.parent = cell.parent
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Process a Section. Each Section will either be split off into its own page
|
426
|
+
# or processed as normal by Html5Converter.
|
427
|
+
def section(node)
|
428
|
+
doc = node.document
|
429
|
+
if doc.processed
|
430
|
+
# This node can now be handled by Html5Converter.
|
431
|
+
super
|
432
|
+
else
|
433
|
+
# This node is from the original document and has not yet been processed.
|
434
|
+
|
435
|
+
# Create a new page for this section
|
436
|
+
page = Asciidoctor::Document.new([],
|
437
|
+
{:attributes => doc.attributes.clone,
|
438
|
+
:doctype => doc.doctype,
|
439
|
+
:header_footer => !doc.attr?(:embedded),
|
440
|
+
:safe => doc.safe})
|
441
|
+
# Retain webfonts attribute (why is doc.attributes.clone not adequate?)
|
442
|
+
page.set_attr('webfonts', doc.attr(:webfonts))
|
443
|
+
# Save sectnum for use later (a Document object normally has no sectnum)
|
444
|
+
if node.parent.respond_to?(:numbered) && node.parent.numbered
|
445
|
+
page.sectnum = node.parent.sectnum
|
446
|
+
end
|
447
|
+
|
448
|
+
# Process node according to mplevel
|
449
|
+
if node.mplevel == :branch
|
450
|
+
# Retain any part intro blocks, delete others, and add a list
|
451
|
+
# of sections for the part landing page.
|
452
|
+
chapters_list = Asciidoctor::List.new(node, :ulist)
|
453
|
+
node.blocks.delete_if do |block|
|
454
|
+
if block.context == :section
|
455
|
+
chapter = block
|
456
|
+
chapter.convert
|
457
|
+
text = %(<<#{chapter.id},#{chapter.captioned_title}>>)
|
458
|
+
# NOTE, there is a non-breaking space (Unicode U+00A0) below.
|
459
|
+
if desc = block.attr('desc') then text << %( – #{desc}) end
|
460
|
+
chapters_list << Asciidoctor::ListItem.new(chapters_list, text)
|
461
|
+
true
|
462
|
+
end
|
463
|
+
end
|
464
|
+
# Add chapters list to node, reparent node to new page, add
|
465
|
+
# node to page, mark as processed, and add page to @pages.
|
466
|
+
node << chapters_list
|
467
|
+
reparent(node, page)
|
468
|
+
page.blocks << node
|
469
|
+
else # :leaf
|
470
|
+
# Reparent node to new page, add node to page, mark as
|
471
|
+
# processed, and add page to @pages.
|
472
|
+
reparent(node, page)
|
473
|
+
page.blocks << node
|
474
|
+
end
|
475
|
+
|
476
|
+
# Add navigation links using saved HTML
|
477
|
+
page.nav_links = node.nav_links
|
478
|
+
add_nav_links(page)
|
479
|
+
|
480
|
+
# Mark page as processed and add to collection of pages
|
481
|
+
@pages << page
|
482
|
+
page.id = node.id
|
483
|
+
page.catalog = @catalog
|
484
|
+
page.mplevel = node.mplevel
|
485
|
+
page.processed = true
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# Add multipage attribute to all sections in node, recursively.
|
490
|
+
def set_multipage_attrs(node)
|
491
|
+
doc = node.document
|
492
|
+
node.mplevel = :root if node.class == Asciidoctor::Document
|
493
|
+
node.sections.each do |section|
|
494
|
+
# Check custom multipage-level attribute on section
|
495
|
+
if section.attr?('multipage-level', nil, false) &&
|
496
|
+
section.attr('multipage-level').to_i <
|
497
|
+
node.attr('multipage-level').to_i
|
498
|
+
logger.warn %(multipage-level value specified for "#{section.id}" ) +
|
499
|
+
%(section cannot be less than the parent section value)
|
500
|
+
section.set_attr('multipage-level', nil)
|
501
|
+
end
|
502
|
+
# Propogate custom multipage-level value to child node
|
503
|
+
if !section.attr?('multipage-level', nil, false) &&
|
504
|
+
node.attr('multipage-level') != doc.attr('multipage-level')
|
505
|
+
section.set_attr('multipage-level', node.attr('multipage-level'))
|
506
|
+
end
|
507
|
+
# Set section type
|
508
|
+
if section.level < section.attr('multipage-level').to_i
|
509
|
+
section.mplevel = :branch
|
510
|
+
elsif section.level == section.attr('multipage-level').to_i
|
511
|
+
section.mplevel = :leaf
|
512
|
+
else
|
513
|
+
section.mplevel = :content
|
514
|
+
end
|
515
|
+
# Set multipage attribute on child sections now.
|
516
|
+
set_multipage_attrs(section)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
# Convert each page and write it to file. Use filenames based on IDs.
|
521
|
+
def write(output, target)
|
522
|
+
# Write primary (book) landing page
|
523
|
+
::File.open(target, 'w') do |f|
|
524
|
+
f.write(output)
|
525
|
+
end
|
526
|
+
# Write remaining part/chapter pages
|
527
|
+
outdir = ::File.dirname(target)
|
528
|
+
ext = ::File.extname(target)
|
529
|
+
target_name = ::File.basename(target, ext)
|
530
|
+
@pages.each do |doc|
|
531
|
+
chapter_target = doc.id + ext
|
532
|
+
outfile = ::File.join(outdir, chapter_target)
|
533
|
+
::File.open(outfile, 'w') do |f|
|
534
|
+
f.write(doc.convert)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
class MultipageHtml5CSS < Asciidoctor::Extensions::DocinfoProcessor
|
541
|
+
use_dsl
|
542
|
+
at_location :head
|
543
|
+
|
544
|
+
def process doc
|
545
|
+
css = []
|
546
|
+
# Style Table Of Contents entry for current page
|
547
|
+
css << %(.toc-current{font-weight: bold;})
|
548
|
+
# Style Table Of Contents entry for root page
|
549
|
+
css << %(.toc-root{font-family: "Open Sans","DejaVu Sans",sans-serif;
|
550
|
+
font-size: 0.9em;})
|
551
|
+
# Style navigation bar at bottom of each page
|
552
|
+
css << %(#content{display: flex; flex-direction: column; flex: 1 1 auto;}
|
553
|
+
.nav-footer{text-align: center; margin-top: auto;}
|
554
|
+
.nav-footer > p > a {white-space: nowrap;})
|
555
|
+
%(<style>#{css.join(' ')}</style>)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
Asciidoctor::Extensions.register do
|
560
|
+
docinfo_processor MultipageHtml5CSS
|
561
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asciidoctor-multipage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Owen T. Heisler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: asciidoctor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.5.7.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.5.7.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thread_safe
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: An Asciidoctor extension that generates HTML output using multiple pages
|
84
|
+
email:
|
85
|
+
- writer@owenh.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/asciidoctor-multipage.rb
|
91
|
+
homepage: https://github.com/owenh000/asciidoctor-multipage
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata:
|
95
|
+
bug_tracker_uri: https://github.com/owenh000/asciidoctor-multipage/issues
|
96
|
+
homepage_uri: https://github.com/owenh000/asciidoctor-multipage
|
97
|
+
source_code_uri: https://github.com/owenh000/asciidoctor-multipage
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '2.5'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.7.6.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Asciidoctor multipage HTML output extension
|
118
|
+
test_files: []
|