metanorma-document 0.3.0 → 0.4.0

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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +3 -2
  3. data/Rakefile +30 -5
  4. data/frontend/dist/app.css +1 -0
  5. data/frontend/dist/app.iife.js +24 -0
  6. data/frontend/package-lock.json +7 -505
  7. data/frontend/tailwind.config.js +5 -0
  8. data/lib/metanorma/csa_document/root.rb +27 -0
  9. data/lib/metanorma/csa_document.rb +9 -0
  10. data/lib/metanorma/document/cli.rb +52 -0
  11. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +14 -0
  12. data/lib/metanorma/document/components/inline/comma_element.rb +3 -0
  13. data/lib/metanorma/document/components/inline/enum_comma_element.rb +3 -0
  14. data/lib/metanorma/document/components/inline/eref_element.rb +4 -1
  15. data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +4 -2
  16. data/lib/metanorma/document/components/inline/math_element.rb +13 -7
  17. data/lib/metanorma/document/components/inline/vocabulary.rb +4 -0
  18. data/lib/metanorma/document/components/multi_paragraph/admonition_block.rb +6 -1
  19. data/lib/metanorma/document/components/paragraphs/paragraph_block.rb +3 -0
  20. data/lib/metanorma/document/components/reference_elements/callout.rb +3 -2
  21. data/lib/metanorma/document/components/tables/table_cell.rb +3 -0
  22. data/lib/metanorma/document/elements/input.rb +44 -0
  23. data/lib/metanorma/document/elements.rb +1 -0
  24. data/lib/metanorma/document/relaton/address.rb +5 -0
  25. data/lib/metanorma/document/relaton/affiliation.rb +3 -0
  26. data/lib/metanorma/document/relaton/bib_item_size.rb +8 -9
  27. data/lib/metanorma/document/relaton/bib_item_type.rb +3 -0
  28. data/lib/metanorma/document/relaton/bibliographic_date.rb +5 -0
  29. data/lib/metanorma/document/relaton/contact_method.rb +5 -0
  30. data/lib/metanorma/document/relaton/contribution_info.rb +6 -2
  31. data/lib/metanorma/document/relaton/contributor.rb +4 -0
  32. data/lib/metanorma/document/relaton/contributor_role.rb +3 -0
  33. data/lib/metanorma/document/relaton/copyright_association.rb +3 -0
  34. data/lib/metanorma/document/relaton/copyright_owner.rb +3 -0
  35. data/lib/metanorma/document/relaton/date_time.rb +3 -0
  36. data/lib/metanorma/document/relaton/document_identifier.rb +4 -0
  37. data/lib/metanorma/document/relaton/document_relation.rb +8 -0
  38. data/lib/metanorma/document/relaton/document_status.rb +4 -0
  39. data/lib/metanorma/document/relaton/formatted_address.rb +4 -0
  40. data/lib/metanorma/document/relaton/full_name.rb +14 -3
  41. data/lib/metanorma/document/relaton/keyword_type.rb +3 -0
  42. data/lib/metanorma/document/relaton/locality_stack.rb +6 -9
  43. data/lib/metanorma/document/relaton/localized_name.rb +8 -5
  44. data/lib/metanorma/document/relaton/logo_element.rb +5 -0
  45. data/lib/metanorma/document/relaton/medium_type.rb +5 -16
  46. data/lib/metanorma/document/relaton/org_identifier.rb +2 -0
  47. data/lib/metanorma/document/relaton/org_subdivision.rb +2 -0
  48. data/lib/metanorma/document/relaton/org_subdivision_identifier.rb +2 -0
  49. data/lib/metanorma/document/relaton/organization.rb +3 -0
  50. data/lib/metanorma/document/relaton/person.rb +7 -1
  51. data/lib/metanorma/document/relaton/person_identifier.rb +3 -0
  52. data/lib/metanorma/document/relaton/place_type.rb +5 -0
  53. data/lib/metanorma/document/relaton/region_element.rb +3 -9
  54. data/lib/metanorma/document/relaton/relaton_collection.rb +2 -0
  55. data/lib/metanorma/document/relaton/series_type.rb +7 -0
  56. data/lib/metanorma/document/relaton/typed_note.rb +4 -0
  57. data/lib/metanorma/document/relaton/typed_title_string.rb +4 -0
  58. data/lib/metanorma/document/relaton/typed_uri.rb +3 -8
  59. data/lib/metanorma/document/relaton/validity_type.rb +8 -10
  60. data/lib/metanorma/document/relaton/variant_full_name.rb +3 -0
  61. data/lib/metanorma/document/relaton/variant_org_name.rb +3 -0
  62. data/lib/metanorma/document/relaton/version_info.rb +5 -0
  63. data/lib/metanorma/document/relaton/vocab_id_type.rb +6 -12
  64. data/lib/metanorma/document/relaton.rb +0 -14
  65. data/lib/metanorma/document/version.rb +1 -1
  66. data/lib/metanorma/document.rb +1 -0
  67. data/lib/metanorma/html/base_renderer.rb +86 -234
  68. data/lib/metanorma/html/concerns/metadata_extraction.rb +61 -0
  69. data/lib/metanorma/html/concerns/presentation_validation.rb +61 -0
  70. data/lib/metanorma/html/concerns/svg_processing.rb +32 -0
  71. data/lib/metanorma/html/concerns/text_extraction.rb +93 -0
  72. data/lib/metanorma/html/concerns/toc_registry.rb +47 -0
  73. data/lib/metanorma/html/csa_renderer.rb +9 -0
  74. data/lib/metanorma/html/drops/figure_drop.rb +33 -10
  75. data/lib/metanorma/html/drops/sourcecode_drop.rb +3 -6
  76. data/lib/metanorma/html/flavor.rb +6 -1
  77. data/lib/metanorma/html/generator.rb +8 -1
  78. data/lib/metanorma/html/iso_renderer.rb +0 -142
  79. data/lib/metanorma/html/renderers/block_renderer.rb +10 -0
  80. data/lib/metanorma/html/renderers/element_order_traversal.rb +15 -1
  81. data/lib/metanorma/html/renderers/inline_renderer.rb +13 -75
  82. data/lib/metanorma/html/standard_renderer.rb +195 -35
  83. data/lib/metanorma/html.rb +1 -51
  84. data/lib/metanorma/iso_document/sections/iso_preface.rb +27 -13
  85. data/lib/metanorma/mirror/default_registry.rb +21 -0
  86. data/lib/metanorma/mirror/handlers/inline/catalog.rb +7 -12
  87. data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +2 -3
  88. data/lib/metanorma/mirror/handlers/sourcecode.rb +1 -1
  89. data/lib/metanorma/mirror/model/guide.rb +3 -2
  90. data/lib/metanorma/mirror/output/formats/inline_format.rb +64 -3
  91. data/lib/metanorma/mirror/output/pipeline.rb +1 -0
  92. data/lib/metanorma/mirror/output.rb +0 -2
  93. data/lib/metanorma/mirror/rewriter.rb +3 -1
  94. data/lib/metanorma/registers/setup.rb +10 -0
  95. data/lib/metanorma/standard_document/annotation_container.rb +2 -0
  96. data/lib/metanorma/standard_document/elements/input.rb +4 -25
  97. data/lib/metanorma/standard_document/metadata/metanorma_extension.rb +12 -2
  98. data/lib/metanorma/standard_document/metadata/presentation_metadata.rb +107 -0
  99. data/lib/metanorma/standard_document/metadata/semantic_metadata.rb +18 -0
  100. data/lib/metanorma/standard_document/metadata/unitsml/amount_of_substance.rb +19 -0
  101. data/lib/metanorma/standard_document/metadata/unitsml/base_quantity.rb +34 -0
  102. data/lib/metanorma/standard_document/metadata/unitsml/dimension.rb +39 -0
  103. data/lib/metanorma/standard_document/metadata/unitsml/dimension_set.rb +19 -0
  104. data/lib/metanorma/standard_document/metadata/unitsml/electric_current.rb +19 -0
  105. data/lib/metanorma/standard_document/metadata/unitsml/enumerated_root_unit.rb +23 -0
  106. data/lib/metanorma/standard_document/metadata/unitsml/length.rb +19 -0
  107. data/lib/metanorma/standard_document/metadata/unitsml/luminous_intensity.rb +19 -0
  108. data/lib/metanorma/standard_document/metadata/unitsml/mass.rb +19 -0
  109. data/lib/metanorma/standard_document/metadata/unitsml/namespace.rb +17 -0
  110. data/lib/metanorma/standard_document/metadata/unitsml/plane_angle.rb +19 -0
  111. data/lib/metanorma/standard_document/metadata/unitsml/prefix.rb +30 -0
  112. data/lib/metanorma/standard_document/metadata/unitsml/prefix_name.rb +21 -0
  113. data/lib/metanorma/standard_document/metadata/unitsml/prefix_set.rb +19 -0
  114. data/lib/metanorma/standard_document/metadata/unitsml/prefix_symbol.rb +21 -0
  115. data/lib/metanorma/standard_document/metadata/unitsml/quantity.rb +28 -0
  116. data/lib/metanorma/standard_document/metadata/unitsml/quantity_name.rb +21 -0
  117. data/lib/metanorma/standard_document/metadata/unitsml/quantity_set.rb +19 -0
  118. data/lib/metanorma/standard_document/metadata/unitsml/root_units.rb +19 -0
  119. data/lib/metanorma/standard_document/metadata/unitsml/thermodynamic_temperature.rb +19 -0
  120. data/lib/metanorma/standard_document/metadata/unitsml/time.rb +19 -0
  121. data/lib/metanorma/standard_document/metadata/unitsml/unit.rb +32 -0
  122. data/lib/metanorma/standard_document/metadata/unitsml/unit_name.rb +21 -0
  123. data/lib/metanorma/standard_document/metadata/unitsml/unit_set.rb +19 -0
  124. data/lib/metanorma/standard_document/metadata/unitsml/unit_symbol.rb +36 -0
  125. data/lib/metanorma/standard_document/metadata/unitsml/unit_system.rb +23 -0
  126. data/lib/metanorma/standard_document/metadata/unitsml/unitsml_root.rb +28 -0
  127. data/lib/metanorma/standard_document/metadata/unitsml.rb +39 -0
  128. data/lib/metanorma/standard_document/metadata.rb +4 -0
  129. data/lib/metanorma/standard_document/sections/misc_container.rb +8 -6
  130. data/lib/metanorma/standard_document/sections/sections.rb +7 -0
  131. data/lib/metanorma/un_document/blocks/un_admonition_block.rb +86 -0
  132. data/lib/metanorma/un_document/blocks.rb +9 -0
  133. data/lib/metanorma/un_document/root.rb +8 -0
  134. data/lib/metanorma/un_document/sections/un_abstract_section.rb +48 -0
  135. data/lib/metanorma/un_document/sections/un_preface.rb +19 -2
  136. data/lib/metanorma/un_document/sections.rb +1 -0
  137. data/lib/metanorma/un_document/un_text_element.rb +116 -0
  138. data/lib/metanorma/un_document.rb +2 -0
  139. data/script/benchmark_render.rb +35 -0
  140. data/tasks/roundtrip_samples.rake +1 -1
  141. metadata +63 -45
  142. data/.rubocop_todo.yml +0 -220
  143. data/TODO.finalize/01-split-multi-class-files.md +0 -8
  144. data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +0 -6
  145. data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +0 -11
  146. data/TODO.finalize/04-theme-per-flavor-config-driven.md +0 -9
  147. data/TODO.finalize/05-migrate-all-html-to-liquid.md +0 -16
  148. data/TODO.finalize/06-integrate-pubid-library.md +0 -16
  149. data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +0 -39
  150. data/TODO.finalize/08-refactor-renderer-architecture.md +0 -25
  151. data/TODO.finalize/09-eliminate-code-smells.md +0 -14
  152. data/TODO.finalize/10-comprehensive-spec-coverage.md +0 -24
  153. data/TODO.finalize/11-config-driven-flavor-metadata.md +0 -20
  154. data/TODO.finalize/12-add-frozen-string-literal.md +0 -6
  155. data/TODO.finalize/13-extract-composition-from-god-class.md +0 -96
  156. data/TODO.finalize/14-config-driven-section-ordering.md +0 -31
  157. data/TODO.finalize/15-theme-directories-per-flavor.md +0 -46
  158. data/TODO.finalize/16-theme-lutaml-model-schema.md +0 -44
  159. data/docs/html-renderer.adoc +0 -261
  160. data/lib/data/dist/iso_document/frontend/assets/index-mwzbzmnK.js +0 -22
  161. data/lib/data/dist/iso_document/frontend/assets/index-pGvKTNid.css +0 -1
  162. data/lib/data/dist/iso_document/frontend/assets/iso-red.svg +0 -53
  163. data/lib/data/dist/iso_document/frontend/index.html +0 -13
  164. data/lib/metanorma/document/relaton/bib_item_size_type.rb +0 -21
  165. data/lib/metanorma/document/relaton/bibliographic_date_type.rb +0 -22
  166. data/lib/metanorma/document/relaton/document_relation_type.rb +0 -31
  167. data/lib/metanorma/document/relaton/iso4217_code.rb +0 -17
  168. data/lib/metanorma/document/relaton/iso8601_date.rb +0 -17
  169. data/lib/metanorma/document/relaton/personal_identifier_type.rb +0 -21
  170. data/lib/metanorma/document/relaton/region_type.rb +0 -20
  171. data/lib/metanorma/document/relaton/series_type_type.rb +0 -21
  172. data/lib/metanorma/document/relaton/specific_locality_type.rb +0 -25
  173. data/lib/metanorma/document/relaton/title_type.rb +0 -21
  174. data/lib/metanorma/mirror/output/html_renderer.rb +0 -95
  175. data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +0 -178
  176. data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +0 -61
  177. data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +0 -96
  178. data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +0 -41
  179. data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +0 -129
  180. data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +0 -80
  181. data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +0 -87
  182. data/lib/metanorma/mirror/output/html_renderers.rb +0 -76
  183. data/lib/metanorma/standard_document/annotation.rb +0 -37
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ module Concerns
6
+ # Table-of-contents entry collection and ToC rendering, mixed into
7
+ # BaseRenderer. Sub-renderers register section/figure/table entries
8
+ # as they walk the document; assemble_document renders the collected
9
+ # entries through the _toc.html.liquid template.
10
+ module TocRegistry
11
+ def toc_entries
12
+ @toc_entries
13
+ end
14
+
15
+ def build_toc_html(entries)
16
+ entry_drops = entries.map { |e| Drops::TocEntryDrop.new(e) }
17
+ figure_drops = @figure_entries.map { |f| Drops::FigureListEntryDrop.new(f) }
18
+ table_drops = @table_entries.map { |t| Drops::FigureListEntryDrop.new(t) }
19
+ has_special_lists = !@figure_entries.empty? || !@table_entries.empty?
20
+
21
+ render_liquid("_toc.html.liquid", {
22
+ "entries" => entry_drops,
23
+ "figures" => figure_drops,
24
+ "tables" => table_drops,
25
+ "has_special_lists" => has_special_lists,
26
+ })
27
+ end
28
+
29
+ def register_toc_entry(id:, level:, text:)
30
+ @toc_entries << { id: id, level: level, text: text }
31
+ end
32
+
33
+ def register_figure_entry(id:, text:)
34
+ @figure_entries << { id: id, text: text }
35
+ end
36
+
37
+ def figure_entries
38
+ @figure_entries
39
+ end
40
+
41
+ def register_table_entry(id:, text:)
42
+ @table_entries << { id: id, text: text }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ class CsaRenderer < IsoRenderer
6
+ register_render Metanorma::CsaDocument::Root, :render_document
7
+ end
8
+ end
9
+ end
@@ -47,24 +47,47 @@ module Metanorma
47
47
  end
