metanorma-document 0.2.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 (682) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rake.yml +21 -0
  3. data/.github/workflows/release.yml +25 -0
  4. data/.github/workflows/roundtrip-samples.yml +132 -0
  5. data/.gitignore +19 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +22 -0
  8. data/.rubocop_todo.yml +166 -0
  9. data/CHANGELOG.md +21 -0
  10. data/Gemfile +26 -0
  11. data/LICENSE.txt +25 -0
  12. data/README.adoc +176 -0
  13. data/Rakefile +14 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/data/javascripts/components/mn-code-copy.js +36 -0
  17. data/data/javascripts/components/mn-glossary.js +103 -0
  18. data/data/javascripts/components/mn-index-panel.js +33 -0
  19. data/data/javascripts/components/mn-lightbox.js +18 -0
  20. data/data/javascripts/components/mn-search.js +212 -0
  21. data/data/javascripts/components/mn-shortcuts.js +83 -0
  22. data/data/javascripts/components/mn-stem-dropdown.js +42 -0
  23. data/data/javascripts/components/mn-toc.js +182 -0
  24. data/data/javascripts/core/mn-navigation.js +84 -0
  25. data/data/javascripts/core/mn-reader.js +38 -0
  26. data/data/javascripts/core/mn-reveal.js +36 -0
  27. data/data/javascripts/core/mn-scroll.js +87 -0
  28. data/data/javascripts/core/mn-theme.js +140 -0
  29. data/data/javascripts/search.js +203 -0
  30. data/data/logos/bipm-logo.svg +1 -0
  31. data/data/logos/iec-logo.svg +1 -0
  32. data/data/logos/iho-logo.svg +555 -0
  33. data/data/logos/iso-logo.svg +1 -0
  34. data/data/logos/itu-logo.svg +1 -0
  35. data/data/logos/metanorma-logo.svg +41 -0
  36. data/data/logos/ogc-logo.svg +1 -0
  37. data/data/logos/pdfa-logo.svg +95 -0
  38. data/data/stylesheets/base/_dark.css +193 -0
  39. data/data/stylesheets/base/_layout.css +112 -0
  40. data/data/stylesheets/base/_print.css +46 -0
  41. data/data/stylesheets/base/_reset.css +55 -0
  42. data/data/stylesheets/base/_transitions.css +77 -0
  43. data/data/stylesheets/base/_typography.css +89 -0
  44. data/data/stylesheets/components/admonition.css +19 -0
  45. data/data/stylesheets/components/bibliography.css +18 -0
  46. data/data/stylesheets/components/cover.css +205 -0
  47. data/data/stylesheets/components/example.css +20 -0
  48. data/data/stylesheets/components/figure.css +30 -0
  49. data/data/stylesheets/components/footer.css +24 -0
  50. data/data/stylesheets/components/footnote.css +41 -0
  51. data/data/stylesheets/components/formula.css +25 -0
  52. data/data/stylesheets/components/glossary.css +61 -0
  53. data/data/stylesheets/components/header.css +149 -0
  54. data/data/stylesheets/components/index.css +118 -0
  55. data/data/stylesheets/components/inline.css +88 -0
  56. data/data/stylesheets/components/note.css +24 -0
  57. data/data/stylesheets/components/progress.css +40 -0
  58. data/data/stylesheets/components/search.css +45 -0
  59. data/data/stylesheets/components/section.css +26 -0
  60. data/data/stylesheets/components/shortcuts.css +71 -0
  61. data/data/stylesheets/components/sourcecode.css +30 -0
  62. data/data/stylesheets/components/table.css +57 -0
  63. data/data/stylesheets/components/term.css +45 -0
  64. data/data/stylesheets/components/toc.css +162 -0
  65. data/lib/data/dist/iso_document/frontend/assets/index-mwzbzmnK.js +22 -0
  66. data/lib/data/dist/iso_document/frontend/assets/index-pGvKTNid.css +1 -0
  67. data/lib/data/dist/iso_document/frontend/assets/iso-red.svg +53 -0
  68. data/lib/data/dist/iso_document/frontend/index.html +13 -0
  69. data/lib/metanorma/basic_document/ancillary_blocks/example_block.rb +58 -0
  70. data/lib/metanorma/basic_document/ancillary_blocks/figure_block.rb +64 -0
  71. data/lib/metanorma/basic_document/ancillary_blocks/formula_block.rb +38 -0
  72. data/lib/metanorma/basic_document/ancillary_blocks/literal_block.rb +26 -0
  73. data/lib/metanorma/basic_document/ancillary_blocks/sourcecode_block.rb +57 -0
  74. data/lib/metanorma/basic_document/ancillary_blocks/subfigure.rb +18 -0
  75. data/lib/metanorma/basic_document/ancillary_blocks.rb +15 -0
  76. data/lib/metanorma/basic_document/bib_data/bib_data.rb +18 -0
  77. data/lib/metanorma/basic_document/bib_data/bib_data_extension_type.rb +17 -0
  78. data/lib/metanorma/basic_document/bib_data/bibliographic_item.rb +93 -0
  79. data/lib/metanorma/basic_document/bib_data/document_type.rb +21 -0
  80. data/lib/metanorma/basic_document/bib_data.rb +14 -0
  81. data/lib/metanorma/basic_document/blocks/basic_block.rb +13 -0
  82. data/lib/metanorma/basic_document/blocks/basic_block_no_notes.rb +21 -0
  83. data/lib/metanorma/basic_document/blocks/note_block.rb +13 -0
  84. data/lib/metanorma/basic_document/blocks.rb +32 -0
  85. data/lib/metanorma/basic_document/change/attribute_change_action.rb +19 -0
  86. data/lib/metanorma/basic_document/change/attribute_modify.rb +19 -0
  87. data/lib/metanorma/basic_document/change/change.rb +25 -0
  88. data/lib/metanorma/basic_document/change/change_set.rb +20 -0
  89. data/lib/metanorma/basic_document/change/content_action.rb +21 -0
  90. data/lib/metanorma/basic_document/change/content_change.rb +17 -0
  91. data/lib/metanorma/basic_document/change/content_change_action.rb +28 -0
  92. data/lib/metanorma/basic_document/change/content_modify.rb +18 -0
  93. data/lib/metanorma/basic_document/change/node_change.rb +16 -0
  94. data/lib/metanorma/basic_document/change/node_delete.rb +17 -0
  95. data/lib/metanorma/basic_document/change/node_insert.rb +18 -0
  96. data/lib/metanorma/basic_document/change/node_move.rb +20 -0
  97. data/lib/metanorma/basic_document/change/unique_identifier.rb +17 -0
  98. data/lib/metanorma/basic_document/change.rb +23 -0
  99. data/lib/metanorma/basic_document/contrib_metadata/contribution_element_metadata.rb +22 -0
  100. data/lib/metanorma/basic_document/contrib_metadata/hash.rb +19 -0
  101. data/lib/metanorma/basic_document/contrib_metadata/integrity_value.rb +15 -0
  102. data/lib/metanorma/basic_document/contrib_metadata/iso10118_oid.rb +17 -0
  103. data/lib/metanorma/basic_document/contrib_metadata/iso14888_oid.rb +17 -0
  104. data/lib/metanorma/basic_document/contrib_metadata/signature.rb +21 -0
  105. data/lib/metanorma/basic_document/contrib_metadata.rb +16 -0
  106. data/lib/metanorma/basic_document/data_types/formatted_string.rb +17 -0
  107. data/lib/metanorma/basic_document/data_types/iso15924_code.rb +17 -0
  108. data/lib/metanorma/basic_document/data_types/iso3166_code.rb +17 -0
  109. data/lib/metanorma/basic_document/data_types/iso639_code.rb +17 -0
  110. data/lib/metanorma/basic_document/data_types/iso8601_date_time.rb +17 -0
  111. data/lib/metanorma/basic_document/data_types/localized_string.rb +23 -0
  112. data/lib/metanorma/basic_document/data_types/string_format.rb +22 -0
  113. data/lib/metanorma/basic_document/data_types/uri.rb +17 -0
  114. data/lib/metanorma/basic_document/data_types.rb +16 -0
  115. data/lib/metanorma/basic_document/empty_elements/basic_element.rb +18 -0
  116. data/lib/metanorma/basic_document/empty_elements/horizontal_rule_element.rb +14 -0
  117. data/lib/metanorma/basic_document/empty_elements/index_element.rb +24 -0
  118. data/lib/metanorma/basic_document/empty_elements/line_break_element.rb +14 -0
  119. data/lib/metanorma/basic_document/empty_elements/page_break_element.rb +15 -0
  120. data/lib/metanorma/basic_document/empty_elements.rb +16 -0
  121. data/lib/metanorma/basic_document/id_elements/alt_source.rb +21 -0
  122. data/lib/metanorma/basic_document/id_elements/audio.rb +18 -0
  123. data/lib/metanorma/basic_document/id_elements/bookmark.rb +17 -0
  124. data/lib/metanorma/basic_document/id_elements/id_element.rb +19 -0
  125. data/lib/metanorma/basic_document/id_elements/image.rb +19 -0
  126. data/lib/metanorma/basic_document/id_elements/media.rb +39 -0
  127. data/lib/metanorma/basic_document/id_elements/media_type.rb +17 -0
  128. data/lib/metanorma/basic_document/id_elements/video.rb +22 -0
  129. data/lib/metanorma/basic_document/id_elements.rb +16 -0
  130. data/lib/metanorma/basic_document/lists/definition.rb +21 -0
  131. data/lib/metanorma/basic_document/lists/definition_list.rb +18 -0
  132. data/lib/metanorma/basic_document/lists/list.rb +17 -0
  133. data/lib/metanorma/basic_document/lists/ordered_list.rb +19 -0
  134. data/lib/metanorma/basic_document/lists/ordered_list_type.rb +21 -0
  135. data/lib/metanorma/basic_document/lists/unordered_list.rb +14 -0
  136. data/lib/metanorma/basic_document/lists.rb +14 -0
  137. data/lib/metanorma/basic_document/multi_paragraph/admonition_block.rb +36 -0
  138. data/lib/metanorma/basic_document/multi_paragraph/admonition_type.rb +21 -0
  139. data/lib/metanorma/basic_document/multi_paragraph/paragraphs_block.rb +19 -0
  140. data/lib/metanorma/basic_document/multi_paragraph/quote_block.rb +26 -0
  141. data/lib/metanorma/basic_document/multi_paragraph/review_block.rb +32 -0
  142. data/lib/metanorma/basic_document/multi_paragraph.rb +16 -0
  143. data/lib/metanorma/basic_document/paragraphs/paragraph_block.rb +26 -0
  144. data/lib/metanorma/basic_document/paragraphs/paragraph_with_footnote.rb +21 -0
  145. data/lib/metanorma/basic_document/paragraphs/text_alignment.rb +21 -0
  146. data/lib/metanorma/basic_document/paragraphs.rb +12 -0
  147. data/lib/metanorma/basic_document/reference_elements/callout.rb +21 -0
  148. data/lib/metanorma/basic_document/reference_elements/citation.rb +25 -0
  149. data/lib/metanorma/basic_document/reference_elements/footnote.rb +17 -0
  150. data/lib/metanorma/basic_document/reference_elements/index_xref_element.rb +25 -0
  151. data/lib/metanorma/basic_document/reference_elements/reference_element.rb +22 -0
  152. data/lib/metanorma/basic_document/reference_elements/reference_format.rb +21 -0
  153. data/lib/metanorma/basic_document/reference_elements/reference_to_citation_element.rb +20 -0
  154. data/lib/metanorma/basic_document/reference_elements/reference_to_id_element.rb +17 -0
  155. data/lib/metanorma/basic_document/reference_elements/reference_to_id_with_paragraph_element.rb +19 -0
  156. data/lib/metanorma/basic_document/reference_elements/reference_to_link_element.rb +17 -0
  157. data/lib/metanorma/basic_document/reference_elements.rb +25 -0
  158. data/lib/metanorma/basic_document/sections/basic_section.rb +38 -0
  159. data/lib/metanorma/basic_document/sections/content_section.rb +18 -0
  160. data/lib/metanorma/basic_document/sections/hierarchical_section.rb +18 -0
  161. data/lib/metanorma/basic_document/sections/references_section.rb +19 -0
  162. data/lib/metanorma/basic_document/sections.rb +14 -0
  163. data/lib/metanorma/basic_document/tables/paragraph_table_cell.rb +18 -0
  164. data/lib/metanorma/basic_document/tables/table_block.rb +37 -0
  165. data/lib/metanorma/basic_document/tables/table_cell.rb +23 -0
  166. data/lib/metanorma/basic_document/tables/text_alignment.rb +21 -0
  167. data/lib/metanorma/basic_document/tables/text_table_cell.rb +18 -0
  168. data/lib/metanorma/basic_document/tables/text_table_row.rb +21 -0
  169. data/lib/metanorma/basic_document/tables/vertical_alignment.rb +21 -0
  170. data/lib/metanorma/basic_document/tables.rb +16 -0
  171. data/lib/metanorma/basic_document/text_elements/asciiml.rb +17 -0
  172. data/lib/metanorma/basic_document/text_elements/emphasis_element.rb +14 -0
  173. data/lib/metanorma/basic_document/text_elements/keyword_element.rb +14 -0
  174. data/lib/metanorma/basic_document/text_elements/latex.rb +17 -0
  175. data/lib/metanorma/basic_document/text_elements/mathml.rb +17 -0
  176. data/lib/metanorma/basic_document/text_elements/monospace_element.rb +14 -0
  177. data/lib/metanorma/basic_document/text_elements/ruby_element.rb +21 -0
  178. data/lib/metanorma/basic_document/text_elements/small_caps_element.rb +14 -0
  179. data/lib/metanorma/basic_document/text_elements/stem_element.rb +19 -0
  180. data/lib/metanorma/basic_document/text_elements/stem_type.rb +17 -0
  181. data/lib/metanorma/basic_document/text_elements/stem_value.rb +17 -0
  182. data/lib/metanorma/basic_document/text_elements/strike_element.rb +14 -0
  183. data/lib/metanorma/basic_document/text_elements/strong_element.rb +14 -0
  184. data/lib/metanorma/basic_document/text_elements/subscript_element.rb +14 -0
  185. data/lib/metanorma/basic_document/text_elements/superscript_element.rb +14 -0
  186. data/lib/metanorma/basic_document/text_elements/text_element.rb +18 -0
  187. data/lib/metanorma/basic_document/text_elements/text_element_type.rb +20 -0
  188. data/lib/metanorma/basic_document/text_elements/underline_element.rb +13 -0
  189. data/lib/metanorma/basic_document/text_elements.rb +33 -0
  190. data/lib/metanorma/basic_document.rb +23 -0
  191. data/lib/metanorma/bipm_document/metadata/bipm_bib_data_extension_type.rb +20 -0
  192. data/lib/metanorma/bipm_document/metadata/bipm_bibliographic_item.rb +31 -0
  193. data/lib/metanorma/bipm_document/metadata/bipm_structured_identifier.rb +21 -0
  194. data/lib/metanorma/bipm_document/metadata.rb +15 -0
  195. data/lib/metanorma/bipm_document/root.rb +65 -0
  196. data/lib/metanorma/bipm_document.rb +10 -0
  197. data/lib/metanorma/cc_document/metadata/cc_bib_data_extension_type.rb +12 -0
  198. data/lib/metanorma/cc_document/metadata/cc_bibliographic_item.rb +17 -0
  199. data/lib/metanorma/cc_document/metadata.rb +11 -0
  200. data/lib/metanorma/cc_document/root.rb +65 -0
  201. data/lib/metanorma/cc_document.rb +10 -0
  202. data/lib/metanorma/collection/directive.rb +18 -0
  203. data/lib/metanorma/collection/doc_container.rb +18 -0
  204. data/lib/metanorma/collection/entry.rb +33 -0
  205. data/lib/metanorma/collection/root.rb +31 -0
  206. data/lib/metanorma/collection.rb +12 -0
  207. data/lib/metanorma/document/components/ancillary_blocks/example_block.rb +124 -0
  208. data/lib/metanorma/document/components/ancillary_blocks/figure_block.rb +113 -0
  209. data/lib/metanorma/document/components/ancillary_blocks/formula_block.rb +85 -0
  210. data/lib/metanorma/document/components/ancillary_blocks/key_element.rb +26 -0
  211. data/lib/metanorma/document/components/ancillary_blocks/literal_block.rb +32 -0
  212. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_block.rb +123 -0
  213. data/lib/metanorma/document/components/ancillary_blocks/subfigure.rb +41 -0
  214. data/lib/metanorma/document/components/ancillary_blocks.rb +18 -0
  215. data/lib/metanorma/document/components/bib_data/bib_data.rb +67 -0
  216. data/lib/metanorma/document/components/bib_data/bib_data_extension_type.rb +19 -0
  217. data/lib/metanorma/document/components/bib_data/bibliographic_item.rb +107 -0
  218. data/lib/metanorma/document/components/bib_data/document_type.rb +23 -0
  219. data/lib/metanorma/document/components/bib_data.rb +15 -0
  220. data/lib/metanorma/document/components/blocks/basic_block.rb +19 -0
  221. data/lib/metanorma/document/components/blocks/basic_block_no_notes.rb +22 -0
  222. data/lib/metanorma/document/components/blocks/note_block.rb +79 -0
  223. data/lib/metanorma/document/components/blocks.rb +13 -0
  224. data/lib/metanorma/document/components/change/attribute_change_action.rb +21 -0
  225. data/lib/metanorma/document/components/change/attribute_modify.rb +20 -0
  226. data/lib/metanorma/document/components/change/change.rb +27 -0
  227. data/lib/metanorma/document/components/change/change_set.rb +21 -0
  228. data/lib/metanorma/document/components/change/content_action.rb +23 -0
  229. data/lib/metanorma/document/components/change/content_change.rb +19 -0
  230. data/lib/metanorma/document/components/change/content_change_action.rb +30 -0
  231. data/lib/metanorma/document/components/change/content_modify.rb +19 -0
  232. data/lib/metanorma/document/components/change/node_change.rb +18 -0
  233. data/lib/metanorma/document/components/change/node_delete.rb +19 -0
  234. data/lib/metanorma/document/components/change/node_insert.rb +20 -0
  235. data/lib/metanorma/document/components/change/node_move.rb +22 -0
  236. data/lib/metanorma/document/components/change/unique_identifier.rb +19 -0
  237. data/lib/metanorma/document/components/change.rb +24 -0
  238. data/lib/metanorma/document/components/contrib_metadata/contribution_element_metadata.rb +23 -0
  239. data/lib/metanorma/document/components/contrib_metadata/hash.rb +21 -0
  240. data/lib/metanorma/document/components/contrib_metadata/integrity_value.rb +17 -0
  241. data/lib/metanorma/document/components/contrib_metadata/iso10118_oid.rb +19 -0
  242. data/lib/metanorma/document/components/contrib_metadata/iso14888_oid.rb +19 -0
  243. data/lib/metanorma/document/components/contrib_metadata/signature.rb +23 -0
  244. data/lib/metanorma/document/components/contrib_metadata.rb +17 -0
  245. data/lib/metanorma/document/components/data_types/formatted_string.rb +95 -0
  246. data/lib/metanorma/document/components/data_types/iso15924_code.rb +19 -0
  247. data/lib/metanorma/document/components/data_types/iso3166_code.rb +19 -0
  248. data/lib/metanorma/document/components/data_types/iso639_code.rb +19 -0
  249. data/lib/metanorma/document/components/data_types/iso8601_date_time.rb +19 -0
  250. data/lib/metanorma/document/components/data_types/localized_string.rb +29 -0
  251. data/lib/metanorma/document/components/data_types/string_format.rb +24 -0
  252. data/lib/metanorma/document/components/data_types/uri.rb +19 -0
  253. data/lib/metanorma/document/components/data_types.rb +18 -0
  254. data/lib/metanorma/document/components/empty_elements/basic_element.rb +20 -0
  255. data/lib/metanorma/document/components/empty_elements/horizontal_rule_element.rb +16 -0
  256. data/lib/metanorma/document/components/empty_elements/index_element.rb +26 -0
  257. data/lib/metanorma/document/components/empty_elements/line_break_element.rb +16 -0
  258. data/lib/metanorma/document/components/empty_elements/page_break_element.rb +20 -0
  259. data/lib/metanorma/document/components/empty_elements.rb +18 -0
  260. data/lib/metanorma/document/components/id_elements/alt_source.rb +23 -0
  261. data/lib/metanorma/document/components/id_elements/audio.rb +19 -0
  262. data/lib/metanorma/document/components/id_elements/bookmark.rb +24 -0
  263. data/lib/metanorma/document/components/id_elements/id_element.rb +21 -0
  264. data/lib/metanorma/document/components/id_elements/image.rb +23 -0
  265. data/lib/metanorma/document/components/id_elements/media.rb +41 -0
  266. data/lib/metanorma/document/components/id_elements/media_type.rb +19 -0
  267. data/lib/metanorma/document/components/id_elements/video.rb +23 -0
  268. data/lib/metanorma/document/components/id_elements.rb +18 -0
  269. data/lib/metanorma/document/components/inline/asciimath_element.rb +16 -0
  270. data/lib/metanorma/document/components/inline/attribution_element.rb +17 -0
  271. data/lib/metanorma/document/components/inline/bcp14_element.rb +16 -0
  272. data/lib/metanorma/document/components/inline/biblio_tag_element.rb +23 -0
  273. data/lib/metanorma/document/components/inline/br_element.rb +13 -0
  274. data/lib/metanorma/document/components/inline/comma_element.rb +13 -0
  275. data/lib/metanorma/document/components/inline/concept_element.rb +22 -0
  276. data/lib/metanorma/document/components/inline/display_text_element.rb +37 -0
  277. data/lib/metanorma/document/components/inline/em_raw_element.rb +23 -0
  278. data/lib/metanorma/document/components/inline/enum_comma_element.rb +13 -0
  279. data/lib/metanorma/document/components/inline/eref_element.rb +35 -0
  280. data/lib/metanorma/document/components/inline/fmt_admitted_element.rb +17 -0
  281. data/lib/metanorma/document/components/inline/fmt_annotation_body_element.rb +38 -0
  282. data/lib/metanorma/document/components/inline/fmt_annotation_end_element.rb +26 -0
  283. data/lib/metanorma/document/components/inline/fmt_annotation_start_element.rb +26 -0
  284. data/lib/metanorma/document/components/inline/fmt_concept_element.rb +23 -0
  285. data/lib/metanorma/document/components/inline/fmt_definition_element.rb +18 -0
  286. data/lib/metanorma/document/components/inline/fmt_fn_body_element.rb +22 -0
  287. data/lib/metanorma/document/components/inline/fmt_fn_label_element.rb +23 -0
  288. data/lib/metanorma/document/components/inline/fmt_footnote_container_element.rb +16 -0
  289. data/lib/metanorma/document/components/inline/fmt_identifier_element.rb +16 -0
  290. data/lib/metanorma/document/components/inline/fmt_name_element.rb +23 -0
  291. data/lib/metanorma/document/components/inline/fmt_preferred_element.rb +17 -0
  292. data/lib/metanorma/document/components/inline/fmt_sourcecode_element.rb +29 -0
  293. data/lib/metanorma/document/components/inline/fmt_stem_element.rb +18 -0
  294. data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +20 -0
  295. data/lib/metanorma/document/components/inline/fmt_title_element.rb +29 -0
  296. data/lib/metanorma/document/components/inline/fmt_xref_element.rb +25 -0
  297. data/lib/metanorma/document/components/inline/fmt_xref_label_element.rb +23 -0
  298. data/lib/metanorma/document/components/inline/fn_element.rb +31 -0
  299. data/lib/metanorma/document/components/inline/link_element.rb +27 -0
  300. data/lib/metanorma/document/components/inline/localized_string_element.rb +31 -0
  301. data/lib/metanorma/document/components/inline/localized_strings_element.rb +16 -0
  302. data/lib/metanorma/document/components/inline/math_element.rb +16 -0
  303. data/lib/metanorma/document/components/inline/name_with_id_element.rb +20 -0
  304. data/lib/metanorma/document/components/inline/semx_child_element.rb +25 -0
  305. data/lib/metanorma/document/components/inline/semx_element.rb +117 -0
  306. data/lib/metanorma/document/components/inline/small_cap_element.rb +16 -0
  307. data/lib/metanorma/document/components/inline/span_element.rb +37 -0
  308. data/lib/metanorma/document/components/inline/stem_inline_element.rb +24 -0
  309. data/lib/metanorma/document/components/inline/strong_raw_element.rb +25 -0
  310. data/lib/metanorma/document/components/inline/sub_element.rb +15 -0
  311. data/lib/metanorma/document/components/inline/sup_element.rb +26 -0
  312. data/lib/metanorma/document/components/inline/tab_element.rb +13 -0
  313. data/lib/metanorma/document/components/inline/title_with_annotation_element.rb +51 -0
  314. data/lib/metanorma/document/components/inline/tt_element.rb +20 -0
  315. data/lib/metanorma/document/components/inline/variant_title_element.rb +27 -0
  316. data/lib/metanorma/document/components/inline/xref_element.rb +24 -0
  317. data/lib/metanorma/document/components/inline.rb +100 -0
  318. data/lib/metanorma/document/components/lists/dd_element.rb +58 -0
  319. data/lib/metanorma/document/components/lists/definition.rb +32 -0
  320. data/lib/metanorma/document/components/lists/definition_list.rb +44 -0
  321. data/lib/metanorma/document/components/lists/dt_element.rb +71 -0
  322. data/lib/metanorma/document/components/lists/list.rb +25 -0
  323. data/lib/metanorma/document/components/lists/list_item.rb +102 -0
  324. data/lib/metanorma/document/components/lists/ordered_list.rb +42 -0
  325. data/lib/metanorma/document/components/lists/ordered_list_type.rb +23 -0
  326. data/lib/metanorma/document/components/lists/unordered_list.rb +34 -0
  327. data/lib/metanorma/document/components/lists.rb +19 -0
  328. data/lib/metanorma/document/components/multi_paragraph/admonition_block.rb +62 -0
  329. data/lib/metanorma/document/components/multi_paragraph/admonition_type.rb +23 -0
  330. data/lib/metanorma/document/components/multi_paragraph/paragraphs_block.rb +21 -0
  331. data/lib/metanorma/document/components/multi_paragraph/quote_block.rb +74 -0
  332. data/lib/metanorma/document/components/multi_paragraph/review_block.rb +34 -0
  333. data/lib/metanorma/document/components/multi_paragraph.rb +15 -0
  334. data/lib/metanorma/document/components/paragraphs/paragraph_block.rb +155 -0
  335. data/lib/metanorma/document/components/paragraphs/paragraph_with_footnote.rb +24 -0
  336. data/lib/metanorma/document/components/paragraphs/text_alignment.rb +23 -0
  337. data/lib/metanorma/document/components/paragraphs.rb +14 -0
  338. data/lib/metanorma/document/components/reference_elements/callout.rb +25 -0
  339. data/lib/metanorma/document/components/reference_elements/citation.rb +38 -0
  340. data/lib/metanorma/document/components/reference_elements/footnote.rb +19 -0
  341. data/lib/metanorma/document/components/reference_elements/index_xref_element.rb +27 -0
  342. data/lib/metanorma/document/components/reference_elements/reference_element.rb +24 -0
  343. data/lib/metanorma/document/components/reference_elements/reference_format.rb +23 -0
  344. data/lib/metanorma/document/components/reference_elements/reference_to_citation_element.rb +33 -0
  345. data/lib/metanorma/document/components/reference_elements/reference_to_id_element.rb +19 -0
  346. data/lib/metanorma/document/components/reference_elements/reference_to_id_with_paragraph_element.rb +21 -0
  347. data/lib/metanorma/document/components/reference_elements/reference_to_link_element.rb +19 -0
  348. data/lib/metanorma/document/components/reference_elements.rb +27 -0
  349. data/lib/metanorma/document/components/sections/basic_section.rb +37 -0
  350. data/lib/metanorma/document/components/sections/content_section.rb +20 -0
  351. data/lib/metanorma/document/components/sections/hierarchical_section.rb +19 -0
  352. data/lib/metanorma/document/components/sections/references_section.rb +25 -0
  353. data/lib/metanorma/document/components/sections.rb +15 -0
  354. data/lib/metanorma/document/components/tables/header_table_cell.rb +17 -0
  355. data/lib/metanorma/document/components/tables/paragraph_table_cell.rb +20 -0
  356. data/lib/metanorma/document/components/tables/table_block.rb +99 -0
  357. data/lib/metanorma/document/components/tables/table_cell.rb +139 -0
  358. data/lib/metanorma/document/components/tables/table_section.rb +42 -0
  359. data/lib/metanorma/document/components/tables/text_alignment.rb +23 -0
  360. data/lib/metanorma/document/components/tables/text_table_cell.rb +17 -0
  361. data/lib/metanorma/document/components/tables/text_table_row.rb +30 -0
  362. data/lib/metanorma/document/components/tables/vertical_alignment.rb +23 -0
  363. data/lib/metanorma/document/components/tables.rb +22 -0
  364. data/lib/metanorma/document/components/text_elements/asciiml.rb +19 -0
  365. data/lib/metanorma/document/components/text_elements/emphasis_element.rb +16 -0
  366. data/lib/metanorma/document/components/text_elements/keyword_element.rb +16 -0
  367. data/lib/metanorma/document/components/text_elements/latex.rb +19 -0
  368. data/lib/metanorma/document/components/text_elements/latexmath_element.rb +19 -0
  369. data/lib/metanorma/document/components/text_elements/monospace_element.rb +16 -0
  370. data/lib/metanorma/document/components/text_elements/ruby_element.rb +23 -0
  371. data/lib/metanorma/document/components/text_elements/small_caps_element.rb +16 -0
  372. data/lib/metanorma/document/components/text_elements/stem_element.rb +34 -0
  373. data/lib/metanorma/document/components/text_elements/stem_type.rb +23 -0
  374. data/lib/metanorma/document/components/text_elements/stem_value.rb +19 -0
  375. data/lib/metanorma/document/components/text_elements/strike_element.rb +33 -0
  376. data/lib/metanorma/document/components/text_elements/strong_element.rb +16 -0
  377. data/lib/metanorma/document/components/text_elements/subscript_element.rb +16 -0
  378. data/lib/metanorma/document/components/text_elements/superscript_element.rb +16 -0
  379. data/lib/metanorma/document/components/text_elements/text_element.rb +20 -0
  380. data/lib/metanorma/document/components/text_elements/text_element_type.rb +21 -0
  381. data/lib/metanorma/document/components/text_elements/underline_element.rb +33 -0
  382. data/lib/metanorma/document/components/text_elements.rb +31 -0
  383. data/lib/metanorma/document/components.rb +27 -0
  384. data/lib/metanorma/document/data_types.rb +11 -0
  385. data/lib/metanorma/document/relaton/address.rb +39 -0
  386. data/lib/metanorma/document/relaton/affiliation.rb +22 -0
  387. data/lib/metanorma/document/relaton/bib_item_locality.rb +21 -0
  388. data/lib/metanorma/document/relaton/bib_item_size.rb +19 -0
  389. data/lib/metanorma/document/relaton/bib_item_size_type.rb +21 -0
  390. data/lib/metanorma/document/relaton/bib_item_type.rb +25 -0
  391. data/lib/metanorma/document/relaton/bibliographic_date.rb +27 -0
  392. data/lib/metanorma/document/relaton/bibliographic_date_type.rb +22 -0
  393. data/lib/metanorma/document/relaton/contact_method.rb +22 -0
  394. data/lib/metanorma/document/relaton/contribution_info.rb +21 -0
  395. data/lib/metanorma/document/relaton/contributor.rb +15 -0
  396. data/lib/metanorma/document/relaton/contributor_role.rb +23 -0
  397. data/lib/metanorma/document/relaton/copyright_association.rb +32 -0
  398. data/lib/metanorma/document/relaton/date_time.rb +19 -0
  399. data/lib/metanorma/document/relaton/document_identifier.rb +25 -0
  400. data/lib/metanorma/document/relaton/document_relation.rb +26 -0
  401. data/lib/metanorma/document/relaton/document_relation_type.rb +31 -0
  402. data/lib/metanorma/document/relaton/document_status.rb +26 -0
  403. data/lib/metanorma/document/relaton/edition.rb +21 -0
  404. data/lib/metanorma/document/relaton/full_name.rb +34 -0
  405. data/lib/metanorma/document/relaton/iso4217_code.rb +17 -0
  406. data/lib/metanorma/document/relaton/iso8601_date.rb +17 -0
  407. data/lib/metanorma/document/relaton/keyword_type.rb +23 -0
  408. data/lib/metanorma/document/relaton/locality_stack.rb +19 -0
  409. data/lib/metanorma/document/relaton/localized_name.rb +22 -0
  410. data/lib/metanorma/document/relaton/medium_type.rb +26 -0
  411. data/lib/metanorma/document/relaton/org_identifier.rb +20 -0
  412. data/lib/metanorma/document/relaton/org_subdivision.rb +28 -0
  413. data/lib/metanorma/document/relaton/org_subdivision_identifier.rb +20 -0
  414. data/lib/metanorma/document/relaton/organization.rb +48 -0
  415. data/lib/metanorma/document/relaton/person.rb +28 -0
  416. data/lib/metanorma/document/relaton/person_identifier.rb +18 -0
  417. data/lib/metanorma/document/relaton/personal_identifier_type.rb +21 -0
  418. data/lib/metanorma/document/relaton/phone.rb +18 -0
  419. data/lib/metanorma/document/relaton/place_type.rb +36 -0
  420. data/lib/metanorma/document/relaton/price_type.rb +18 -0
  421. data/lib/metanorma/document/relaton/region_type.rb +20 -0
  422. data/lib/metanorma/document/relaton/relaton_collection.rb +25 -0
  423. data/lib/metanorma/document/relaton/series_type.rb +36 -0
  424. data/lib/metanorma/document/relaton/series_type_type.rb +21 -0
  425. data/lib/metanorma/document/relaton/specific_locality_type.rb +25 -0
  426. data/lib/metanorma/document/relaton/title_type.rb +21 -0
  427. data/lib/metanorma/document/relaton/typed_note.rb +25 -0
  428. data/lib/metanorma/document/relaton/typed_title_string.rb +50 -0
  429. data/lib/metanorma/document/relaton/typed_uri.rb +18 -0
  430. data/lib/metanorma/document/relaton/validity_type.rb +22 -0
  431. data/lib/metanorma/document/relaton/variant_full_name.rb +18 -0
  432. data/lib/metanorma/document/relaton/variant_org_name.rb +18 -0
  433. data/lib/metanorma/document/relaton/version_info.rb +19 -0
  434. data/lib/metanorma/document/relaton/vocab_id_type.rb +22 -0
  435. data/lib/metanorma/document/relaton.rb +64 -0
  436. data/lib/metanorma/document/root.rb +14 -0
  437. data/lib/metanorma/document/version.rb +8 -0
  438. data/lib/metanorma/document.rb +54 -0
  439. data/lib/metanorma/html/asset_pipeline.rb +100 -0
  440. data/lib/metanorma/html/base_renderer.rb +1610 -0
  441. data/lib/metanorma/html/bipm_renderer.rb +145 -0
  442. data/lib/metanorma/html/cc_renderer.rb +55 -0
  443. data/lib/metanorma/html/component/base.rb +63 -0
  444. data/lib/metanorma/html/component/footnote_collector.rb +57 -0
  445. data/lib/metanorma/html/component/index_section.rb +74 -0
  446. data/lib/metanorma/html/component/index_term_collector.rb +100 -0
  447. data/lib/metanorma/html/component.rb +12 -0
  448. data/lib/metanorma/html/component_registry.rb +37 -0
  449. data/lib/metanorma/html/drops/footnote_drop.rb +26 -0
  450. data/lib/metanorma/html/drops.rb +9 -0
  451. data/lib/metanorma/html/generator.rb +114 -0
  452. data/lib/metanorma/html/icc_renderer.rb +49 -0
  453. data/lib/metanorma/html/iec_renderer.rb +54 -0
  454. data/lib/metanorma/html/ieee_renderer.rb +55 -0
  455. data/lib/metanorma/html/ietf_renderer.rb +58 -0
  456. data/lib/metanorma/html/iho_renderer.rb +56 -0
  457. data/lib/metanorma/html/iso_renderer.rb +821 -0
  458. data/lib/metanorma/html/itu_renderer.rb +55 -0
  459. data/lib/metanorma/html/ogc_renderer.rb +86 -0
  460. data/lib/metanorma/html/oiml_renderer.rb +55 -0
  461. data/lib/metanorma/html/pdfa_renderer.rb +51 -0
  462. data/lib/metanorma/html/ribose_renderer.rb +55 -0
  463. data/lib/metanorma/html/standard_renderer.rb +535 -0
  464. data/lib/metanorma/html/templates/_cover.html.liquid +11 -0
  465. data/lib/metanorma/html/templates/_doc_title.html.liquid +3 -0
  466. data/lib/metanorma/html/templates/_footer.html.liquid +8 -0
  467. data/lib/metanorma/html/templates/_footnotes.html.liquid +11 -0
  468. data/lib/metanorma/html/templates/_header.html.liquid +65 -0
  469. data/lib/metanorma/html/templates/_iso_cover.html.liquid +31 -0
  470. data/lib/metanorma/html/templates/_iso_doc_title.html.liquid +3 -0
  471. data/lib/metanorma/html/templates/document.html.liquid +40 -0
  472. data/lib/metanorma/html/theme.rb +196 -0
  473. data/lib/metanorma/html/whitespace_patch.rb +47 -0
  474. data/lib/metanorma/html.rb +29 -0
  475. data/lib/metanorma/iec_document/root.rb +66 -0
  476. data/lib/metanorma/iec_document.rb +10 -0
  477. data/lib/metanorma/ieee_document/metadata/ieee_bib_data_extension_type.rb +18 -0
  478. data/lib/metanorma/ieee_document/metadata/ieee_bibliographic_item.rb +20 -0
  479. data/lib/metanorma/ieee_document/metadata/ieee_structured_identifier.rb +25 -0
  480. data/lib/metanorma/ieee_document/metadata.rb +14 -0
  481. data/lib/metanorma/ieee_document/root.rb +65 -0
  482. data/lib/metanorma/ieee_document.rb +11 -0
  483. data/lib/metanorma/ietf_document/metadata/ietf_bib_data_extension_type.rb +58 -0
  484. data/lib/metanorma/ietf_document/metadata/ietf_bibliographic_item.rb +53 -0
  485. data/lib/metanorma/ietf_document/metadata/ietf_editorial_group.rb +17 -0
  486. data/lib/metanorma/ietf_document/metadata.rb +14 -0
  487. data/lib/metanorma/ietf_document/root.rb +66 -0
  488. data/lib/metanorma/ietf_document/sections.rb +31 -0
  489. data/lib/metanorma/ietf_document.rb +10 -0
  490. data/lib/metanorma/iho_document/metadata/iho_bib_data_extension_type.rb +25 -0
  491. data/lib/metanorma/iho_document/metadata/iho_bibliographic_item.rb +20 -0
  492. data/lib/metanorma/iho_document/metadata/iho_editorial_group.rb +19 -0
  493. data/lib/metanorma/iho_document/metadata/iho_series.rb +22 -0
  494. data/lib/metanorma/iho_document/metadata/iho_sub_group.rb +18 -0
  495. data/lib/metanorma/iho_document/metadata.rb +15 -0
  496. data/lib/metanorma/iho_document/root.rb +65 -0
  497. data/lib/metanorma/iho_document.rb +11 -0
  498. data/lib/metanorma/iso_document/annotation_container.rb +16 -0
  499. data/lib/metanorma/iso_document/blocks/iso_admonition_block.rb +27 -0
  500. data/lib/metanorma/iso_document/blocks/iso_admonition_type.rb +25 -0
  501. data/lib/metanorma/iso_document/blocks.rb +12 -0
  502. data/lib/metanorma/iso_document/boilerplate.rb +17 -0
  503. data/lib/metanorma/iso_document/metadata/abstract_title.rb +36 -0
  504. data/lib/metanorma/iso_document/metadata/doc_identifier.rb +26 -0
  505. data/lib/metanorma/iso_document/metadata/ics.rb +19 -0
  506. data/lib/metanorma/iso_document/metadata/iec_document_category.rb +23 -0
  507. data/lib/metanorma/iso_document/metadata/iso_bib_data_extension_type.rb +86 -0
  508. data/lib/metanorma/iso_document/metadata/iso_bibliographic_item.rb +175 -0
  509. data/lib/metanorma/iso_document/metadata/iso_document_id.rb +46 -0
  510. data/lib/metanorma/iso_document/metadata/iso_document_stage_codes.rb +26 -0
  511. data/lib/metanorma/iso_document/metadata/iso_document_status.rb +56 -0
  512. data/lib/metanorma/iso_document/metadata/iso_document_substage_codes.rb +26 -0
  513. data/lib/metanorma/iso_document/metadata/iso_document_type.rb +25 -0
  514. data/lib/metanorma/iso_document/metadata/iso_localized_title.rb +57 -0
  515. data/lib/metanorma/iso_document/metadata/iso_project_group.rb +32 -0
  516. data/lib/metanorma/iso_document/metadata/iso_sub_group.rb +38 -0
  517. data/lib/metanorma/iso_document/metadata/metanorma_extension.rb +20 -0
  518. data/lib/metanorma/iso_document/metadata/structured_identifier.rb +33 -0
  519. data/lib/metanorma/iso_document/metadata/title_amd.rb +12 -0
  520. data/lib/metanorma/iso_document/metadata/title_amendment_prefix.rb +12 -0
  521. data/lib/metanorma/iso_document/metadata/title_collection.rb +46 -0
  522. data/lib/metanorma/iso_document/metadata/title_full.rb +12 -0
  523. data/lib/metanorma/iso_document/metadata/title_intro.rb +12 -0
  524. data/lib/metanorma/iso_document/metadata/title_main.rb +12 -0
  525. data/lib/metanorma/iso_document/metadata/title_part.rb +12 -0
  526. data/lib/metanorma/iso_document/metadata/title_part_prefix.rb +12 -0
  527. data/lib/metanorma/iso_document/metadata.rb +39 -0
  528. data/lib/metanorma/iso_document/raw_paragraph.rb +23 -0
  529. data/lib/metanorma/iso_document/root.rb +92 -0
  530. data/lib/metanorma/iso_document/sections/colophon.rb +18 -0
  531. data/lib/metanorma/iso_document/sections/iso_abstract_section.rb +15 -0
  532. data/lib/metanorma/iso_document/sections/iso_amendment_clause.rb +24 -0
  533. data/lib/metanorma/iso_document/sections/iso_annex_section.rb +172 -0
  534. data/lib/metanorma/iso_document/sections/iso_clause_section.rb +206 -0
  535. data/lib/metanorma/iso_document/sections/iso_foreword_section.rb +118 -0
  536. data/lib/metanorma/iso_document/sections/iso_preface.rb +43 -0
  537. data/lib/metanorma/iso_document/sections/iso_sections.rb +61 -0
  538. data/lib/metanorma/iso_document/sections/iso_terms_section.rb +88 -0
  539. data/lib/metanorma/iso_document/sections.rb +17 -0
  540. data/lib/metanorma/iso_document/terms/iso_term.rb +84 -0
  541. data/lib/metanorma/iso_document/terms/iso_term_collection.rb +20 -0
  542. data/lib/metanorma/iso_document/terms/term_definition.rb +30 -0
  543. data/lib/metanorma/iso_document/terms/term_designation.rb +44 -0
  544. data/lib/metanorma/iso_document/terms/term_example.rb +39 -0
  545. data/lib/metanorma/iso_document/terms/term_expression.rb +55 -0
  546. data/lib/metanorma/iso_document/terms/term_note.rb +41 -0
  547. data/lib/metanorma/iso_document/terms/term_origin.rb +26 -0
  548. data/lib/metanorma/iso_document/terms/term_source.rb +49 -0
  549. data/lib/metanorma/iso_document/terms/verbal_definition.rb +41 -0
  550. data/lib/metanorma/iso_document/terms.rb +20 -0
  551. data/lib/metanorma/iso_document.rb +16 -0
  552. data/lib/metanorma/itu_document/metadata/itu_bib_data_extension_type.rb +63 -0
  553. data/lib/metanorma/itu_document/metadata/itu_bibliographic_item.rb +21 -0
  554. data/lib/metanorma/itu_document/metadata/itu_series.rb +22 -0
  555. data/lib/metanorma/itu_document/metadata/itu_structured_identifier.rb +23 -0
  556. data/lib/metanorma/itu_document/metadata.rb +19 -0
  557. data/lib/metanorma/itu_document/root.rb +65 -0
  558. data/lib/metanorma/itu_document.rb +10 -0
  559. data/lib/metanorma/ogc_document/metadata/ogc_bib_data_extension_type.rb +12 -0
  560. data/lib/metanorma/ogc_document/metadata/ogc_bibliographic_item.rb +19 -0
  561. data/lib/metanorma/ogc_document/metadata.rb +12 -0
  562. data/lib/metanorma/ogc_document/root.rb +65 -0
  563. data/lib/metanorma/ogc_document.rb +10 -0
  564. data/lib/metanorma/oiml_document/root.rb +66 -0
  565. data/lib/metanorma/oiml_document.rb +10 -0
  566. data/lib/metanorma/ribose_document/metadata/ribose_bib_data_extension_type.rb +20 -0
  567. data/lib/metanorma/ribose_document/metadata/ribose_bibliographic_item.rb +18 -0
  568. data/lib/metanorma/ribose_document/metadata.rb +12 -0
  569. data/lib/metanorma/ribose_document/root.rb +68 -0
  570. data/lib/metanorma/ribose_document.rb +10 -0
  571. data/lib/metanorma/standard_document/blocks/amend_block.rb +50 -0
  572. data/lib/metanorma/standard_document/blocks/amend_content_block.rb +17 -0
  573. data/lib/metanorma/standard_document/blocks/auto_number.rb +20 -0
  574. data/lib/metanorma/standard_document/blocks/block_source.rb +45 -0
  575. data/lib/metanorma/standard_document/blocks/change_type.rb +21 -0
  576. data/lib/metanorma/standard_document/blocks/element_name.rb +22 -0
  577. data/lib/metanorma/standard_document/blocks/form.rb +38 -0
  578. data/lib/metanorma/standard_document/blocks/image_map_area_type.rb +24 -0
  579. data/lib/metanorma/standard_document/blocks/image_map_area_type_type.rb +21 -0
  580. data/lib/metanorma/standard_document/blocks/image_map_block.rb +32 -0
  581. data/lib/metanorma/standard_document/blocks/image_map_coords.rb +19 -0
  582. data/lib/metanorma/standard_document/blocks/image_map_radius.rb +19 -0
  583. data/lib/metanorma/standard_document/blocks/multilingual_rendering_type.rb +21 -0
  584. data/lib/metanorma/standard_document/blocks/passthrough.rb +20 -0
  585. data/lib/metanorma/standard_document/blocks/requirement_model.rb +165 -0
  586. data/lib/metanorma/standard_document/blocks/standard_admonition_block.rb +32 -0
  587. data/lib/metanorma/standard_document/blocks/standard_block.rb +28 -0
  588. data/lib/metanorma/standard_document/blocks/standard_block_no_notes.rb +34 -0
  589. data/lib/metanorma/standard_document/blocks/standard_example_block.rb +30 -0
  590. data/lib/metanorma/standard_document/blocks/standard_figure_block.rb +32 -0
  591. data/lib/metanorma/standard_document/blocks/standard_formula_block.rb +30 -0
  592. data/lib/metanorma/standard_document/blocks/standard_note_block.rb +64 -0
  593. data/lib/metanorma/standard_document/blocks/standard_paragraph_block.rb +25 -0
  594. data/lib/metanorma/standard_document/blocks/standard_quote_block.rb +29 -0
  595. data/lib/metanorma/standard_document/blocks/standard_sourcecode_block.rb +32 -0
  596. data/lib/metanorma/standard_document/blocks/standard_table_block.rb +39 -0
  597. data/lib/metanorma/standard_document/blocks/svg_map_block.rb +23 -0
  598. data/lib/metanorma/standard_document/blocks/svg_target_type.rb +20 -0
  599. data/lib/metanorma/standard_document/blocks/table_col.rb +16 -0
  600. data/lib/metanorma/standard_document/blocks/to_c.rb +17 -0
  601. data/lib/metanorma/standard_document/blocks.rb +60 -0
  602. data/lib/metanorma/standard_document/elements/add.rb +19 -0
  603. data/lib/metanorma/standard_document/elements/del.rb +19 -0
  604. data/lib/metanorma/standard_document/elements/form_input.rb +27 -0
  605. data/lib/metanorma/standard_document/elements/input.rb +33 -0
  606. data/lib/metanorma/standard_document/elements/input_type.rb +27 -0
  607. data/lib/metanorma/standard_document/elements/label.rb +19 -0
  608. data/lib/metanorma/standard_document/elements/option.rb +27 -0
  609. data/lib/metanorma/standard_document/elements/orientation_type.rb +27 -0
  610. data/lib/metanorma/standard_document/elements/select.rb +32 -0
  611. data/lib/metanorma/standard_document/elements/standard_page_break_element.rb +22 -0
  612. data/lib/metanorma/standard_document/elements/textarea.rb +25 -0
  613. data/lib/metanorma/standard_document/elements.rb +20 -0
  614. data/lib/metanorma/standard_document/lists/standard_definition.rb +24 -0
  615. data/lib/metanorma/standard_document/lists/standard_definition_list.rb +30 -0
  616. data/lib/metanorma/standard_document/lists/standard_definition_term.rb +27 -0
  617. data/lib/metanorma/standard_document/lists/standard_unordered_list.rb +28 -0
  618. data/lib/metanorma/standard_document/lists/unordered_checkable_list_item.rb +26 -0
  619. data/lib/metanorma/standard_document/lists.rb +17 -0
  620. data/lib/metanorma/standard_document/metadata/editorial_group_type.rb +25 -0
  621. data/lib/metanorma/standard_document/metadata/ics_type.rb +26 -0
  622. data/lib/metanorma/standard_document/metadata/standard_bib_data.rb +25 -0
  623. data/lib/metanorma/standard_document/metadata/standard_bib_data_extension_type.rb +37 -0
  624. data/lib/metanorma/standard_document/metadata/structured_identifier_type.rb +48 -0
  625. data/lib/metanorma/standard_document/metadata/technical_committee_type.rb +27 -0
  626. data/lib/metanorma/standard_document/metadata.rb +17 -0
  627. data/lib/metanorma/standard_document/namespace.rb +11 -0
  628. data/lib/metanorma/standard_document/refs/reference_to_term.rb +25 -0
  629. data/lib/metanorma/standard_document/refs/reference_to_termbase.rb +27 -0
  630. data/lib/metanorma/standard_document/refs/standoc_locality_stack.rb +24 -0
  631. data/lib/metanorma/standard_document/refs/standoc_reference_to_citation_element.rb +27 -0
  632. data/lib/metanorma/standard_document/refs/standoc_reference_to_id_element.rb +36 -0
  633. data/lib/metanorma/standard_document/refs/xref_case_type.rb +27 -0
  634. data/lib/metanorma/standard_document/refs/xref_connective_type.rb +27 -0
  635. data/lib/metanorma/standard_document/refs/xref_target_type.rb +25 -0
  636. data/lib/metanorma/standard_document/refs.rb +18 -0
  637. data/lib/metanorma/standard_document/root.rb +10 -0
  638. data/lib/metanorma/standard_document/sections/abstract.rb +21 -0
  639. data/lib/metanorma/standard_document/sections/acknowledgements.rb +21 -0
  640. data/lib/metanorma/standard_document/sections/annex_section.rb +32 -0
  641. data/lib/metanorma/standard_document/sections/bibliography_section.rb +21 -0
  642. data/lib/metanorma/standard_document/sections/boilerplate_type.rb +32 -0
  643. data/lib/metanorma/standard_document/sections/clause_hierarchical_section.rb +32 -0
  644. data/lib/metanorma/standard_document/sections/clause_section.rb +29 -0
  645. data/lib/metanorma/standard_document/sections/definition_section.rb +75 -0
  646. data/lib/metanorma/standard_document/sections/floating_title.rb +27 -0
  647. data/lib/metanorma/standard_document/sections/foreword.rb +21 -0
  648. data/lib/metanorma/standard_document/sections/introduction.rb +23 -0
  649. data/lib/metanorma/standard_document/sections/misc_container.rb +26 -0
  650. data/lib/metanorma/standard_document/sections/normative_type.rb +29 -0
  651. data/lib/metanorma/standard_document/sections/standard_content_section.rb +27 -0
  652. data/lib/metanorma/standard_document/sections/standard_hierarchical_section.rb +30 -0
  653. data/lib/metanorma/standard_document/sections/standard_references_section.rb +74 -0
  654. data/lib/metanorma/standard_document/sections/standard_section.rb +39 -0
  655. data/lib/metanorma/standard_document/sections/terms_section.rb +29 -0
  656. data/lib/metanorma/standard_document/sections.rb +30 -0
  657. data/lib/metanorma/standard_document/standard_document_type.rb +15 -0
  658. data/lib/metanorma/standard_document/terms/abbreviation_type.rb +27 -0
  659. data/lib/metanorma/standard_document/terms/concept.rb +42 -0
  660. data/lib/metanorma/standard_document/terms/definition_collection.rb +24 -0
  661. data/lib/metanorma/standard_document/terms/designation.rb +31 -0
  662. data/lib/metanorma/standard_document/terms/expression_designation.rb +38 -0
  663. data/lib/metanorma/standard_document/terms/expression_type.rb +27 -0
  664. data/lib/metanorma/standard_document/terms/grammar_gender.rb +27 -0
  665. data/lib/metanorma/standard_document/terms/grammar_info.rb +36 -0
  666. data/lib/metanorma/standard_document/terms/graphical_symbol_designation.rb +25 -0
  667. data/lib/metanorma/standard_document/terms/letter_symbol_designation.rb +27 -0
  668. data/lib/metanorma/standard_document/terms/non_verbal_representation.rb +33 -0
  669. data/lib/metanorma/standard_document/terms/related_term.rb +31 -0
  670. data/lib/metanorma/standard_document/terms/related_term_type.rb +28 -0
  671. data/lib/metanorma/standard_document/terms/term.rb +66 -0
  672. data/lib/metanorma/standard_document/terms/term_collection.rb +24 -0
  673. data/lib/metanorma/standard_document/terms/term_definition.rb +29 -0
  674. data/lib/metanorma/standard_document/terms/term_source.rb +29 -0
  675. data/lib/metanorma/standard_document/terms/term_source_status.rb +29 -0
  676. data/lib/metanorma/standard_document/terms/term_source_type.rb +21 -0
  677. data/lib/metanorma/standard_document/terms/verbal_expression.rb +35 -0
  678. data/lib/metanorma/standard_document/terms.rb +31 -0
  679. data/lib/metanorma/standard_document.rb +19 -0
  680. data/metanorma-document.gemspec +35 -0
  681. data/tasks/roundtrip_samples.rake +535 -0
  682. metadata +743 -0
