metanorma-document 0.2.0 → 0.2.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 +4 -4
- data/README.adoc +59 -18
- data/data/stylesheets/components/bibliography.css +2 -2
- data/data/stylesheets/components/inline.css +11 -12
- data/docs/html-renderer.adoc +261 -0
- data/lib/metanorma/document/version.rb +1 -1
- data/lib/metanorma/html/base_renderer.rb +180 -219
- data/lib/metanorma/html/drops/admonition_drop.rb +26 -0
- data/lib/metanorma/html/drops/block_element_drop.rb +20 -0
- data/lib/metanorma/html/drops/example_drop.rb +35 -0
- data/lib/metanorma/html/drops/figure_drop.rb +53 -0
- data/lib/metanorma/html/drops/formula_drop.rb +44 -0
- data/lib/metanorma/html/drops/note_drop.rb +32 -0
- data/lib/metanorma/html/drops/sourcecode_drop.rb +37 -0
- data/lib/metanorma/html/drops.rb +7 -0
- data/lib/metanorma/html/iso_renderer.rb +96 -79
- data/lib/metanorma/html/ogc_renderer.rb +5 -5
- data/lib/metanorma/html/standard_renderer.rb +34 -28
- data/lib/metanorma/html/templates/_admonition.html.liquid +4 -0
- data/lib/metanorma/html/templates/_doc_title.html.liquid +1 -1
- data/lib/metanorma/html/templates/_example.html.liquid +3 -0
- data/lib/metanorma/html/templates/_figure.html.liquid +6 -0
- data/lib/metanorma/html/templates/_formula.html.liquid +6 -0
- data/lib/metanorma/html/templates/_iso_doc_title.html.liquid +2 -2
- data/lib/metanorma/html/templates/_note.html.liquid +3 -0
- data/lib/metanorma/html/templates/_sourcecode.html.liquid +4 -0
- metadata +16 -2
|
@@ -17,43 +17,33 @@ module Metanorma
|
|
|
17
17
|
class BaseRenderer
|
|
18
18
|
LOGO_DIR = File.expand_path("../../../data/logos", __dir__)
|
|
19
19
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"TermNum" => "term-number",
|
|
31
|
-
"Terms" => "term-name",
|
|
32
|
-
"DeprecatedTerms" => "term-deprecated",
|
|
33
|
-
"domain" => "term-domain",
|
|
34
|
-
"boldtitle" => "bold-title",
|
|
35
|
-
"note_label" => "note-label",
|
|
36
|
-
"termnote_label" => "term-note-label",
|
|
37
|
-
"example_label" => "example-label",
|
|
38
|
-
"stddocNumber" => "std-doc-number",
|
|
39
|
-
"stdyear" => "std-year",
|
|
40
|
-
"sourcecode-name" => "code-name",
|
|
20
|
+
# HTML-specific class names for inline spans, keyed by the XML span role.
|
|
21
|
+
# The XML class_attr is INPUT only — we never emit it in HTML.
|
|
22
|
+
SPAN_ROLE_CLASSES = {
|
|
23
|
+
"boldtitle" => "title-text",
|
|
24
|
+
"nonboldtitle" => "subtitle-text",
|
|
25
|
+
"citeapp" => "xref-app",
|
|
26
|
+
"citefig" => "xref-fig",
|
|
27
|
+
"citesec" => "xref-section",
|
|
28
|
+
"citetbl" => "xref-table",
|
|
29
|
+
"fmt-autonum-delim" => "number-delim",
|
|
41
30
|
"fmt-caption-label" => "caption-label",
|
|
42
|
-
"fmt-autonum-delim" => "autonum-delim",
|
|
43
|
-
"fmt-element-name" => "element-name",
|
|
44
31
|
"fmt-caption-delim" => "caption-delim",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"nonboldtitle" => "doc-subtitle",
|
|
52
|
-
"stddocPartNumber" => "doc-part-number",
|
|
53
|
-
"stddocTitle" => "doc-part-title",
|
|
54
|
-
"std_publisher" => "doc-publisher",
|
|
55
|
-
"stdpublisher" => "doc-publisher-name",
|
|
32
|
+
"fmt-element-name" => "element-label",
|
|
33
|
+
"fmt-comma" => "comma",
|
|
34
|
+
"fmt-conn" => "connector",
|
|
35
|
+
"fmt-label-delim" => "label-delim",
|
|
36
|
+
"fmt-obligation" => "obligation-text",
|
|
37
|
+
"fmt-xref-container" => "xref-container",
|
|
56
38
|
"fmt-xref-label" => "xref-label",
|
|
39
|
+
"std_publisher" => "ref-publisher",
|
|
40
|
+
"stdpublisher" => "ref-publisher-name",
|
|
41
|
+
"stddocNumber" => "ref-doc-number",
|
|
42
|
+
"stddocTitle" => "ref-title",
|
|
43
|
+
"stddocPartNumber" => "ref-part-number",
|
|
44
|
+
"stdyear" => "ref-year",
|
|
45
|
+
"date" => "date",
|
|
46
|
+
"smallcap" => "small-caps",
|
|
57
47
|
}.freeze
|
|
58
48
|
|
|
59
49
|
METANORMA_LOGO = "metanorma-logo.svg"
|
|
@@ -71,6 +61,40 @@ module Metanorma
|
|
|
71
61
|
|
|
72
62
|
# --- Public API ---
|
|
73
63
|
|
|
64
|
+
# Facade object for Drops to call renderer methods without exposing
|
|
65
|
+
# the full private interface. Delegates to the renderer internally.
|
|
66
|
+
class RendererContext
|
|
67
|
+
def initialize(renderer)
|
|
68
|
+
@renderer = renderer
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def safe_attr(obj, method_name) = @renderer.send(:safe_attr, obj, method_name)
|
|
72
|
+
def escape_html(text) = @renderer.send(:escape_html, text)
|
|
73
|
+
def extract_block_label(block, default) = @renderer.send(:extract_block_label, block, default)
|
|
74
|
+
def extract_plain_text(node) = @renderer.send(:extract_plain_text, node)
|
|
75
|
+
def capture_output(&) = @renderer.send(:capture_output, &)
|
|
76
|
+
def render_paragraph(p) = @renderer.send(:render_paragraph, p)
|
|
77
|
+
def render_mixed_inline(node) = @renderer.send(:render_mixed_inline, node)
|
|
78
|
+
def render_inline_element(el) = @renderer.send(:render_inline_element, el)
|
|
79
|
+
def render_unordered_list(ul) = @renderer.send(:render_unordered_list, ul)
|
|
80
|
+
def render_ordered_list(ol) = @renderer.send(:render_ordered_list, ol)
|
|
81
|
+
def render_definition_list(dl) = @renderer.send(:render_definition_list, dl)
|
|
82
|
+
def render_sourcecode(sc) = @renderer.send(:render_sourcecode, sc)
|
|
83
|
+
def render_table(t) = @renderer.send(:render_table, t)
|
|
84
|
+
def render_figure(f) = @renderer.send(:render_figure, f)
|
|
85
|
+
def render_quote(q) = @renderer.send(:render_quote, q)
|
|
86
|
+
def render_formula(f) = @renderer.send(:render_formula, f)
|
|
87
|
+
def render_note(n) = @renderer.send(:render_note, n)
|
|
88
|
+
def render_image(img) = @renderer.send(:render_image, img)
|
|
89
|
+
def render_stem_content(stem) = @renderer.send(:render_stem_content, stem)
|
|
90
|
+
def register_figure_entry(...) = @renderer.send(:register_figure_entry, ...)
|
|
91
|
+
def render_liquid(template_name, assigns) = @renderer.send(:render_liquid, template_name, assigns)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def renderer_context
|
|
95
|
+
@renderer_context ||= RendererContext.new(self)
|
|
96
|
+
end
|
|
97
|
+
|
|
74
98
|
def to_html
|
|
75
99
|
@output
|
|
76
100
|
end
|
|
@@ -132,16 +156,16 @@ module Metanorma
|
|
|
132
156
|
footer = build_footer
|
|
133
157
|
|
|
134
158
|
render_liquid("document.html.liquid", {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
159
|
+
"lang" => language,
|
|
160
|
+
"title" => html_title,
|
|
161
|
+
"font_url" => flavor_font_url,
|
|
162
|
+
"styles" => build_styles,
|
|
163
|
+
"header" => header,
|
|
164
|
+
"toc" => toc_html,
|
|
165
|
+
"body" => body,
|
|
166
|
+
"footer" => footer,
|
|
167
|
+
"scripts" => build_scripts,
|
|
168
|
+
})
|
|
145
169
|
end
|
|
146
170
|
|
|
147
171
|
# --- Header and Footer ---
|
|
@@ -157,15 +181,15 @@ module Metanorma
|
|
|
157
181
|
end
|
|
158
182
|
|
|
159
183
|
render_liquid("_header.html.liquid", {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
"publisher_logos" => pub_logos,
|
|
185
|
+
"doc_id" => display_id,
|
|
186
|
+
"doc_title" => header_title_text,
|
|
187
|
+
})
|
|
164
188
|
end
|
|
165
189
|
|
|
166
190
|
def header_title_text
|
|
167
191
|
raw = html_title.to_s.split(" — ").first.to_s.gsub(/<[^>]+>/, "")
|
|
168
|
-
raw.length > 60 ? raw[0, 57]
|
|
192
|
+
raw.length > 60 ? "#{raw[0, 57]}..." : raw
|
|
169
193
|
end
|
|
170
194
|
|
|
171
195
|
# Reader controls — kept for backward compat with flavor renderers
|
|
@@ -205,13 +229,12 @@ module Metanorma
|
|
|
205
229
|
svg = svg.sub(/\A\s*<!--.*?-->\s*/m, "")
|
|
206
230
|
svg = svg.sub(/<path[^>]*style="fill:#e3000f[^"]*"[^>]*\/>/, "")
|
|
207
231
|
svg = svg.sub(/<svg\s/, '<svg class="header-logo" ')
|
|
208
|
-
if svg.match?(/<svg[^>]*\sheight="[^"]*"/)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
svg
|
|
214
|
-
svg
|
|
232
|
+
svg = if svg.match?(/<svg[^>]*\sheight="[^"]*"/)
|
|
233
|
+
svg.sub(/(<svg[^>]*?)(\sheight="[^"]*")/, "\\1 height=\"#{height}\"")
|
|
234
|
+
else
|
|
235
|
+
svg.sub(/(<svg\b)/, "\\1 height=\"#{height}\"")
|
|
236
|
+
end
|
|
237
|
+
svg.sub(/(<svg[^>]*?)\swidth="[^"]*"/, '\1')
|
|
215
238
|
rescue StandardError
|
|
216
239
|
nil
|
|
217
240
|
end
|
|
@@ -219,9 +242,9 @@ module Metanorma
|
|
|
219
242
|
def build_footer
|
|
220
243
|
mn_logo = load_logo_svg(METANORMA_LOGO, height: 20)
|
|
221
244
|
render_liquid("_footer.html.liquid", {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
245
|
+
"mn_logo" => mn_logo,
|
|
246
|
+
"generated_at" => Time.now.strftime("%Y-%m-%d %H:%M"),
|
|
247
|
+
})
|
|
225
248
|
end
|
|
226
249
|
|
|
227
250
|
# --- ToC generation ---
|
|
@@ -231,32 +254,32 @@ module Metanorma
|
|
|
231
254
|
main_lines = if entries.empty?
|
|
232
255
|
["<li class=\"toc-empty\">No entries</li>"]
|
|
233
256
|
else
|
|
234
|
-
entries.map
|
|
257
|
+
entries.map do |e|
|
|
235
258
|
id = e[:id].to_s
|
|
236
259
|
text = escape_html(e[:text].to_s)
|
|
237
260
|
lvl = e[:level]
|
|
238
261
|
"<li class=\"toc-level-#{lvl}\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
|
|
239
|
-
|
|
262
|
+
end
|
|
240
263
|
end
|
|
241
264
|
|
|
242
265
|
# List of Figures — at top of sidebar
|
|
243
266
|
unless @figure_entries.empty?
|
|
244
267
|
top_lines << "<li class=\"toc-list-header\" data-list=\"figures\"><button class=\"toc-list-toggle\" aria-expanded=\"false\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"1\"/><circle cx=\"5\" cy=\"6.5\" r=\"1.5\"/><path d=\"M1 12l4-4 2 2 3-3 5 5\"/></svg> Figures <span class=\"toc-list-count\">(#{@figure_entries.size})</span></button></li>"
|
|
245
|
-
@figure_entries.each
|
|
268
|
+
@figure_entries.each do |f|
|
|
246
269
|
id = f[:id].to_s
|
|
247
270
|
text = escape_html(f[:text].to_s)
|
|
248
271
|
top_lines << "<li class=\"toc-list-item toc-figures\" style=\"display:none\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
|
|
249
|
-
|
|
272
|
+
end
|
|
250
273
|
end
|
|
251
274
|
|
|
252
275
|
# List of Tables — at top of sidebar
|
|
253
276
|
unless @table_entries.empty?
|
|
254
277
|
top_lines << "<li class=\"toc-list-header\" data-list=\"tables\"><button class=\"toc-list-toggle\" aria-expanded=\"false\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"1\"/><line x1=\"1\" y1=\"6\" x2=\"15\" y2=\"6\"/><line x1=\"1\" y1=\"10\" x2=\"15\" y2=\"10\"/><line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"14\"/></svg> Tables <span class=\"toc-list-count\">(#{@table_entries.size})</span></button></li>"
|
|
255
|
-
@table_entries.each
|
|
278
|
+
@table_entries.each do |t|
|
|
256
279
|
id = t[:id].to_s
|
|
257
280
|
text = escape_html(t[:text].to_s)
|
|
258
281
|
top_lines << "<li class=\"toc-list-item toc-tables\" style=\"display:none\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
|
|
259
|
-
|
|
282
|
+
end
|
|
260
283
|
end
|
|
261
284
|
|
|
262
285
|
top_lines << "<li class=\"toc-divider\"></li>" unless top_lines.empty?
|
|
@@ -307,12 +330,25 @@ module Metanorma
|
|
|
307
330
|
end
|
|
308
331
|
|
|
309
332
|
if node.is_a?(Lutaml::Model::Serializable)
|
|
310
|
-
return true if
|
|
311
|
-
|
|
333
|
+
return true if begin
|
|
334
|
+
node.fmt_title
|
|
335
|
+
rescue StandardError
|
|
336
|
+
nil
|
|
337
|
+
end
|
|
338
|
+
return true if begin
|
|
339
|
+
node.displayorder
|
|
340
|
+
rescue StandardError
|
|
341
|
+
nil
|
|
342
|
+
end
|
|
312
343
|
|
|
313
344
|
%i[preface sections annex bibliography].each do |attr|
|
|
314
|
-
val =
|
|
345
|
+
val = begin
|
|
346
|
+
node.public_send(attr)
|
|
347
|
+
rescue StandardError
|
|
348
|
+
nil
|
|
349
|
+
end
|
|
315
350
|
next unless val
|
|
351
|
+
|
|
316
352
|
Array(val).each { |v| return true if check_presentation_markers(v) }
|
|
317
353
|
end
|
|
318
354
|
|
|
@@ -405,6 +441,7 @@ module Metanorma
|
|
|
405
441
|
indices = Hash.new(0)
|
|
406
442
|
node.element_order.each do |el|
|
|
407
443
|
next unless el.is_a?(Lutaml::Xml::Element)
|
|
444
|
+
|
|
408
445
|
if el.text?
|
|
409
446
|
parts << el.text_content.to_s
|
|
410
447
|
elsif el.name == "tab"
|
|
@@ -434,42 +471,42 @@ module Metanorma
|
|
|
434
471
|
parts << (t.is_a?(Array) ? t.join : t.to_s) if t
|
|
435
472
|
end
|
|
436
473
|
|
|
437
|
-
parts.join.strip.gsub(
|
|
474
|
+
parts.join.strip.gsub("\u00A0", " ")
|
|
438
475
|
end
|
|
439
476
|
|
|
440
477
|
# Dispatch to the appropriate render method based on node class.
|
|
441
|
-
def render(node, **
|
|
478
|
+
def render(node, **)
|
|
442
479
|
case node
|
|
443
480
|
when Metanorma::Document::Components::Paragraphs::ParagraphBlock
|
|
444
|
-
render_paragraph(node, **
|
|
481
|
+
render_paragraph(node, **)
|
|
445
482
|
when Metanorma::Document::Components::Tables::TableBlock
|
|
446
|
-
render_table(node, **
|
|
483
|
+
render_table(node, **)
|
|
447
484
|
when Metanorma::Document::Components::Lists::UnorderedList
|
|
448
|
-
render_unordered_list(node, **
|
|
485
|
+
render_unordered_list(node, **)
|
|
449
486
|
when Metanorma::Document::Components::Lists::OrderedList
|
|
450
|
-
render_ordered_list(node, **
|
|
487
|
+
render_ordered_list(node, **)
|
|
451
488
|
when Metanorma::Document::Components::Lists::DefinitionList
|
|
452
|
-
render_definition_list(node, **
|
|
489
|
+
render_definition_list(node, **)
|
|
453
490
|
when Metanorma::Document::Components::AncillaryBlocks::FigureBlock
|
|
454
|
-
render_figure(node, **
|
|
491
|
+
render_figure(node, **)
|
|
455
492
|
when Metanorma::Document::Components::Blocks::NoteBlock
|
|
456
|
-
render_note(node, **
|
|
493
|
+
render_note(node, **)
|
|
457
494
|
when Metanorma::Document::Components::AncillaryBlocks::ExampleBlock
|
|
458
|
-
render_example(node, **
|
|
495
|
+
render_example(node, **)
|
|
459
496
|
when Metanorma::Document::Components::AncillaryBlocks::SourcecodeBlock
|
|
460
|
-
render_sourcecode(node, **
|
|
497
|
+
render_sourcecode(node, **)
|
|
461
498
|
when Metanorma::Document::Components::AncillaryBlocks::FormulaBlock
|
|
462
|
-
render_formula(node, **
|
|
499
|
+
render_formula(node, **)
|
|
463
500
|
when Metanorma::Document::Components::MultiParagraph::QuoteBlock
|
|
464
|
-
render_quote(node, **
|
|
501
|
+
render_quote(node, **)
|
|
465
502
|
when Metanorma::Document::Components::MultiParagraph::AdmonitionBlock
|
|
466
|
-
render_admonition(node, **
|
|
503
|
+
render_admonition(node, **)
|
|
467
504
|
when Metanorma::Document::Components::Sections::HierarchicalSection
|
|
468
|
-
render_hierarchical_section(node, **
|
|
505
|
+
render_hierarchical_section(node, **)
|
|
469
506
|
when Metanorma::Document::Components::Sections::BasicSection
|
|
470
|
-
render_basic_section(node, **
|
|
507
|
+
render_basic_section(node, **)
|
|
471
508
|
when Metanorma::Document::Components::Sections::ContentSection
|
|
472
|
-
render_content_section(node, **
|
|
509
|
+
render_content_section(node, **)
|
|
473
510
|
when Metanorma::Document::Components::EmptyElements::PageBreakElement
|
|
474
511
|
""
|
|
475
512
|
when Metanorma::Document::Components::IdElements::Bookmark
|
|
@@ -488,7 +525,7 @@ module Metanorma
|
|
|
488
525
|
# --- Block-level rendering ---
|
|
489
526
|
|
|
490
527
|
def render_paragraph(p, **_opts)
|
|
491
|
-
attrs = element_attrs(id: safe_attr(p, :id),
|
|
528
|
+
attrs = element_attrs(id: safe_attr(p, :id), style: alignment_style(safe_attr(p, :alignment)))
|
|
492
529
|
tag("p", attrs) { render_mixed_inline(p) }
|
|
493
530
|
end
|
|
494
531
|
|
|
@@ -531,19 +568,21 @@ module Metanorma
|
|
|
531
568
|
if table.colgroup&.col && !table.colgroup.col.empty?
|
|
532
569
|
return table.colgroup.col.size
|
|
533
570
|
end
|
|
571
|
+
|
|
534
572
|
# Walk all rows to find max column count, accounting for colspan
|
|
535
573
|
max_cols = 0
|
|
536
|
-
[
|
|
574
|
+
%i[thead tbody tfoot].each do |section|
|
|
537
575
|
sec = table.public_send(section)
|
|
538
576
|
next unless sec&.tr
|
|
577
|
+
|
|
539
578
|
sec.tr.each do |tr|
|
|
540
579
|
cols = 0
|
|
541
|
-
Array(tr.th).each { |th| cols +=
|
|
542
|
-
Array(tr.td).each { |td| cols +=
|
|
580
|
+
Array(tr.th).each { |th| cols += th.colspan && th.colspan > 1 ? th.colspan : 1 }
|
|
581
|
+
Array(tr.td).each { |td| cols += td.colspan && td.colspan > 1 ? td.colspan : 1 }
|
|
543
582
|
max_cols = cols if cols > max_cols
|
|
544
583
|
end
|
|
545
584
|
end
|
|
546
|
-
max_cols
|
|
585
|
+
max_cols.positive? ? max_cols : 1
|
|
547
586
|
end
|
|
548
587
|
|
|
549
588
|
def render_table_colgroup(colgroup)
|
|
@@ -564,6 +603,7 @@ module Metanorma
|
|
|
564
603
|
@output << "<tr>"
|
|
565
604
|
walked = walk_ordered(tr) do |type, obj|
|
|
566
605
|
next unless type == :element
|
|
606
|
+
|
|
567
607
|
render_table_cell(obj)
|
|
568
608
|
end
|
|
569
609
|
unless walked
|
|
@@ -575,7 +615,7 @@ module Metanorma
|
|
|
575
615
|
end
|
|
576
616
|
|
|
577
617
|
def render_unordered_list(ul, **_opts)
|
|
578
|
-
attrs = element_attrs(id: safe_attr(ul, :id)
|
|
618
|
+
attrs = element_attrs(id: safe_attr(ul, :id))
|
|
579
619
|
tag("ul", attrs) do
|
|
580
620
|
ul.listitem&.each { |li| render_list_item(li) }
|
|
581
621
|
end
|
|
@@ -595,7 +635,7 @@ module Metanorma
|
|
|
595
635
|
end
|
|
596
636
|
|
|
597
637
|
def render_ordered_list(ol, **_opts)
|
|
598
|
-
attrs = element_attrs(id: safe_attr(ol, :id),
|
|
638
|
+
attrs = element_attrs(id: safe_attr(ol, :id), start: safe_attr(ol, :start), type: safe_attr(ol, :type_attr))
|
|
599
639
|
tag("ol", attrs) do
|
|
600
640
|
ol.listitem&.each { |li| render_list_item(li) }
|
|
601
641
|
end
|
|
@@ -634,7 +674,8 @@ module Metanorma
|
|
|
634
674
|
children = []
|
|
635
675
|
|
|
636
676
|
walk_ordered(section) do |type, obj|
|
|
637
|
-
next if
|
|
677
|
+
next if %i[text tab].include?(type)
|
|
678
|
+
|
|
638
679
|
children << obj
|
|
639
680
|
end
|
|
640
681
|
|
|
@@ -643,6 +684,7 @@ module Metanorma
|
|
|
643
684
|
supplementary_attrs.each do |attr|
|
|
644
685
|
val = safe_attr(section, attr)
|
|
645
686
|
next if val.nil?
|
|
687
|
+
|
|
646
688
|
Array(val).each do |v|
|
|
647
689
|
children << v unless children.include?(v)
|
|
648
690
|
end
|
|
@@ -665,7 +707,11 @@ module Metanorma
|
|
|
665
707
|
|
|
666
708
|
def sort_by_displayorder(children)
|
|
667
709
|
children.sort_by do |node|
|
|
668
|
-
order =
|
|
710
|
+
order = begin
|
|
711
|
+
node.displayorder
|
|
712
|
+
rescue StandardError
|
|
713
|
+
nil
|
|
714
|
+
end
|
|
669
715
|
order &&= order.to_i
|
|
670
716
|
order || Float::INFINITY
|
|
671
717
|
end
|
|
@@ -689,29 +735,8 @@ module Metanorma
|
|
|
689
735
|
end
|
|
690
736
|
|
|
691
737
|
def render_figure(figure, **_opts)
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
fig_name = safe_attr(figure, :fmt_name) || safe_attr(figure, :name)
|
|
695
|
-
if fig_id && fig_name
|
|
696
|
-
register_figure_entry(id: fig_id, text: extract_plain_text(fig_name))
|
|
697
|
-
end
|
|
698
|
-
tag("figure", attrs) do
|
|
699
|
-
if figure.image
|
|
700
|
-
render_image(figure.image)
|
|
701
|
-
elsif safe_attr(figure, :source)
|
|
702
|
-
@output << %(<img src="#{escape_html(figure.source)}" />)
|
|
703
|
-
end
|
|
704
|
-
render_video(figure.video) if safe_attr(figure, :video)
|
|
705
|
-
render_audio(figure.audio) if safe_attr(figure, :audio)
|
|
706
|
-
figure.figure&.each { |sub| render_figure(sub) }
|
|
707
|
-
if safe_attr(figure, :name) || safe_attr(figure, :fmt_name)
|
|
708
|
-
@output << "<figcaption>"
|
|
709
|
-
render_inline_element(safe_attr(figure, :fmt_name) || figure.name)
|
|
710
|
-
@output << "</figcaption>"
|
|
711
|
-
end
|
|
712
|
-
safe_attr(figure, :note)&.each { |n| render_note(n) }
|
|
713
|
-
safe_attr(figure, :dl)&.then { |dl| render_definition_list(dl) }
|
|
714
|
-
end
|
|
738
|
+
drop = Drops::FigureDrop.from_model(figure, renderer: renderer_context)
|
|
739
|
+
@output << render_liquid("_figure.html.liquid", { "block" => drop })
|
|
715
740
|
end
|
|
716
741
|
|
|
717
742
|
def render_image(image)
|
|
@@ -743,91 +768,23 @@ module Metanorma
|
|
|
743
768
|
end
|
|
744
769
|
|
|
745
770
|
def render_note(note, **_opts)
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
label = extract_block_label(note, "NOTE")
|
|
749
|
-
@output << %(<span class="note-label">#{escape_html(label)}</span> )
|
|
750
|
-
if note.content && !note.content.empty?
|
|
751
|
-
note.content.each { |para| render_paragraph(para) }
|
|
752
|
-
else
|
|
753
|
-
render_mixed_inline(note)
|
|
754
|
-
end
|
|
755
|
-
note.ul&.each { |ul| render_unordered_list(ul) }
|
|
756
|
-
note.ol&.each { |ol| render_ordered_list(ol) }
|
|
757
|
-
note.dl&.then { |dl| render_definition_list(dl) }
|
|
758
|
-
end
|
|
771
|
+
drop = Drops::NoteDrop.from_model(note, renderer: renderer_context)
|
|
772
|
+
@output << render_liquid("_note.html.liquid", { "block" => drop })
|
|
759
773
|
end
|
|
760
774
|
|
|
761
775
|
def render_example(example, **_opts)
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
label = extract_block_label(example, "EXAMPLE")
|
|
765
|
-
@output << %(<span class="example-label">#{escape_html(label)}</span> )
|
|
766
|
-
if example.paragraphs && !example.paragraphs.empty?
|
|
767
|
-
example.paragraphs.each { |para| render_paragraph(para) }
|
|
768
|
-
end
|
|
769
|
-
example.ul&.each { |ul| render_unordered_list(ul) }
|
|
770
|
-
example.ol&.each { |ol| render_ordered_list(ol) }
|
|
771
|
-
example.dl&.each { |dl| render_definition_list(dl) } if example.dl
|
|
772
|
-
example.sourcecode&.each { |sc| render_sourcecode(sc) }
|
|
773
|
-
example.table&.each { |t| render_table(t) }
|
|
774
|
-
example.figure&.each { |f| render_figure(f) }
|
|
775
|
-
example.quote&.each { |q| render_quote(q) }
|
|
776
|
-
example.formula&.each { |f| render_formula(f) }
|
|
777
|
-
end
|
|
776
|
+
drop = Drops::ExampleDrop.from_model(example, renderer: renderer_context)
|
|
777
|
+
@output << render_liquid("_example.html.liquid", { "block" => drop })
|
|
778
778
|
end
|
|
779
779
|
|
|
780
780
|
def render_sourcecode(sc, **_opts)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
tag("div", attrs) do
|
|
784
|
-
if sc.name
|
|
785
|
-
@output << "<p class=\"code-name\">"
|
|
786
|
-
render_inline_element(sc.name)
|
|
787
|
-
@output << "</p>"
|
|
788
|
-
end
|
|
789
|
-
code_attrs = lang ? %( lang="#{escape_html(lang)}") : ""
|
|
790
|
-
@output << "<pre><code#{code_attrs}>"
|
|
791
|
-
# Use body.content if available, else content, else text
|
|
792
|
-
code_text = if sc.body && sc.body.content
|
|
793
|
-
sc.body.content
|
|
794
|
-
elsif sc.content
|
|
795
|
-
sc.content
|
|
796
|
-
else
|
|
797
|
-
""
|
|
798
|
-
end
|
|
799
|
-
# body.content from map_all_content may contain pre-escaped HTML
|
|
800
|
-
# entities (< etc); decode first to get raw text, then escape
|
|
801
|
-
# for HTML output.
|
|
802
|
-
raw_text = code_text.gsub("<", "<").gsub(">", ">").gsub("&", "&").gsub(""", "\"")
|
|
803
|
-
@output << escape_html(raw_text)
|
|
804
|
-
@output << "</code></pre>"
|
|
805
|
-
end
|
|
781
|
+
drop = Drops::SourcecodeDrop.from_model(sc, renderer: renderer_context)
|
|
782
|
+
@output << render_liquid("_sourcecode.html.liquid", { "block" => drop })
|
|
806
783
|
end
|
|
807
784
|
|
|
808
785
|
def render_formula(formula, **_opts)
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
@output << render_stem_content(formula.stem) if formula.stem
|
|
812
|
-
|
|
813
|
-
# Render "where" clause from key element (non-presentation XML)
|
|
814
|
-
if formula.key
|
|
815
|
-
if formula.key.dl
|
|
816
|
-
@output << "<p class=\"formula-where\">where</p>"
|
|
817
|
-
render_definition_list(formula.key.dl)
|
|
818
|
-
end
|
|
819
|
-
formula.key.p&.each { |para| render_paragraph(para) }
|
|
820
|
-
end
|
|
821
|
-
|
|
822
|
-
formula.dl&.then { |dl| render_definition_list(dl) }
|
|
823
|
-
|
|
824
|
-
name_el = safe_attr(formula, :fmt_name) || safe_attr(formula, :name)
|
|
825
|
-
if name_el
|
|
826
|
-
@output << "<span class=\"formula-number\">"
|
|
827
|
-
render_inline_element(name_el)
|
|
828
|
-
@output << "</span>"
|
|
829
|
-
end
|
|
830
|
-
end
|
|
786
|
+
drop = Drops::FormulaDrop.from_model(formula, renderer: renderer_context)
|
|
787
|
+
@output << render_liquid("_formula.html.liquid", { "block" => drop })
|
|
831
788
|
end
|
|
832
789
|
|
|
833
790
|
def render_quote(quote, **_opts)
|
|
@@ -845,12 +802,8 @@ module Metanorma
|
|
|
845
802
|
end
|
|
846
803
|
|
|
847
804
|
def render_admonition(admonition, **_opts)
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
tag("div", attrs) do
|
|
851
|
-
@output << "<p class=\"admonition-title\">#{escape_html(type.capitalize)}</p>"
|
|
852
|
-
admonition.paragraphs&.each { |para| render_paragraph(para) }
|
|
853
|
-
end
|
|
805
|
+
drop = Drops::AdmonitionDrop.from_model(admonition, renderer: renderer_context)
|
|
806
|
+
@output << render_liquid("_admonition.html.liquid", { "block" => drop })
|
|
854
807
|
end
|
|
855
808
|
|
|
856
809
|
def render_bookmark(bookmark)
|
|
@@ -973,13 +926,14 @@ module Metanorma
|
|
|
973
926
|
skip = {}
|
|
974
927
|
node.element_order.each_with_index do |el, i|
|
|
975
928
|
next unless el.element?
|
|
929
|
+
|
|
976
930
|
next_tag = skip_after[el.name]
|
|
977
931
|
next unless next_tag
|
|
978
932
|
|
|
979
933
|
next_el = node.element_order[i + 1]
|
|
980
934
|
if next_tag.nil?
|
|
981
935
|
skip[i] = true
|
|
982
|
-
elsif next_el
|
|
936
|
+
elsif next_el&.element? && next_el.name == next_tag
|
|
983
937
|
skip[i] = true
|
|
984
938
|
end
|
|
985
939
|
end
|
|
@@ -1107,7 +1061,9 @@ module Metanorma
|
|
|
1107
1061
|
# Source element — skip; rendered via fmt-xref in semx wrapper
|
|
1108
1062
|
nil
|
|
1109
1063
|
when Metanorma::Document::Components::Inline::SpanElement
|
|
1110
|
-
|
|
1064
|
+
xml_class = safe_attr(element, :class_attr).to_s
|
|
1065
|
+
html_class = html_class_for_span(xml_class) unless xml_class.empty?
|
|
1066
|
+
attrs = element_attrs(style: safe_attr(element, :style), class: html_class)
|
|
1111
1067
|
tag("span", attrs) { render_mixed_inline(element) }
|
|
1112
1068
|
when Metanorma::Document::Components::Inline::FnElement
|
|
1113
1069
|
render_fn(element)
|
|
@@ -1183,13 +1139,13 @@ module Metanorma
|
|
|
1183
1139
|
texts = node.text
|
|
1184
1140
|
if texts.is_a?(Array)
|
|
1185
1141
|
texts.each do |t|
|
|
1186
|
-
if t.is_a?(Metanorma::Document::Components::Inline::MathElement)
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1142
|
+
@output << if t.is_a?(Metanorma::Document::Components::Inline::MathElement)
|
|
1143
|
+
t.content.to_s
|
|
1144
|
+
elsif t.is_a?(Metanorma::Document::Components::Inline::AsciimathElement)
|
|
1145
|
+
%(<span class="stem">#{escape_html(Array(t.text).join)}</span>)
|
|
1146
|
+
else
|
|
1147
|
+
escape_html(t.to_s)
|
|
1148
|
+
end
|
|
1193
1149
|
end
|
|
1194
1150
|
elsif texts.is_a?(String)
|
|
1195
1151
|
@output << escape_html(texts)
|
|
@@ -1239,6 +1195,7 @@ module Metanorma
|
|
|
1239
1195
|
display_attrs.each do |attr|
|
|
1240
1196
|
val = safe_attr(element, attr)
|
|
1241
1197
|
next if val.nil?
|
|
1198
|
+
|
|
1242
1199
|
if val.is_a?(Array)
|
|
1243
1200
|
val.each do |v|
|
|
1244
1201
|
if v.is_a?(Metanorma::Document::Components::Paragraphs::ParagraphBlock)
|
|
@@ -1261,7 +1218,7 @@ module Metanorma
|
|
|
1261
1218
|
return semx_text unless first_word
|
|
1262
1219
|
|
|
1263
1220
|
tail = output[-200..]
|
|
1264
|
-
return semx_text unless tail
|
|
1221
|
+
return semx_text unless tail&.rstrip&.end_with?(first_word)
|
|
1265
1222
|
|
|
1266
1223
|
semx_text.sub(/\A\s*#{Regexp.escape(first_word)}\s*/, "")
|
|
1267
1224
|
end
|
|
@@ -1279,7 +1236,7 @@ module Metanorma
|
|
|
1279
1236
|
doc.css("fmt-link").each do |el|
|
|
1280
1237
|
target = el["target"] || el["href"]
|
|
1281
1238
|
if target
|
|
1282
|
-
display_text = target.
|
|
1239
|
+
display_text = target.delete_prefix("mailto:")
|
|
1283
1240
|
a = doc.document.create_element("a", display_text, "href" => target)
|
|
1284
1241
|
el.replace(a)
|
|
1285
1242
|
else
|
|
@@ -1292,11 +1249,12 @@ module Metanorma
|
|
|
1292
1249
|
doc.traverse do |node|
|
|
1293
1250
|
next unless node.element?
|
|
1294
1251
|
next unless %w[xref eref stem link].include?(node.name)
|
|
1252
|
+
|
|
1295
1253
|
next_sib = node.next_sibling
|
|
1296
1254
|
while next_sib.is_a?(Nokogiri::XML::Text) && next_sib.text.strip.empty?
|
|
1297
1255
|
next_sib = next_sib.next_sibling
|
|
1298
1256
|
end
|
|
1299
|
-
next unless next_sib
|
|
1257
|
+
next unless next_sib&.element? && next_sib.name == "semx"
|
|
1300
1258
|
|
|
1301
1259
|
deduplicate_semx_label(node, next_sib)
|
|
1302
1260
|
node.remove
|
|
@@ -1305,6 +1263,10 @@ module Metanorma
|
|
|
1305
1263
|
%w[semx fmt-xref].each do |tag|
|
|
1306
1264
|
doc.css(tag).each { |el| el.replace(el.children) }
|
|
1307
1265
|
end
|
|
1266
|
+
# Remap XML class names to HTML-specific class names
|
|
1267
|
+
doc.css("[class]").each do |el|
|
|
1268
|
+
el["class"] = el["class"].split(/\s+/).map { |c| html_class_for_span(c) }.join(" ")
|
|
1269
|
+
end
|
|
1308
1270
|
doc.inner_html
|
|
1309
1271
|
end
|
|
1310
1272
|
|
|
@@ -1328,13 +1290,13 @@ module Metanorma
|
|
|
1328
1290
|
|
|
1329
1291
|
def render_link(link)
|
|
1330
1292
|
target = safe_attr(link, :target) || safe_attr(link, :href)
|
|
1331
|
-
attrs = element_attrs(href: target, id: safe_attr(link, :id)
|
|
1293
|
+
attrs = element_attrs(href: target, id: safe_attr(link, :id))
|
|
1332
1294
|
tag("a", attrs) do
|
|
1333
1295
|
content = safe_attr(link, :content)
|
|
1334
1296
|
if content && !Array(content).join.strip.empty?
|
|
1335
1297
|
render_mixed_inline(link)
|
|
1336
1298
|
else
|
|
1337
|
-
display_text = target.to_s.
|
|
1299
|
+
display_text = target.to_s.delete_prefix("mailto:")
|
|
1338
1300
|
@output << escape_html(display_text)
|
|
1339
1301
|
end
|
|
1340
1302
|
end
|
|
@@ -1342,7 +1304,7 @@ module Metanorma
|
|
|
1342
1304
|
|
|
1343
1305
|
def render_xref(xref)
|
|
1344
1306
|
target = safe_attr(xref, :target) || safe_attr(xref, :to_attr)
|
|
1345
|
-
attrs = element_attrs(href: "##{escape_html(target)}", id: safe_attr(xref, :id)
|
|
1307
|
+
attrs = element_attrs(href: "##{escape_html(target)}", id: safe_attr(xref, :id))
|
|
1346
1308
|
tag("a", attrs) { render_mixed_inline(xref) }
|
|
1347
1309
|
end
|
|
1348
1310
|
|
|
@@ -1460,14 +1422,13 @@ module Metanorma
|
|
|
1460
1422
|
attrs.each do |k, v|
|
|
1461
1423
|
next if v.nil? || v == false || (v.is_a?(String) && v.empty?)
|
|
1462
1424
|
|
|
1463
|
-
|
|
1464
|
-
parts << %( #{k}="#{escape_html(val)}")
|
|
1425
|
+
parts << %( #{k}="#{escape_html(v.to_s)}")
|
|
1465
1426
|
end
|
|
1466
1427
|
parts.join
|
|
1467
1428
|
end
|
|
1468
1429
|
|
|
1469
|
-
def
|
|
1470
|
-
|
|
1430
|
+
def html_class_for_span(xml_class)
|
|
1431
|
+
SPAN_ROLE_CLASSES[xml_class] || "span-#{xml_class}"
|
|
1471
1432
|
end
|
|
1472
1433
|
|
|
1473
1434
|
def block_element?(obj)
|
|
@@ -1503,7 +1464,7 @@ module Metanorma
|
|
|
1503
1464
|
secondary: safe_attr(element, :secondary)&.to_s&.strip,
|
|
1504
1465
|
tertiary: safe_attr(element, :tertiary)&.to_s&.strip,
|
|
1505
1466
|
target_id: @current_section_id,
|
|
1506
|
-
target_text: @current_section_number
|
|
1467
|
+
target_text: @current_section_number,
|
|
1507
1468
|
)
|
|
1508
1469
|
rescue StandardError
|
|
1509
1470
|
nil
|