48
48
  sub_figures_html = sub_figures_parts.join
49
49
 
50
- key_parts = []
51
- renderer.safe_attr(figure, :note)&.each do |n|
52
- key_parts << (renderer.render_note(n) || "")
53
- end
54
- renderer.safe_attr(figure, :dl)&.then do |dl|
55
- key_parts << (renderer.render_definition_list(dl) || "")
56
- end
57
- key_html = key_parts.join
58
-
59
50
  new(
60
51
  id: id,
61
52
  image_html: image_html,
62
53
  caption_html: caption_html,
63
- key_html: key_html,
54
+ key_html: build_key_html(figure, renderer),
64
55
  sub_figures_html: sub_figures_html,
65
56
  css_class: "figure",
66
57
  )
67
58
  end
59
+
60
+ class << self
61
+ private
62
+
63
+ # Notes, plain definition lists, and figure.key (e.g.
64
+ # <key class="formula_dl">) — symbol/footnote definitions
65
+ # belonging to the figure.
66
+ def build_key_html(figure, renderer)
67
+ parts = []
68
+ renderer.safe_attr(figure, :note)&.each do |n|
69
+ parts << (renderer.render_note(n) || "")
70
+ end
71
+ renderer.safe_attr(figure, :dl)&.then do |dl|
72
+ parts << (renderer.render_definition_list(dl) || "")
73
+ end
74
+ append_figure_key(parts, renderer.safe_attr(figure, :key), renderer)
75
+ parts.join
76
+ end
77
+
78
+ def append_figure_key(parts, key, renderer)
79
+ return unless key
80
+
81
+ name = renderer.safe_attr(key, :name)
82
+ parts << (renderer.render_inline_element(name) || "") if name
83
+ if renderer.safe_attr(key, :dl)
84
+ parts << (renderer.render_definition_list(key.dl) || "")
85
+ end
86
+ renderer.safe_attr(key, :p)&.each do |para|
87
+ parts << (renderer.render_paragraph(para) || "")
88
+ end
89
+ end
90
+ end
68
91
  end