@@ -0,0 +1,1610 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid"
4
+ require "nokogiri"
5
+ require_relative "drops/footnote_drop"
6
+
7
+ module Metanorma
8
+ module Html
9
+ TEMPLATES_ROOT = File.join(__dir__, "templates")
10
+
11
+ Liquid::Environment.default.file_system = Liquid::LocalFileSystem.new(TEMPLATES_ROOT, "_%s.html.liquid")
12
+
13
+ # Renders BasicDocument components to HTML.
14
+ # Subclassed by StandardRenderer and flavor-specific renderers.
15
+ # Owns the full HTML document generation pipeline: body content, header,
16
+ # footer, ToC sidebar, CSS (via Theme), and JavaScript.
17
+ class BaseRenderer
18
+ LOGO_DIR = File.expand_path("../../../data/logos", __dir__)
19
+
20
+ # Map legacy XML class names to clean, semantic names.
21
+ CLASS_MAP = {
22
+ "zzSTDTitle1" => "doc-title",
23
+ "coverpage_docnumber" => "cover-doc-id",
24
+ "coverpage_docstage" => "cover-stage",
25
+ "doctitle-en" => "cover-title",
26
+ "ForewordTitle" => "foreword-title",
27
+ "IntroTitle" => "intro-title",
28
+ "Annex" => "annex-title",
29
+ "Section3" => "section-sub",
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",
41
+ "fmt-caption-label" => "caption-label",
42
+ "fmt-autonum-delim" => "autonum-delim",
43
+ "fmt-element-name" => "element-name",
44
+ "fmt-caption-delim" => "caption-delim",
45
+ "smallcap" => "small-caps",
46
+ "obligation" => "obligation-text",
47
+ "tableblock" => "table-block",
48
+ "Biblio" => "biblio-entry",
49
+ "Note" => "note-block",
50
+ "source" => "term-source",
51
+ "nonboldtitle" => "doc-subtitle",
52
+ "stddocPartNumber" => "doc-part-number",
53
+ "stddocTitle" => "doc-part-title",
54
+ "std_publisher" => "doc-publisher",
55
+ "stdpublisher" => "doc-publisher-name",
56
+ "fmt-xref-label" => "xref-label",
57
+ }.freeze
58
+
59
+ METANORMA_LOGO = "metanorma-logo.svg"
60
+
61
+ def initialize
62
+ @output = +""
63
+ @toc_entries = []
64
+ @figure_entries = []
65
+ @table_entries = []
66
+ @index_term_collector = Component::IndexTermCollector.new
67
+ @footnote_collector = Component::FootnoteCollector.new
68
+ @current_section_id = nil
69
+ @current_section_number = nil
70
+ end
71
+
72
+ # --- Public API ---
73
+
74
+ def to_html
75
+ @output
76
+ end
77
+
78
+ def toc_entries
79
+ @toc_entries
80
+ end
81
+
82
+ # Generate a complete HTML document from a presentation XML document.
83
+ def generate_full_document(document)
84
+ @document = document
85
+ validate_presentation_xml!
86
+
87
+ # First pass: render body content (collects ToC entries as side effect)
88
+ render(@document)
89
+ body = @output
90
+
91
+ assemble_document(body)
92
+ end
93
+
94
+ # --- Flavor configuration hooks (override in subclasses) ---
95
+
96
+ def theme
97
+ @theme ||= Theme.new
98
+ end
99
+
100
+ def flavor_publishers(_doc_id)
101
+ []
102
+ end
103
+
104
+ def flavor_publisher_name
105
+ pubs = flavor_publishers(extract_primary_doc_id)
106
+ pubs.empty? ? nil : pubs.join("/")
107
+ end
108
+
109
+ # Map of publisher name => logo filename. Override in flavor renderers.
110
+ def publisher_logo_map
111
+ {}
112
+ end
113
+
114
+ def flavor_font_url
115
+ theme.font_url
116
+ end
117
+
118
+ # --- Document Assembly ---
119
+
120
+ TEMPLATE_CACHE = Hash.new { |h, k| h[k] = Liquid::Template.parse(File.read(k)) }
121
+
122
+ def render_liquid(template_name, assigns)
123
+ template_path = File.join(TEMPLATES_ROOT, template_name)
124
+ template = TEMPLATE_CACHE[template_path]
125
+ assigns = assigns.transform_keys(&:to_s) if assigns.is_a?(Hash)
126
+ template.render(assigns)
127
+ end
128
+
129
+ def assemble_document(body)
130
+ toc_html = build_toc_html(@toc_entries)
131
+ header = build_header
132
+ footer = build_footer
133
+
134
+ render_liquid("document.html.liquid", {
135
+ "lang" => language,
136
+ "title" => html_title,
137
+ "font_url" => flavor_font_url,
138
+ "styles" => build_styles,
139
+ "header" => header,
140
+ "toc" => toc_html,
141
+ "body" => body,
142
+ "footer" => footer,
143
+ "scripts" => build_scripts,
144
+ })
145
+ end
146
+
147
+ # --- Header and Footer ---
148
+
149
+ def build_header
150
+ doc_id = extract_primary_doc_id
151
+ pub_logos = build_publisher_logos
152
+ pub_name = flavor_publisher_name
153
+ display_id = if pub_name && doc_id && !doc_id.start_with?(pub_name)
154
+ "#{pub_name} #{doc_id}"
155
+ else
156
+ doc_id
157
+ end
158
+
159
+ render_liquid("_header.html.liquid", {
160
+ "publisher_logos" => pub_logos,
161
+ "doc_id" => display_id,
162
+ "doc_title" => header_title_text,
163
+ })
164
+ end
165
+
166
+ def header_title_text
167
+ raw = html_title.to_s.split(" — ").first.to_s.gsub(/<[^>]+>/, "")
168
+ raw.length > 60 ? raw[0, 57] + "..." : raw
169
+ end
170
+
171
+ # Reader controls — kept for backward compat with flavor renderers
172
+ def build_reader_controls
173
+ ""
174
+ end
175
+
176
+ def build_publisher_logos
177
+ publishers = flavor_publishers(extract_primary_doc_id)
178
+ logo_map = publisher_logo_map
179
+ return "" if publishers.empty? && logo_map.empty?
180
+
181
+ # Use flavor-declared publishers; fall back to logo map keys
182
+ display_pubs = publishers.empty? ? logo_map.keys : publishers
183
+
184
+ display_pubs.filter_map do |pub|
185
+ filename = logo_map[pub]
186
+ next unless filename
187
+
188
+ svg = load_logo_svg(filename, height: 26)
189
+ next unless svg
190
+
191
+ "<span class=\"brand-logo\" aria-label=\"#{pub} logo\">#{svg}</span>"
192
+ end.join("\n")
193
+ end
194
+
195
+ def detect_publishers
196
+ flavor_publishers(extract_primary_doc_id)
197
+ end
198
+
199
+ def load_logo_svg(filename, height: 32)
200
+ path = File.join(LOGO_DIR, filename)
201
+ return nil unless File.exist?(path)
202
+
203
+ svg = File.read(path)
204
+ svg = svg.sub(/\A<\?xml[^?]*\?>\s*/, "")
205
+ svg = svg.sub(/\A\s*<!--.*?-->\s*/m, "")
206
+ svg = svg.sub(/<path[^>]*style="fill:#e3000f[^"]*"[^>]*\/>/, "")
207
+ svg = svg.sub(/<svg\s/, '<svg class="header-logo" ')
208
+ if svg.match?(/<svg[^>]*\sheight="[^"]*"/)
209
+ svg = svg.sub(/(<svg[^>]*?)(\sheight="[^"]*")/, "\\1 height=\"#{height}\"")
210
+ else
211
+ svg = svg.sub(/(<svg\b)/, "\\1 height=\"#{height}\"")
212
+ end
213
+ svg = svg.sub(/(<svg[^>]*?)\swidth="[^"]*"/, '\1')
214
+ svg
215
+ rescue StandardError
216
+ nil
217
+ end
218
+
219
+ def build_footer
220
+ mn_logo = load_logo_svg(METANORMA_LOGO, height: 20)
221
+ render_liquid("_footer.html.liquid", {
222
+ "mn_logo" => mn_logo,
223
+ "generated_at" => Time.now.strftime('%Y-%m-%d %H:%M'),
224
+ })
225
+ end
226
+
227
+ # --- ToC generation ---
228
+
229
+ def build_toc_html(entries)
230
+ top_lines = []
231
+ main_lines = if entries.empty?
232
+ ["<li class=\"toc-empty\">No entries</li>"]
233
+ else
234
+ entries.map { |e|
235
+ id = e[:id].to_s
236
+ text = escape_html(e[:text].to_s)
237
+ lvl = e[:level]
238
+ "<li class=\"toc-level-#{lvl}\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
239
+ }
240
+ end
241
+
242
+ # List of Figures — at top of sidebar
243
+ unless @figure_entries.empty?
244
+ 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 { |f|
246
+ id = f[:id].to_s
247
+ text = escape_html(f[:text].to_s)
248
+ 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
+ }
250
+ end
251
+
252
+ # List of Tables — at top of sidebar
253
+ unless @table_entries.empty?
254
+ 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 { |t|
256
+ id = t[:id].to_s
257
+ text = escape_html(t[:text].to_s)
258
+ 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
+ }
260
+ end
261
+
262
+ top_lines << "<li class=\"toc-divider\"></li>" unless top_lines.empty?
263
+
264
+ (top_lines + main_lines).join("\n")
265
+ end
266
+
267
+ # --- Scripts ---
268
+
269
+ def build_scripts
270
+ pipeline = AssetPipeline.new
271
+ compiled = pipeline.compile_js(flavor_js: flavor_js_module)
272
+ "<script>\n#{compiled}\n</script>"
273
+ end
274
+
275
+ def flavor_js_module
276
+ nil
277
+ end
278
+
279
+ def flavor_css_module
280
+ nil
281
+ end
282
+
283
+ def build_styles
284
+ pipeline = AssetPipeline.new
285
+ css = pipeline.compile_css(flavor_css: flavor_css_module)
286
+ "#{theme.to_css_root}\n#{css}\n#{theme.to_css_extras}"
287
+ end
288
+
289
+ # --- Validation ---
290
+
291
+ def validate_presentation_xml!
292
+ has_presentation = check_presentation_markers(@document)
293
+ return if has_presentation
294
+
295
+ raise ArgumentError,
296
+ "HTML generation requires Presentation XML input. " \
297
+ "Semantic XML does not contain formatting data needed for HTML. " \
298
+ "Use a '.presentation.xml' file instead."
299
+ end
300
+
301
+ def check_presentation_markers(node)
302
+ return false unless node
303
+ return false if node.is_a?(String)
304
+
305
+ if node.is_a?(Metanorma::Document::Root) && node.type == "presentation"
306
+ return true
307
+ end
308
+
309
+ if node.is_a?(Lutaml::Model::Serializable)
310
+ return true if (node.public_send(:fmt_title) rescue nil)
311
+ return true if (node.public_send(:displayorder) rescue nil)
312
+
313
+ %i[preface sections annex bibliography].each do |attr|
314
+ val = node.public_send(attr) rescue nil
315
+ next unless val
316
+ Array(val).each { |v| return true if check_presentation_markers(v) }
317
+ end
318
+
319
+ node.each_mixed_content do |child|
320
+ next if child.is_a?(String)
321
+ return true if check_presentation_markers(child)
322
+ end
323
+ end
324
+
325
+ false
326
+ end
327
+
328
+ # --- Metadata extraction ---
329
+
330
+ def language
331
+ bibdata = @document.bibdata
332
+ return "en" unless bibdata
333
+
334
+ langs = bibdata.language
335
+ if langs && !langs.empty?
336
+ lang = langs.find { |l| l.current == "true" } || langs.first
337
+ lang.value || lang.to_s
338
+ else
339
+ "en"
340
+ end
341
+ end
342
+
343
+ def html_title
344
+ bibdata = @document.bibdata
345
+ return "Document" unless bibdata
346
+
347
+ titles = bibdata.titles
348
+ if titles
349
+ title = bibdata.title_for("en")
350
+ title.to_s
351
+ else
352
+ "Document"
353
+ end
354
+ end
355
+
356
+ def extract_primary_doc_id
357
+ bibdata = @document.bibdata
358
+ return nil unless bibdata
359
+
360
+ identifiers = bibdata.doc_identifier
361
+ return nil unless identifiers && !identifiers.empty?
362
+
363
+ first_id = identifiers.first
364
+ text = if first_id.is_a?(String)
365
+ first_id
366
+ elsif first_id.is_a?(Lutaml::Model::Serializable)
367
+ Array(first_id.value).join
368
+ else
369
+ first_id.to_s
370
+ end
371
+ text.strip.empty? ? nil : text.strip
372
+ end
373
+
374
+ # --- CSS loader ---
375
+
376
+ def register_toc_entry(id:, level:, text:)
377
+ @toc_entries << { id: id, level: level, text: text }
378
+ end
379
+
380
+ def register_figure_entry(id:, text:)
381
+ @figure_entries << { id: id, text: text }
382
+ end
383
+
384
+ def register_table_entry(id:, text:)
385
+ @table_entries << { id: id, text: text }
386
+ end
387
+
388
+ attr_reader :index_term_collector, :footnote_collector
389
+
390
+ def extract_plain_text(node)
391
+ return node.to_s if node.is_a?(String)
392
+ return extract_text_value(node).to_s unless node.is_a?(Lutaml::Model::Serializable)
393
+
394
+ parts = []
395
+ xml_mapping = node.class.mappings_for(:xml, node.lutaml_register)
396
+
397
+ if node.element_order.is_a?(Array) && xml_mapping
398
+ element_to_attr = {}
399
+ xml_mapping.mapping_elements_hash.each_value do |rule_or_array|
400
+ Array(rule_or_array).each do |rule|
401
+ element_to_attr[rule.name.to_s] = rule.to
402
+ end
403
+ end
404
+
405
+ indices = Hash.new(0)
406
+ node.element_order.each do |el|
407
+ next unless el.is_a?(Lutaml::Xml::Element)
408
+ if el.text?
409
+ parts << el.text_content.to_s
410
+ elsif el.name == "tab"
411
+ parts << "\u00A0\u00A0"
412
+ elsif el.name == "br"
413
+ parts << " "
414
+ elsif el.element?
415
+ attr_name = element_to_attr[el.name]
416
+ next unless attr_name
417
+
418
+ coll = node.public_send(attr_name)
419
+ obj = if coll.is_a?(Array)
420
+ idx = indices[attr_name]
421
+ indices[attr_name] += 1
422
+ coll[idx]
423
+ else
424
+ coll
425
+ end
426
+ parts << extract_plain_text(obj) if obj
427
+ end
428
+ end
429
+ end
430
+
431
+ # Fallback: try .text
432
+ if parts.join.strip.empty?
433
+ t = safe_attr(node, :text)
434
+ parts << (t.is_a?(Array) ? t.join : t.to_s) if t
435
+ end
436
+
437
+ parts.join.strip.gsub(/\u00A0/, " ")
438
+ end
439
+
440
+ # Dispatch to the appropriate render method based on node class.
441
+ def render(node, **opts)
442
+ case node
443
+ when Metanorma::Document::Components::Paragraphs::ParagraphBlock
444
+ render_paragraph(node, **opts)
445
+ when Metanorma::Document::Components::Tables::TableBlock
446
+ render_table(node, **opts)
447
+ when Metanorma::Document::Components::Lists::UnorderedList
448
+ render_unordered_list(node, **opts)
449
+ when Metanorma::Document::Components::Lists::OrderedList
450
+ render_ordered_list(node, **opts)
451
+ when Metanorma::Document::Components::Lists::DefinitionList
452
+ render_definition_list(node, **opts)
453
+ when Metanorma::Document::Components::AncillaryBlocks::FigureBlock
454
+ render_figure(node, **opts)
455
+ when Metanorma::Document::Components::Blocks::NoteBlock
456
+ render_note(node, **opts)
457
+ when Metanorma::Document::Components::AncillaryBlocks::ExampleBlock
458
+ render_example(node, **opts)
459
+ when Metanorma::Document::Components::AncillaryBlocks::SourcecodeBlock
460
+ render_sourcecode(node, **opts)
461
+ when Metanorma::Document::Components::AncillaryBlocks::FormulaBlock
462
+ render_formula(node, **opts)
463
+ when Metanorma::Document::Components::MultiParagraph::QuoteBlock
464
+ render_quote(node, **opts)
465
+ when Metanorma::Document::Components::MultiParagraph::AdmonitionBlock
466
+ render_admonition(node, **opts)
467
+ when Metanorma::Document::Components::Sections::HierarchicalSection
468
+ render_hierarchical_section(node, **opts)
469
+ when Metanorma::Document::Components::Sections::BasicSection
470
+ render_basic_section(node, **opts)
471
+ when Metanorma::Document::Components::Sections::ContentSection
472
+ render_content_section(node, **opts)
473
+ when Metanorma::Document::Components::EmptyElements::PageBreakElement
474
+ ""
475
+ when Metanorma::Document::Components::IdElements::Bookmark
476
+ render_bookmark(node)
477
+ when Metanorma::Document::Components::Inline::SemxElement
478
+ render_semx_content(node)
479
+ when String
480
+ escape_html(node)
481
+ else
482
+ ""
483
+ end
484
+ end
485
+
486
+ private
487
+
488
+ # --- Block-level rendering ---
489
+
490
+ def render_paragraph(p, **_opts)
491
+ attrs = element_attrs(id: safe_attr(p, :id), class: safe_attr(p, :class_attr), style: alignment_style(safe_attr(p, :alignment)))
492
+ tag("p", attrs) { render_mixed_inline(p) }
493
+ end
494
+
495
+ def render_table(table, **_opts)
496
+ attrs = element_attrs(id: safe_attr(table, :id), class: "table-block")
497
+ table_id = safe_attr(table, :id)
498
+ name_el = safe_attr(table, :fmt_name) || safe_attr(table, :name)
499
+ if table_id && name_el
500
+ register_table_entry(id: table_id, text: extract_plain_text(name_el))
501
+ end
502
+ col_count = table_column_count(table)
503
+ @output << "<div class=\"table-scroll-wrapper\">"
504
+ tag("table", attrs) do
505
+ name_el = safe_attr(table, :fmt_name) || safe_attr(table, :name)
506
+ if name_el
507
+ @output << "<caption>"
508
+ render_inline_element(name_el)
509
+ @output << "</caption>"
510
+ end
511
+ @output << "<colgroup>" if table.colgroup
512
+ render_table_colgroup(table.colgroup) if table.colgroup
513
+ @output << "</colgroup>" if table.colgroup
514
+ render_table_section(table.thead, "thead") if table.thead
515
+ render_table_section(table.tbody, "tbody") if table.tbody
516
+ if table.tfoot || (table.note && !table.note.empty?)
517
+ @output << "<tfoot>"
518
+ render_table_section_rows(table.tfoot) if table.tfoot
519
+ if table.note && !table.note.empty?
520
+ @output << "<tr><td colspan=\"#{col_count}\" class=\"table-notes\">"
521
+ table.note.each { |n| render_note(n) }
522
+ @output << "</td></tr>"
523
+ end
524
+ @output << "</tfoot>"
525
+ end
526
+ end
527
+ @output << "</div>"
528
+ end
529
+
530
+ def table_column_count(table)
531
+ if table.colgroup&.col && !table.colgroup.col.empty?
532
+ return table.colgroup.col.size
533
+ end
534
+ # Walk all rows to find max column count, accounting for colspan
535
+ max_cols = 0
536
+ [:thead, :tbody, :tfoot].each do |section|
537
+ sec = table.public_send(section)
538
+ next unless sec&.tr
539
+ sec.tr.each do |tr|
540
+ cols = 0
541
+ Array(tr.th).each { |th| cols += (th.colspan && th.colspan > 1) ? th.colspan : 1 }
542
+ Array(tr.td).each { |td| cols += (td.colspan && td.colspan > 1) ? td.colspan : 1 }
543
+ max_cols = cols if cols > max_cols
544
+ end
545
+ end
546
+ max_cols > 0 ? max_cols : 1
547
+ end
548
+
549
+ def render_table_colgroup(colgroup)
550
+ colgroup.col&.each do |col|
551
+ attrs = element_attrs(style: col.width ? "width: #{col.width}" : nil)
552
+ @output << "<col#{attrs}>"
553
+ end
554
+ end
555
+
556
+ def render_table_section(section, tag_name)
557
+ @output << "<#{tag_name}>"
558
+ render_table_section_rows(section)
559
+ @output << "</#{tag_name}>"
560
+ end
561
+
562
+ def render_table_section_rows(section)
563
+ section.tr&.each do |tr|
564
+ @output << "<tr>"
565
+ walked = walk_ordered(tr) do |type, obj|
566
+ next unless type == :element
567
+ render_table_cell(obj)
568
+ end
569
+ unless walked
570
+ Array(tr.th).each { |th| render_table_cell(th, "th") }
571
+ Array(tr.td).each { |td| render_table_cell(td, "td") }
572
+ end
573
+ @output << "</tr>"
574
+ end
575
+ end
576
+
577
+ def render_unordered_list(ul, **_opts)
578
+ attrs = element_attrs(id: safe_attr(ul, :id), class: safe_attr(ul, :class_attr))
579
+ tag("ul", attrs) do
580
+ ul.listitem&.each { |li| render_list_item(li) }
581
+ end
582
+ end
583
+
584
+ def render_table_cell(cell, force_tag = nil)
585
+ tag_name = force_tag || (cell.is_a?(Metanorma::Document::Components::Tables::HeaderTableCell) ? "th" : "td")
586
+ attrs = element_attrs(
587
+ colspan: safe_attr(cell, :colspan),
588
+ rowspan: safe_attr(cell, :rowspan),
589
+ align: safe_attr(cell, :alignment),
590
+ valign: safe_attr(cell, :vertical_alignment),
591
+ )
592
+ @output << "<#{tag_name}#{attrs}>"
593
+ render_cell_content(cell)
594
+ @output << "</#{tag_name}>"
595
+ end
596
+
597
+ def render_ordered_list(ol, **_opts)
598
+ attrs = element_attrs(id: safe_attr(ol, :id), class: safe_attr(ol, :class_attr), start: safe_attr(ol, :start), type: safe_attr(ol, :type_attr))
599
+ tag("ol", attrs) do
600
+ ol.listitem&.each { |li| render_list_item(li) }
601
+ end
602
+ end
603
+
604
+ def render_list_item(li)
605
+ li_id = safe_attr(li, :id)
606
+ attrs = li_id ? %( id="#{escape_html(li_id)}") : ""
607
+ @output << "<li#{attrs}>"
608
+ render_mixed_content_in_order(li)
609
+ @output << "</li>"
610
+ end
611
+
612
+ # Render all children of a mixed-content node in document order,
613
+ # dispatching block elements to their render methods.
614
+ def render_mixed_content_in_order(node)
615
+ node.each_mixed_content do |child|
616
+ case child
617
+ when String
618
+ @output << escape_html(child)
619
+ else
620
+ if block_element?(child)
621
+ render(child)
622
+ else
623
+ render_inline_element(child)
624
+ end
625
+ end
626
+ end
627
+ end
628
+
629
+ # Collect all renderable children from a node in document order,
630
+ # sorted by displayorder when available. Uses walk_ordered to traverse
631
+ # element_order, and also gathers typed attributes that may not appear
632
+ # in element_order (e.g. terms, definitions on section models).
633
+ def collect_ordered_children(section)
634
+ children = []
635
+
636
+ walk_ordered(section) do |type, obj|
637
+ next if type == :text || type == :tab
638
+ children << obj
639
+ end
640
+
641
+ # Gather typed attributes that may not appear in element_order
642
+ supplementary_attrs = %i[terms definitions]
643
+ supplementary_attrs.each do |attr|
644
+ val = safe_attr(section, attr)
645
+ next if val.nil?
646
+ Array(val).each do |v|
647
+ children << v unless children.include?(v)
648
+ end
649
+ end
650
+
651
+ children.compact!
652
+ sort_by_displayorder(children)
653
+ end
654
+
655
+ # Render children of a section in displayorder, skipping title elements.
656
+ def render_ordered_content(section, level = 1)
657
+ children = collect_ordered_children(section)
658
+ children.each do |node|
659
+ next if node.is_a?(String)
660
+ next if is_title_element?(node, section)
661
+
662
+ render(node, level: level + 1)
663
+ end
664
+ end
665
+
666
+ def sort_by_displayorder(children)
667
+ children.sort_by do |node|
668
+ order = node.displayorder rescue nil
669
+ order &&= order.to_i
670
+ order || Float::INFINITY
671
+ end
672
+ end
673
+
674
+ def render_definition_list(dl, **_opts)
675
+ attrs = element_attrs(id: safe_attr(dl, :id))
676
+ tag("dl", attrs) do
677
+ dl.dt&.each_with_index do |dt, i|
678
+ @output << "<dt>"
679
+ render_mixed_inline(dt)
680
+ @output << "</dt>"
681
+ dd = dl.dd&.[](i)
682
+ if dd
683
+ @output << "<dd>"
684
+ render_mixed_inline(dd)
685
+ @output << "</dd>"
686
+ end
687
+ end
688
+ end
689
+ end
690
+
691
+ def render_figure(figure, **_opts)
692
+ attrs = element_attrs(id: safe_attr(figure, :id), class: "figure")
693
+ fig_id = safe_attr(figure, :id)
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
715
+ end
716
+
717
+ def render_image(image)
718
+ src_val = safe_attr(image, :src) || safe_attr(image, :source)
719
+ attrs = element_attrs(
720
+ id: safe_attr(image, :id),
721
+ src: src_val,
722
+ alt: safe_attr(image, :alt),
723
+ height: safe_attr(image, :height),
724
+ width: safe_attr(image, :width),
725
+ )
726
+ @output << "<img#{attrs} />"
727
+ end
728
+
729
+ def render_video(video)
730
+ attrs = element_attrs(
731
+ id: safe_attr(video, :id),
732
+ src: safe_attr(video, :src),
733
+ )
734
+ @output << "<video#{attrs} controls></video>"
735
+ end
736
+
737
+ def render_audio(audio)
738
+ attrs = element_attrs(
739
+ id: safe_attr(audio, :id),
740
+ src: safe_attr(audio, :src),
741
+ )
742
+ @output << "<audio#{attrs} controls></audio>"
743
+ end
744
+
745
+ def render_note(note, **_opts)
746
+ attrs = element_attrs(id: safe_attr(note, :id), class: "note-block")
747
+ tag("div", attrs) do
748
+ label = extract_block_label(note, "NOTE")
749
+ @output << %(<span class="note-label">#{escape_html(label)}</span>&nbsp;)
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
759
+ end
760
+
761
+ def render_example(example, **_opts)
762
+ attrs = element_attrs(id: safe_attr(example, :id), class: "example")
763
+ tag("div", attrs) do
764
+ label = extract_block_label(example, "EXAMPLE")
765
+ @output << %(<span class="example-label">#{escape_html(label)}</span>&nbsp;)
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
778
+ end
779
+
780
+ def render_sourcecode(sc, **_opts)
781
+ attrs = element_attrs(id: safe_attr(sc, :id), class: "sourcecode")
782
+ lang = safe_attr(sc, :lang)
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 (&lt; etc); decode first to get raw text, then escape
801
+ # for HTML output.
802
+ raw_text = code_text.gsub("&lt;", "<").gsub("&gt;", ">").gsub("&amp;", "&").gsub("&quot;", "\"")
803
+ @output << escape_html(raw_text)
804
+ @output << "</code></pre>"
805
+ end
806
+ end
807
+
808
+ def render_formula(formula, **_opts)
809
+ attrs = element_attrs(id: safe_attr(formula, :id), class: "formula")
810
+ tag("div", attrs) do
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
831
+ end
832
+
833
+ def render_quote(quote, **_opts)
834
+ attrs = element_attrs(id: safe_attr(quote, :id), class: "quote")
835
+ tag("blockquote", attrs) do
836
+ quote.paragraphs&.each { |para| render_paragraph(para) }
837
+ quote.ul&.each { |ul| render_unordered_list(ul) }
838
+ quote.ol&.each { |ol| render_ordered_list(ol) }
839
+ if quote.attribution
840
+ @output << "<footer>"
841
+ render_mixed_inline(quote.attribution)
842
+ @output << "</footer>"
843
+ end
844
+ end
845
+ end
846
+
847
+ def render_admonition(admonition, **_opts)
848
+ type = safe_attr(admonition, :type) || "note"
849
+ attrs = element_attrs(id: safe_attr(admonition, :id), class: "admonition #{type}")
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
854
+ end
855
+
856
+ def render_bookmark(bookmark)
857
+ @output << %(<a id="#{escape_html(safe_attr(bookmark, :id).to_s)}"></a>)
858
+ end
859
+
860
+ # --- Section rendering ---
861
+
862
+ def render_basic_section(section, level: 1, **_opts)
863
+ attrs = element_attrs(id: safe_attr(section, :id))
864
+ tag("div", attrs) do
865
+ render_section_title(section, level)
866
+ section.blocks&.each { |block| render(block) }
867
+ section.notes&.each { |note| render_note(note) }
868
+ end
869
+ end
870
+
871
+ def render_hierarchical_section(section, level: 1, **_opts)
872
+ attrs = element_attrs(id: safe_attr(section, :id))
873
+ tag("div", attrs) do
874
+ render_section_title(section, level)
875
+ render_section_content(section, level)
876
+ section.subsections&.each { |sub| render(sub, level: level + 1) }
877
+ end
878
+ end
879
+
880
+ def render_content_section(section, level: 1, **_opts)
881
+ render_hierarchical_section(section, level: level, **_opts)
882
+ end
883
+
884
+ def render_section_title(section, level)
885
+ titles = section.title
886
+ return unless titles && !titles.empty?
887
+
888
+ h = "h#{[[level, 6].min, 1].max}"
889
+ title_text = extract_title_text(titles)
890
+ @output << "<#{h}>#{escape_html(title_text)}</#{h}>"
891
+ end
892
+
893
+ def render_section_content(section, _level)
894
+ section.blocks&.each { |block| render(block) }
895
+ section.notes&.each { |note| render_note(note) }
896
+ end
897
+
898
+ # --- Inline content rendering ---
899
+
900
+ # Walk element_order in document order, resolving each element to its
901
+ # Ruby object via the XML mapping, and yielding (element_order_entry, resolved_object)
902
+ # to the given block. Handles tab elements as nbsp. Skips elements not in the mapping.
903
+ #
904
+ # This is the single ordered-walk primitive used by all mixed content renderers:
905
+ # - render_mixed_inline (inline-only)
906
+ # - render_cell_content (mixed block/inline)
907
+ # - render_semx_content (filtered display attrs only)
908
+ def walk_ordered(node, allow_filter: nil)
909
+ return false unless node.is_a?(Lutaml::Model::Serializable)
910
+ return false unless node.element_order.is_a?(Array) && !node.element_order.empty?
911
+
912
+ xml_mapping = node.class.mappings_for(:xml, node.lutaml_register)
913
+ return false unless xml_mapping
914
+
915
+ element_to_attr = {}
916
+ xml_mapping.mapping_elements_hash.each_value do |rule_or_array|
917
+ Array(rule_or_array).each do |rule|
918
+ element_to_attr[rule.name] = rule.to
919
+ element_to_attr[rule.name.to_s] = rule.to if rule.name.is_a?(Symbol)
920
+ end
921
+ end
922
+
923
+ skip_indices = build_semx_skip_set(node)
924
+
925
+ indices = Hash.new(0)
926
+
927
+ node.element_order.each_with_index do |el, i|
928
+ next if skip_indices.include?(i)
929
+
930
+ if el.text?
931
+ text = el.text_content
932
+ yield :text, text if text && block_given?
933
+ elsif el.element?
934
+ # Handle <tab/> elements
935
+ if el.name == "tab"
936
+ yield :tab, nil if block_given?
937
+ next
938
+ end
939
+
940
+ attr_name = element_to_attr[el.name]
941
+ next unless attr_name
942
+
943
+ # Apply optional filter (used by semx to skip semantic attrs)
944
+ next if allow_filter && !allow_filter.include?(attr_name)
945
+
946
+ coll = node.send(attr_name)
947
+ obj = if coll.is_a?(Array)
948
+ idx = indices[attr_name]
949
+ indices[attr_name] += 1
950
+ coll[idx]
951
+ else
952
+ coll
953
+ end
954
+ yield :element, obj if obj && block_given?
955
+ end
956
+ end
957
+ true
958
+ end
959
+
960
+ # In presentation XML, semantic elements are followed by <semx> wrappers
961
+ # or <fmt-*> display elements. Skip source elements to avoid duplicates.
962
+ def build_semx_skip_set(node)
963
+ skip_after = {
964
+ "link" => "semx",
965
+ "xref" => "semx",
966
+ "eref" => "semx",
967
+ "stem" => nil,
968
+ "concept" => "fmt-concept",
969
+ "refterm" => nil,
970
+ "renderterm" => nil,
971
+ "origin" => "semx",
972
+ }
973
+ skip = {}
974
+ node.element_order.each_with_index do |el, i|
975
+ next unless el.element?
976
+ next_tag = skip_after[el.name]
977
+ next unless next_tag
978
+
979
+ next_el = node.element_order[i + 1]
980
+ if next_tag.nil?
981
+ skip[i] = true
982
+ elsif next_el && next_el.element? && next_el.name == next_tag
983
+ skip[i] = true
984
+ end
985
+ end
986
+ skip
987
+ end
988
+
989
+ # Table cells can contain both block-level content (p, ul, ol, dl)
990
+ # and inline content (text, em, strong, etc.) in document order.
991
+ def render_cell_content(cell)
992
+ walked = walk_ordered(cell) do |type, obj|
993
+ case type
994
+ when :text
995
+ @output << escape_html(obj)
996
+ when :tab
997
+ @output << "\u00a0\u00a0"
998
+ when :element
999
+ if block_element?(obj)
1000
+ render(obj)
1001
+ else
1002
+ render_inline_element(obj)
1003
+ end
1004
+ end
1005
+ end
1006
+ unless walked
1007
+ render_mixed_content_in_order(cell)
1008
+ end
1009
+ end
1010
+
1011
+ def render_mixed_inline(node)
1012
+ # Models using map_all_content (e.g. RawParagraph): raw XML in content
1013
+ if raw_content_node?(node)
1014
+ raw = node.content
1015
+ if raw.is_a?(String) && !raw.strip.empty?
1016
+ @output << render_raw_content(raw)
1017
+ return
1018
+ end
1019
+ end
1020
+
1021
+ if node.is_a?(Lutaml::Model::Serializable) && node.element_order && !node.element_order.empty?
1022
+ render_ordered_inline(node)
1023
+ elsif node.is_a?(Lutaml::Model::Serializable)
1024
+ node.each_mixed_content do |child|
1025
+ case child
1026
+ when String
1027
+ @output << escape_html(child)
1028
+ else
1029
+ render_inline_element(child)
1030
+ end
1031
+ end
1032
+ else
1033
+ render_inline_collections(node)
1034
+ end
1035
+ end
1036
+
1037
+ def raw_content_node?(node)
1038
+ node.is_a?(Metanorma::IsoDocument::RawParagraph)
1039
+ end
1040
+
1041
+ # Iterate element_order directly, preserving whitespace text nodes
1042
+ # that each_mixed_content drops (it skips text where text.strip.empty?)
1043
+ def render_ordered_inline(node)
1044
+ walked = walk_ordered(node) do |type, obj|
1045
+ case type
1046
+ when :text
1047
+ @output << escape_html(obj)
1048
+ when :tab
1049
+ @output << "\u00a0\u00a0"
1050
+ when :element
1051
+ render_inline_element(obj)
1052
+ end
1053
+ end
1054
+ unless walked
1055
+ node.each_mixed_content do |child|
1056
+ case child
1057
+ when String
1058
+ @output << escape_html(child)
1059
+ else
1060
+ render_inline_element(child)
1061
+ end
1062
+ end
1063
+ end
1064
+ rescue StandardError
1065
+ node.each_mixed_content do |child|
1066
+ case child
1067
+ when String
1068
+ @output << escape_html(child)
1069
+ else
1070
+ render_inline_element(child)
1071
+ end
1072
+ end
1073
+ end
1074
+
1075
+ def render_inline_element(element)
1076
+ return "" if element.nil?
1077
+
1078
+ case element
1079
+ when String
1080
+ @output << escape_html(element)
1081
+ when Metanorma::Document::Components::Inline::EmRawElement
1082
+ render_inline_tag("em", element)
1083
+ when Metanorma::Document::Components::Inline::StrongRawElement
1084
+ render_inline_tag("strong", element)
1085
+ when Metanorma::Document::Components::Inline::TtElement
1086
+ render_inline_tag("tt", element)
1087
+ when Metanorma::Document::Components::Inline::SubElement
1088
+ render_inline_tag("sub", element)
1089
+ when Metanorma::Document::Components::Inline::SupElement
1090
+ render_inline_tag("sup", element)
1091
+ when Metanorma::Document::Components::Inline::SmallCapElement
1092
+ render_inline_tag("span", element, class: "small-caps")
1093
+ when Metanorma::Document::Components::TextElements::UnderlineElement
1094
+ render_inline_tag("u", element)
1095
+ when Metanorma::Document::Components::TextElements::StrikeElement
1096
+ render_inline_tag("s", element)
1097
+ when Metanorma::Document::Components::Inline::BrElement
1098
+ @output << "<br />"
1099
+ when Metanorma::Document::Components::Inline::TabElement
1100
+ @output << "\u00a0\u00a0"
1101
+ when Metanorma::Document::Components::Inline::LinkElement
1102
+ render_link(element)
1103
+ when Metanorma::Document::Components::Inline::XrefElement
1104
+ # Source element — skip; rendered via fmt-xref in semx wrapper
1105
+ nil
1106
+ when Metanorma::Document::Components::Inline::ErefElement
1107
+ # Source element — skip; rendered via fmt-xref in semx wrapper
1108
+ nil
1109
+ when Metanorma::Document::Components::Inline::SpanElement
1110
+ attrs = element_attrs(style: safe_attr(element, :style), class: safe_attr(element, :class_attr))
1111
+ tag("span", attrs) { render_mixed_inline(element) }
1112
+ when Metanorma::Document::Components::Inline::FnElement
1113
+ render_fn(element)
1114
+ when Metanorma::Document::Components::Inline::ConceptElement
1115
+ render_concept(element)
1116
+ when Metanorma::Document::Components::Inline::StemInlineElement
1117
+ # Source element — skip; rendered via FmtStemElement
1118
+ nil
1119
+ when Metanorma::Document::Components::TextElements::StemElement
1120
+ @output << render_stem_content(element)
1121
+ when Metanorma::Document::Components::Inline::SemxElement
1122
+ render_semx_content(element)
1123
+ when Metanorma::Document::Components::Inline::FmtNameElement,
1124
+ Metanorma::Document::Components::Inline::FmtTitleElement,
1125
+ Metanorma::Document::Components::Inline::FmtXrefLabelElement,
1126
+ Metanorma::Document::Components::Inline::FmtFnLabelElement,
1127
+ Metanorma::Document::Components::Inline::FmtConceptElement,
1128
+ Metanorma::Document::Components::Inline::FmtAnnotationStartElement,
1129
+ Metanorma::Document::Components::Inline::FmtAnnotationEndElement,
1130
+ Metanorma::Document::Components::Inline::FmtAnnotationBodyElement,
1131
+ Metanorma::Document::Components::Inline::VariantTitleElement,
1132
+ Metanorma::Document::Components::Inline::LocalizedStringElement,
1133
+ Metanorma::Document::Components::Inline::TitleWithAnnotationElement,
1134
+ Metanorma::Document::Components::Inline::BiblioTagElement,
1135
+ Metanorma::Document::Components::Inline::NameWithIdElement,
1136
+ Metanorma::Document::Components::Inline::DisplayTextElement,
1137
+ Metanorma::Document::Components::Inline::FmtFootnoteContainerElement,
1138
+ Metanorma::Document::Components::Inline::FmtFnBodyElement,
1139
+ Metanorma::Document::Components::Inline::FmtPreferredElement,
1140
+ Metanorma::Document::Components::Inline::FmtDefinitionElement,
1141
+ Metanorma::Document::Components::Inline::FmtTermsourceElement,
1142
+ Metanorma::Document::Components::Inline::FmtAdmittedElement,
1143
+ Metanorma::Document::Components::Inline::FmtIdentifierElement,
1144
+ Metanorma::Document::Components::Inline::FmtSourcecodeElement
1145
+ render_mixed_inline(element)
1146
+ when Metanorma::Document::Components::Inline::FmtXrefElement
1147
+ target = safe_attr(element, :target) || safe_attr(element, :to_attr)
1148
+ if target
1149
+ attrs = element_attrs(href: "##{escape_html(target)}", class: "xref")
1150
+ tag("a", attrs) { render_mixed_inline(element) }
1151
+ else
1152
+ render_mixed_inline(element)
1153
+ end
1154
+ when Metanorma::Document::Components::Inline::FmtStemElement
1155
+ render_fmt_stem(element)
1156
+ when Metanorma::Document::Components::Inline::CommaElement,
1157
+ Metanorma::Document::Components::Inline::EnumCommaElement
1158
+ @output << ", "
1159
+ when Metanorma::Document::Components::IdElements::Bookmark
1160
+ render_bookmark(element)
1161
+ when Metanorma::Document::Components::IdElements::Image
1162
+ render_image(element)
1163
+ when Metanorma::Document::Components::Inline::MathElement
1164
+ @output << element.content.to_s
1165
+ when Metanorma::Document::Components::Inline::AsciimathElement
1166
+ @output << %(<span class="stem">#{escape_html(Array(element.text).join)}</span>)
1167
+ when Metanorma::Document::Components::EmptyElements::IndexElement,
1168
+ Metanorma::Document::Components::ReferenceElements::IndexXrefElement
1169
+ collect_index_term(element)
1170
+ ""
1171
+ when Metanorma::Document::Components::Blocks::NoteBlock
1172
+ render_note(element)
1173
+ else
1174
+ # Attempt generic mixed content rendering for unknown inline types
1175
+ if element.is_a?(Lutaml::Model::Serializable) && element.mixed?
1176
+ render_mixed_inline(element)
1177
+ end
1178
+ end
1179
+ end
1180
+
1181
+ def render_inline_collections(node)
1182
+ # Fallback: render text and inline collections sequentially
1183
+ texts = node.text
1184
+ if texts.is_a?(Array)
1185
+ texts.each do |t|
1186
+ if t.is_a?(Metanorma::Document::Components::Inline::MathElement)
1187
+ @output << t.content.to_s
1188
+ elsif t.is_a?(Metanorma::Document::Components::Inline::AsciimathElement)
1189
+ @output << %(<span class="stem">#{escape_html(Array(t.text).join)}</span>)
1190
+ else
1191
+ @output << escape_html(t.to_s)
1192
+ end
1193
+ end
1194
+ elsif texts.is_a?(String)
1195
+ @output << escape_html(texts)
1196
+ end
1197
+
1198
+ inline_attrs = %i[em strong smallcap sub sup tt underline strike
1199
+ xref eref link span stem concept fn br tab keyword
1200
+ fmt_annotation_start fmt_annotation_end
1201
+ fmt_stem fmt_fn_label fmt_concept
1202
+ bookmark image semx fmt_xref_label]
1203
+ inline_attrs.each do |attr|
1204
+ values = safe_attr(node, attr)
1205
+ next if values.nil?
1206
+
1207
+ Array(values).each { |v| render_inline_element(v) }
1208
+ end
1209
+ end
1210
+
1211
+ # Render SemxElement display content only, skipping semantic linkage.
1212
+ # semx wraps both semantic data (origin, xref, source, etc.) and
1213
+ # display content (fmt-xref, span, strong, etc.). Only render display.
1214
+ def render_semx_content(element)
1215
+ display_attrs = %i[text fmt_xref fmt_link fmt_concept span strong em sup p semx
1216
+ asciimath math sub_child tt_child br_child tab_child
1217
+ stem_child figure_child formula_child sourcecode_child]
1218
+ label_stripped = false
1219
+
1220
+ walked = walk_ordered(element, allow_filter: display_attrs) do |type, obj|
1221
+ case type
1222
+ when :text
1223
+ text = obj
1224
+ if !label_stripped
1225
+ text = deduplicate_semx_text(text, @output)
1226
+ label_stripped = true
1227
+ end
1228
+ @output << escape_html(text)
1229
+ when :element
1230
+ if obj.is_a?(Metanorma::Document::Components::Paragraphs::ParagraphBlock)
1231
+ render_paragraph(obj)
1232
+ else
1233
+ render_inline_element(obj)
1234
+ end
1235
+ end
1236
+ end
1237
+
1238
+ unless walked
1239
+ display_attrs.each do |attr|
1240
+ val = safe_attr(element, attr)
1241
+ next if val.nil?
1242
+ if val.is_a?(Array)
1243
+ val.each do |v|
1244
+ if v.is_a?(Metanorma::Document::Components::Paragraphs::ParagraphBlock)
1245
+ render_paragraph(v)
1246
+ else
1247
+ render_inline_element(v)
1248
+ end
1249
+ end
1250
+ elsif val.is_a?(String)
1251
+ @output << escape_html(val)
1252
+ else
1253
+ render_inline_element(val)
1254
+ end
1255
+ end
1256
+ end
1257
+ end
1258
+
1259
+ def deduplicate_semx_text(semx_text, output)
1260
+ first_word = semx_text[/\A\s*(\S+)/, 1]
1261
+ return semx_text unless first_word
1262
+
1263
+ tail = output[-200..]
1264
+ return semx_text unless tail && tail.rstrip.end_with?(first_word)
1265
+
1266
+ semx_text.sub(/\A\s*#{Regexp.escape(first_word)}\s*/, "")
1267
+ end
1268
+
1269
+ def render_inline_tag(tag_name, element, **extra_attrs)
1270
+ tag(tag_name, element_attrs(**extra_attrs)) { render_mixed_inline(element) }
1271
+ end
1272
+
1273
+ # Process raw XML content from map_all_content models (e.g. RawParagraph).
1274
+ # Strips source elements (xref, eref, stem) that have a following <semx>
1275
+ # wrapper, keeping only the semx display content.
1276
+ def render_raw_content(raw_xml)
1277
+ doc = Nokogiri::XML.fragment(raw_xml)
1278
+ # Convert fmt-link elements to HTML <a> tags before stripping wrappers
1279
+ doc.css("fmt-link").each do |el|
1280
+ target = el["target"] || el["href"]
1281
+ if target
1282
+ display_text = target.sub(/\Amailto:/, "")
1283
+ a = doc.document.create_element("a", display_text, "href" => target)
1284
+ el.replace(a)
1285
+ else
1286
+ el.replace(el.children)
1287
+ end
1288
+ end
1289
+ # Remove source elements that precede a <semx> sibling,
1290
+ # deduplicating any label text that appears in both the source
1291
+ # paragraph and the semx display content.
1292
+ doc.traverse do |node|
1293
+ next unless node.element?
1294
+ next unless %w[xref eref stem link].include?(node.name)
1295
+ next_sib = node.next_sibling
1296
+ while next_sib.is_a?(Nokogiri::XML::Text) && next_sib.text.strip.empty?
1297
+ next_sib = next_sib.next_sibling
1298
+ end
1299
+ next unless next_sib && next_sib.element? && next_sib.name == "semx"
1300
+
1301
+ deduplicate_semx_label(node, next_sib)
1302
+ node.remove
1303
+ end
1304
+ # Strip presentation wrappers, keeping inner content
1305
+ %w[semx fmt-xref].each do |tag|
1306
+ doc.css(tag).each { |el| el.replace(el.children) }
1307
+ end
1308
+ doc.inner_html
1309
+ end
1310
+
1311
+ def deduplicate_semx_label(source_node, semx_node)
1312
+ first_text = semx_node.children.find { |c| c.text? && !c.text.strip.empty? }
1313
+ return unless first_text
1314
+
1315
+ semx_prefix = first_text.text[/\A(\s*\S+)/, 1]
1316
+ return unless semx_prefix && !semx_prefix.strip.empty?
1317
+
1318
+ prev = source_node.previous_sibling
1319
+ return unless prev.is_a?(Nokogiri::XML::Text)
1320
+
1321
+ label = semx_prefix.strip
1322
+ prev_text = prev.text.rstrip
1323
+ return unless prev_text.end_with?(label)
1324
+
1325
+ prev.content = prev_text.sub(/#{Regexp.escape(label)}\s*\z/, "")
1326
+ first_text.content = first_text.text.sub(/\A\s*#{Regexp.escape(label)}\s*/, "")
1327
+ end
1328
+
1329
+ def render_link(link)
1330
+ target = safe_attr(link, :target) || safe_attr(link, :href)
1331
+ attrs = element_attrs(href: target, id: safe_attr(link, :id), class: safe_attr(link, :class_attr))
1332
+ tag("a", attrs) do
1333
+ content = safe_attr(link, :content)
1334
+ if content && !Array(content).join.strip.empty?
1335
+ render_mixed_inline(link)
1336
+ else
1337
+ display_text = target.to_s.sub(/\Amailto:/, "")
1338
+ @output << escape_html(display_text)
1339
+ end
1340
+ end
1341
+ end
1342
+
1343
+ def render_xref(xref)
1344
+ target = safe_attr(xref, :target) || safe_attr(xref, :to_attr)
1345
+ attrs = element_attrs(href: "##{escape_html(target)}", id: safe_attr(xref, :id), class: safe_attr(xref, :class_attr))
1346
+ tag("a", attrs) { render_mixed_inline(xref) }
1347
+ end
1348
+
1349
+ def render_eref(eref)
1350
+ citeas = safe_attr(eref, :citeas)
1351
+ if citeas
1352
+ @output << escape_html(citeas)
1353
+ else
1354
+ render_mixed_inline(eref)
1355
+ end
1356
+ end
1357
+
1358
+ def render_fn(fn)
1359
+ fn_id = safe_attr(fn, :id)
1360
+ number = @footnote_collector.register(fn)
1361
+
1362
+ attrs = element_attrs(id: fn_id, class: "fn-marker")
1363
+ tag("span", attrs) do
1364
+ label = safe_attr(fn, :fn_label) || safe_attr(fn, :reference)
1365
+ if label
1366
+ @output << "<sup><a href=\"##{escape_html("footnote-#{number}")}\" " \
1367
+ "class=\"fn-link\" id=\"#{escape_html("fnref-#{number}")}\">" \
1368
+ "#{escape_html(label.to_s)}</a></sup>"
1369
+ end
1370
+ end
1371
+ end
1372
+
1373
+ def render_concept(concept)
1374
+ render_mixed_inline(concept)
1375
+ end
1376
+
1377
+ # --- Stem/math rendering ---
1378
+
1379
+ def render_fmt_stem(fmt_stem)
1380
+ semx_items = Array(fmt_stem.semx)
1381
+ return if semx_items.empty?
1382
+
1383
+ semx = semx_items.first
1384
+ math_items = Array(semx.math)
1385
+ ascii_items = Array(semx.asciimath)
1386
+
1387
+ # Collect source formats for interactive copy dropdown
1388
+ source_formats = {}
1389
+ if ascii_items.any?
1390
+ ascii_text = ascii_items.map { |a| a.text.to_s.strip }.join
1391
+ source_formats["asciimath"] = ascii_text unless ascii_text.empty?
1392
+ end
1393
+
1394
+ data_attrs = ""
1395
+ unless source_formats.empty?
1396
+ data_attrs = " data-stem-formats='#{escape_html(source_formats.to_json)}'"
1397
+ end
1398
+
1399
+ if math_items.any?
1400
+ content = math_items.map { |m| m.content.to_s }.join
1401
+ unless content.empty?
1402
+ @output << "<span class=\"math-container\"#{data_attrs}>"
1403
+ @output << "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">#{content}</math>"
1404
+ @output << "</span>"
1405
+ end
1406
+ elsif ascii_items.any?
1407
+ # No MathML — render asciimath as fallback
1408
+ text = ascii_items.map { |a| a.text.to_s.strip }.join
1409
+ @output << "<span class=\"stem\"#{data_attrs}>#{escape_html(text)}</span>"
1410
+ end
1411
+ end
1412
+
1413
+ def render_stem_content(stem)
1414
+ return "" if stem.nil?
1415
+
1416
+ # StemInlineElement — source element, skip (rendered via FmtStemElement)
1417
+ if stem.is_a?(Metanorma::Document::Components::Inline::StemInlineElement)
1418
+ return ""
1419
+ end
1420
+
1421
+ # FmtStemElement — already handled by render_fmt_stem
1422
+ if stem.is_a?(Metanorma::Document::Components::Inline::FmtStemElement)
1423
+ return ""
1424
+ end
1425
+
1426
+ # TextElements::StemElement — block math (no fmt- counterpart for display formulas)
1427
+ if stem.is_a?(Metanorma::Document::Components::TextElements::StemElement)
1428
+ if stem.math
1429
+ begin
1430
+ return stem.math.to_xml
1431
+ rescue StandardError
1432
+ math_items = Array(stem.math)
1433
+ return math_items.map { |m| m.respond_to?(:content) ? m.content.to_s : m.to_s }.join
1434
+ end
1435
+ end
1436
+ if stem.asciimath
1437
+ text = extract_text_value(stem.asciimath)
1438
+ return %(<span class="stem">#{escape_html(text)}</span>)
1439
+ end
1440
+ if stem.latexmath
1441
+ text = extract_text_value(stem.latexmath)
1442
+ return %(<span class="stem">#{escape_html(text)}</span>)
1443
+ end
1444
+ end
1445
+
1446
+ text = extract_text_value(stem)
1447
+ text.empty? ? "" : %(<span class="stem">#{escape_html(text)}</span>)
1448
+ end
1449
+
1450
+ # --- Helper methods ---
1451
+
1452
+ def tag(name, attrs_str)
1453
+ @output << "<#{name}#{attrs_str}>"
1454
+ yield
1455
+ @output << "</#{name}>"
1456
+ end
1457
+
1458
+ def element_attrs(**attrs)
1459
+ parts = []
1460
+ attrs.each do |k, v|
1461
+ next if v.nil? || v == false || (v.is_a?(String) && v.empty?)
1462
+
1463
+ val = k == :class ? translate_class(v.to_s) : v.to_s
1464
+ parts << %( #{k}="#{escape_html(val)}")
1465
+ end
1466
+ parts.join
1467
+ end
1468
+
1469
+ def translate_class(class_str)
1470
+ class_str.split(/\s+/).map { |c| CLASS_MAP[c] || c }.join(" ")
1471
+ end
1472
+
1473
+ def block_element?(obj)
1474
+ obj.is_a?(Metanorma::Document::Components::Paragraphs::ParagraphBlock) ||
1475
+ obj.is_a?(Metanorma::Document::Components::Tables::TableBlock) ||
1476
+ obj.is_a?(Metanorma::Document::Components::Lists::UnorderedList) ||
1477
+ obj.is_a?(Metanorma::Document::Components::Lists::OrderedList) ||
1478
+ obj.is_a?(Metanorma::Document::Components::Lists::DefinitionList) ||
1479
+ obj.is_a?(Metanorma::Document::Components::AncillaryBlocks::FigureBlock) ||
1480
+ obj.is_a?(Metanorma::Document::Components::Blocks::NoteBlock) ||
1481
+ obj.is_a?(Metanorma::Document::Components::AncillaryBlocks::ExampleBlock) ||
1482
+ obj.is_a?(Metanorma::Document::Components::AncillaryBlocks::SourcecodeBlock) ||
1483
+ obj.is_a?(Metanorma::Document::Components::AncillaryBlocks::FormulaBlock) ||
1484
+ obj.is_a?(Metanorma::Document::Components::MultiParagraph::QuoteBlock) ||
1485
+ obj.is_a?(Metanorma::Document::Components::MultiParagraph::AdmonitionBlock) ||
1486
+ obj.is_a?(Metanorma::Document::Components::Sections::HierarchicalSection) ||
1487
+ obj.is_a?(Metanorma::Document::Components::Sections::BasicSection) ||
1488
+ obj.is_a?(Metanorma::Document::Components::Sections::ContentSection)
1489
+ end
1490
+
1491
+ def safe_attr(obj, method_name)
1492
+ obj.public_send(method_name)
1493
+ rescue NoMethodError
1494
+ nil
1495
+ end
1496
+
1497
+ def collect_index_term(element)
1498
+ primary = safe_attr(element, :primary)
1499
+ return unless primary && !primary.to_s.strip.empty?
1500
+
1501
+ @index_term_collector.add(
1502
+ primary: primary.to_s.strip,
1503
+ secondary: safe_attr(element, :secondary)&.to_s&.strip,
1504
+ tertiary: safe_attr(element, :tertiary)&.to_s&.strip,
1505
+ target_id: @current_section_id,
1506
+ target_text: @current_section_number
1507
+ )
1508
+ rescue StandardError
1509
+ nil
1510
+ end
1511
+
1512
+ def extract_block_label(block, default)
1513
+ # Presentation XML puts label in <name> child element
1514
+ names = safe_attr(block, :name)
1515
+ if names && !names.empty?
1516
+ name = names.is_a?(Array) ? names.first : names
1517
+ text = extract_text_value(name)
1518
+ return text unless text.to_s.strip.empty?
1519
+ end
1520
+
1521
+ # Fallback: autonum XML attribute
1522
+ autonum = safe_attr(block, :autonum)
1523
+ if autonum && !autonum.to_s.empty?
1524
+ number = autonum.to_s
1525
+ return "#{default} #{number}"
1526
+ end
1527
+
1528
+ default
1529
+ end
1530
+
1531
+ def alignment_style(alignment)
1532
+ return nil if alignment.nil? || alignment.to_s.empty?
1533
+
1534
+ "text-align: #{alignment}"
1535
+ end
1536
+
1537
+ def extract_title_text(titles)
1538
+ return "" if titles.nil? || titles.empty?
1539
+
1540
+ titles = Array(titles)
1541
+ title = titles.first
1542
+ extract_text_value(title).to_s
1543
+ end
1544
+
1545
+ def escape_html(text)
1546
+ return "" if text.nil?
1547
+
1548
+ text
1549
+ .to_s
1550
+ .gsub("&", "&amp;")
1551
+ .gsub("<", "&lt;")
1552
+ .gsub(">", "&gt;")
1553
+ .gsub('"', "&quot;")
1554
+ end
1555
+
1556
+ def extract_text_value(val)
1557
+ return nil if val.nil?
1558
+ return val if val.is_a?(String)
1559
+
1560
+ if val.is_a?(Array)
1561
+ val.map { |v| extract_text_value(v) }.join
1562
+ elsif val.is_a?(Lutaml::Model::Serializable)
1563
+ c = safe_attr(val, :content)
1564
+ if c && !c.equal?(val)
1565
+ extract_text_value(c)
1566
+ else
1567
+ t = safe_attr(val, :text)
1568
+ if t
1569
+ extract_text_value(t)
1570
+ else
1571
+ v = safe_attr(val, :value)
1572
+ if v
1573
+ extract_text_value(v)
1574
+ else
1575
+ val.to_s
1576
+ end
1577
+ end
1578
+ end
1579
+ else
1580
+ val.to_s
1581
+ end
1582
+ end
1583
+
1584
+ def render_footnotes_section
1585
+ return if @footnote_collector.empty?
1586
+
1587
+ drops = @footnote_collector.to_a.map do |entry|
1588
+ content_html = ""
1589
+ if entry.content && !entry.content.empty?
1590
+ content_html = capture_output do
1591
+ Array(entry.content).each { |p| render_paragraph(p) }
1592
+ end
1593
+ end
1594
+ Drops::FootnoteDrop.new(entry, content_html)
1595
+ end
1596
+
1597
+ @output << render_liquid("_footnotes.html.liquid", { "footnotes" => drops })
1598
+ end
1599
+
1600
+ def capture_output
1601
+ old_output = @output
1602
+ @output = +""
1603
+ yield
1604
+ result = @output
1605
+ @output = old_output
1606
+ result
1607
+ end
1608
+ end
1609
+ end
1610
+ end