69
92
  end
70
93
  end
@@ -23,22 +23,19 @@ css_class: nil)
23
23
  renderer.render_inline_element(sc.name)
24
24
  end
25
25
 
26
- code_text = if sc.body&.content
27
- Array(sc.body.content).join
26
+ code_text = if sc.body
27
+ sc.body.decoded_content
28
28
  elsif sc.content
29
29
  Array(sc.content).join
30
30
  else
31
31
  ""
32
32
  end
33
- raw_text = code_text.gsub("&lt;", "<").gsub("&gt;", ">").gsub("&amp;", "&").gsub(
34
- "&quot;", "\""
35
- )
36
33
 
37
34
  new(
38
35
  id: id,
39
36
  lang: lang,
40
37
  name_html: name_html,
41
- code_html: renderer.escape_html(raw_text),
38
+ code_html: renderer.escape_html(code_text),
42
39
  css_class: "sourcecode",
43
40
  )
44
41
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pubid"
4
+
3
5
  module Metanorma
4
6
  module Html
5
7
  # A flavor ties together the four concepts that identify a Metanorma
@@ -38,7 +40,10 @@ module Metanorma
38
40
 
39
41
  Object.const_get(pubid_module.to_s)
40
42
  rescue NameError
41
- nil
43
+ raise ArgumentError,
44
+ "Flavor #{name.inspect}: pubid module #{pubid_module} could " \
45
+ "not be resolved — fix the registration in " \
46
+ "Html::Generator.build_flavor_registry"
42
47
  end
43
48
  end
44
49
  end
@@ -87,6 +87,7 @@ module Metanorma
87
87
  IsoRenderer
88
88
  BipmRenderer
89
89
  CcRenderer
90
+ CsaRenderer
90
91
  IccRenderer
91
92
  IecRenderer
92
93
  IeeeRenderer
@@ -136,6 +137,12 @@ module Metanorma
136
137
  model_class: Metanorma::CcDocument::Root,
137
138
  renderer_class: CcRenderer,
138
139
  ))
140
+ registry.register(Flavor.new(
141
+ name: :csa,
142
+ model_class: Metanorma::CsaDocument::Root,
143
+ renderer_class: CsaRenderer,
144
+ pubid_module: :"Pubid::Csa",
145
+ ))
139
146
  registry.register(Flavor.new(
140
147
  name: :iec,
141
148
  model_class: Metanorma::IecDocument::Root,
@@ -163,7 +170,7 @@ module Metanorma
163
170
  name: :itu,
164
171
  model_class: Metanorma::ItuDocument::Root,
165
172
  renderer_class: ItuRenderer,
166
- pubid_module: :"Pubid::Ithu",
173
+ pubid_module: :"Pubid::Itu",
167
174
  ))
168
175
  registry.register(Flavor.new(
169
176
  name: :ogc,
@@ -330,148 +330,6 @@ module Metanorma
330
330
 
331
331
  # --- ISO Term rendering ---
332
332
 
333
- def render_term(term, **_opts)
334
- term_name = extract_term_name(term)
335
- term_def = extract_term_definition(term)
336
- data_attrs = {}
337
- if term_name && !term_name.empty?
338
- data_attrs["data-term-name"] = term_name
339
- end
340
- if term_def && !term_def.empty?
341
- data_attrs["data-term-definition"] = term_def
342
- end
343
- attrs = element_attrs(id: safe_attr(term, :id), **data_attrs)
344
- fmt_name = safe_attr(term, :fmt_name)
345
- fmt_preferred = safe_attr(term, :fmt_preferred)
346
- fmt_admitted = safe_attr(term, :fmt_admitted)
347
- fmt_deprecates = safe_attr(term, :fmt_deprecates)
348
- fmt_definition = safe_attr(term, :fmt_definition)
349
- fmt_termsource = safe_attr(term, :fmt_termsource)
350
- term_number = safe_attr(term, :term_number)
351
- term_p = safe_attr(term, :p)
352
- term_note = safe_attr(term, :termnote) || Array(safe_attr(term, :note))
353
- term_example = safe_attr(term,
354
- :termexample) || Array(safe_attr(term,
355
- :example))
356
- term_termsource = safe_attr(term, :termsource)
357
- term_admonition = safe_attr(term, :admonition)
358
- term_nested = safe_attr(term, :term)
359
-
360
- parts = []
361
- if fmt_name
362
- tn_content = render_inline_element(fmt_name)
363
- parts << render_liquid("_term_number.html.liquid",
364
- { "content" => tn_content })
365
- elsif term_number
366
- tn_text = if term_number.is_a?(String)
367
- term_number
368
- else
369
- extract_text_value(term_number)
370
- end
371
- parts << render_liquid("_term_number.html.liquid",
372
- { "content" => escape_html(tn_text) })
373
- end
374
-
375
- if fmt_preferred && !fmt_preferred.empty?
376
- fmt_preferred.each do |fp|
377
- fp.p&.each { |para| parts << (render_paragraph(para) || "") }
378
- end
379
- elsif term.preferred && !term.preferred.empty?
380
- term.preferred&.each do |designation|
381
- parts << (render_term_designation(designation, "preferred") || "")
382
- end
383
- end
384
-
385
- if fmt_admitted && !fmt_admitted.empty?
386
- fmt_admitted.each do |fa|
387
- fa.p&.each { |para| parts << (render_paragraph(para) || "") }
388
- end
389
- elsif term.admitted && !term.admitted.empty?
390
- term.admitted&.each do |designation|
391
- parts << (render_term_designation(designation, "admitted") || "")
392
- end
393
- end
394
-
395
- if fmt_deprecates
396
- fmt_deprecates.p&.each do |para|
397
- parts << (render_paragraph(para) || "")
398
- end
399
- elsif term.deprecates && !term.deprecates.empty?
400
- term.deprecates&.each do |designation|
401
- parts << (render_term_designation(designation, "deprecated") || "")
402
- end
403
- end
404
-
405
- if term.domain && !fmt_definition
406
- domain_text = safe_attr(term.domain, :text)
407
- if domain_text
408
- parts << render_liquid("_term_domain.html.liquid", {
409
- "text" => escape_html(domain_text),
410
- })
411
- end
412
- end
413
-
414
- if fmt_definition
415
- parts << (render_ordered_content(fmt_definition) || "")
416
- else
417
- term_p&.each { |para| parts << (render_paragraph(para) || "") }
418
- term.definition&.each do |defn|
419
- parts << (render_term_definition(defn) || "")
420
- end
421
- end
422
-
423
- term_note&.each { |note| parts << (render_term_note(note) || "") }
424
- term_example&.each { |ex| parts << (render_term_example(ex) || "") }
425
-
426
- if fmt_termsource && !fmt_termsource.empty?
427
- fmt_termsource.each do |fts|
428
- src_content = render_mixed_inline(fts)
429
- parts << render_liquid("_paragraph.html.liquid", {
430
- "attrs" => " class=\"term-source\"",
431
- "content" => src_content,
432
- })
433
- end
434
- else
435
- term.source&.each { |src| parts << (render_term_source(src) || "") }
436
- term_termsource&.each do |src|
437
- parts << (render_term_source_element(src) || "")
438
- end
439
- end
440
-
441
- term_admonition&.each { |adm| parts << (render_admonition(adm) || "") }
442
- term_nested&.each { |sub| parts << (render_term(sub) || "") }
443
-
444
- render_liquid("_element.html.liquid", "tag" => "div",
445
- "extra_attrs" => attrs, "content" => parts.join)
446
- end
447
-
448
- def extract_term_name(term)
449
- fmt_pref = safe_attr(term, :fmt_preferred)
450
- if fmt_pref && !fmt_pref.empty?
451
- fp = fmt_pref.first
452
- if fp.p && !fp.p.empty?
453
- return extract_plain_text(fp.p.first)
454
- end
455
- end
456
- if term.preferred && !term.preferred.empty?
457
- return extract_designation_name(term.preferred.first).to_s
458
- end
459
-
460
- safe_attr(term, :id).to_s.delete_prefix("term-")
461
- end
462
-
463
- def extract_term_definition(term)
464
- fmt_def = safe_attr(term, :fmt_definition)
465
- return extract_plain_text(fmt_def) if fmt_def
466
-
467
- term_p = safe_attr(term, :p)
468
- if term_p && !term_p.empty?
469
- text = term_p.map { |para| extract_plain_text(para) }.join(" ")
470
- return text.strip unless text.strip.empty?
471
- end
472
- nil
473
- end
474
-
475
333
  def render_term_designation(designation, type)
476
334
  css_class = type == "deprecated" ? "term-deprecated" : "term-name"
477
335
  name = extract_designation_name(designation)
@@ -271,6 +271,16 @@ module Metanorma
271
271
  render_liquid("_example.html.liquid", { "block" => drop })
272
272
  end
273
273
 
274
+ def render_form(form, **_opts)
275
+ attrs = element_attrs(id: safe_attr(form, :id), class: "form")
276
+ content = coordinator.render_ordered_content(form)
277
+ render_liquid("_element.html.liquid", {
278
+ "tag" => "div",
279
+ "extra_attrs" => attrs,
280
+ "content" => content,
281
+ })
282
+ end
283
+
274
284
  def render_sourcecode(sc, **_opts)
275
285
  drop = Drops::SourcecodeDrop.from_model(sc,
276
286
  renderer: coordinator.renderer_context)
@@ -15,10 +15,24 @@ module Metanorma
15
15
  module ElementOrderTraversal
16
16
  module_function
17
17
 
18
+ # The map depends only on the model class's xml mapping, which is
19
+ # immutable after class definition — cache one map per mapping
20
+ # object instead of rebuilding it for every rendered node.
21
+ @cache = {}.compare_by_identity
22
+
23
+ def element_to_attr_map(xml_mapping)
24
+ @cache[xml_mapping] ||= build_map(xml_mapping)
25
+ end
26
+
27
+ # Test/benchmark hook: drop all memoized maps.
28
+ def reset_cache!
29
+ @cache.clear
30
+ end
31
+
18
32
  # Returns `{ xml_element_name => attr_name }` including both
19
33
  # symbol and string keys for each mapped element, so callers
20
34
  # can look up by either el.name form.
21
- def element_to_attr_map(xml_mapping)
35
+ def build_map(xml_mapping)
22
36
  {}.tap do |map|
23
37
  xml_mapping.mapping_elements_hash.each_value do |rule_or_array|
24
38
  Array(rule_or_array).each do |rule|
@@ -104,13 +104,6 @@ module Metanorma
104
104
  end
105
105
 
106
106
  def render_mixed_inline(node)
107
- if raw_content_node?(node)
108
- raw = node.content
109
- if raw.is_a?(String) && !raw.strip.empty?
110
- return render_raw_content(raw)
111
- end
112
- end
113
-
114
107
  if node.is_a?(Lutaml::Model::Serializable) && node.element_order && !node.element_order.empty?
115
108
  render_ordered_inline(node)
116
109
  elsif node.is_a?(Lutaml::Model::Serializable)
@@ -129,13 +122,6 @@ module Metanorma
129
122
  end
130
123
  end
131
124
 
132
- def raw_content_node?(node)
133
- node.is_a?(Lutaml::Model::Serializable) &&
134
- node.content.is_a?(String)
135
- rescue NoMethodError
136
- false
137
- end
138
-
139
125
  def render_ordered_inline(node)
140
126
  parts = []
141
127
  walked = walk_ordered(node) do |type, obj|
@@ -243,8 +229,19 @@ module Metanorma
243
229
  ", "
244
230
  end
245
231
 
232
+ # Form inputs render as disabled HTML inputs — this is a document
233
+ # rendering, not an interactive form.
234
+ def render_input(el)
235
+ attrs = element_attrs(
236
+ type: safe_attr(el, :type) || "text",
237
+ disabled: "disabled",
238
+ checked: safe_attr(el, :checked) ? "checked" : nil,
239
+ )
240
+ "<input#{attrs}>"
241
+ end
242
+
246
243
  def render_math(el)
247
- el.content.to_s
244
+ el.to_xml
248
245
  end
249
246
 
250
247
  def render_asciimath(el)
@@ -374,65 +371,6 @@ module Metanorma
374
371
  })
375
372
  end
376
373
 
377
- def render_raw_content(raw_xml)
378
- doc = Nokogiri::XML.fragment(raw_xml)
379
- doc.css("fmt-link").each do |el|
380
- target = el["target"] || el["href"]
381
- if target
382
- display_text = target.delete_prefix("mailto:")
383
- a = doc.document.create_element("a", display_text,
384
- "href" => target)
385
- el.replace(a)
386
- else
387
- el.replace(el.children)
388
- end
389
- end
390
- doc.traverse do |node|
391
- next unless node.element?
392
- next unless %w[xref eref stem link].include?(node.name)
393
-
394
- next_sib = node.next_sibling
395
- while next_sib.is_a?(Nokogiri::XML::Text) && next_sib.text.strip.empty?
396
- next_sib = next_sib.next_sibling
397
- end
398
- next unless next_sib&.element? && next_sib.name == "semx"
399
-
400
- deduplicate_semx_label(node, next_sib)
401
- node.remove
402
- end
403
- %w[semx fmt-xref].each do |tag|
404
- doc.css(tag).each { |el| el.replace(el.children) }
405
- end
406
- doc.css("[class]").each do |el|
407
- el["class"] = el["class"].split(/\s+/).map do |c|
408
- html_class_for_span(c)
409
- end.join(" ")
410
- end
411
- doc.inner_html
412
- end
413
-
414
- def deduplicate_semx_label(source_node, semx_node)
415
- first_text = semx_node.children.find do |c|
416
- c.text? && !c.text.strip.empty?
417
- end
418
- return unless first_text
419
-
420
- semx_prefix = first_text.text[/\A(\s*\S+)/, 1]
421
- return unless semx_prefix && !semx_prefix.strip.empty?
422
-
423
- prev = source_node.previous_sibling
424
- return unless prev.is_a?(Nokogiri::XML::Text)
425
-
426
- label = semx_prefix.strip
427
- prev_text = prev.text.rstrip
428
- return unless prev_text.end_with?(label)
429
-
430
- prev.content = prev_text.sub(/#{Regexp.escape(label)}\s*\z/, "")
431
- first_text.content = first_text.text.sub(
432
- /\A\s*#{Regexp.escape(label)}\s*/, ""
433
- )
434
- end
435
-
436
374
  def render_link(link)
437
375
  target = safe_attr(link, :target) || safe_attr(link, :href)
438
376
  attrs = element_attrs(href: target, id: safe_attr(link, :id))
@@ -517,7 +455,7 @@ module Metanorma
517
455
  end
518
456
 
519
457
  if math_items.any?
520
- content = math_items.map { |m| m.content.to_s }.join
458
+ content = math_items.map(&:to_xml).join
521
459
  unless content.empty?
522
460
  return render_liquid("_math_container.html.liquid", {
523
461
  "data_attrs" => data_attrs,