metanorma-document 0.2.9 → 0.2.12

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 (390) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +164 -66
  3. data/CLAUDE.md +26 -0
  4. data/README.adoc +144 -0
  5. data/Rakefile +14 -2
  6. data/TODO.finalize/01-split-multi-class-files.md +8 -0
  7. data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +6 -0
  8. data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +11 -0
  9. data/TODO.finalize/04-theme-per-flavor-config-driven.md +9 -0
  10. data/TODO.finalize/05-migrate-all-html-to-liquid.md +16 -0
  11. data/TODO.finalize/06-integrate-pubid-library.md +16 -0
  12. data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +39 -0
  13. data/TODO.finalize/08-refactor-renderer-architecture.md +25 -0
  14. data/TODO.finalize/09-eliminate-code-smells.md +14 -0
  15. data/TODO.finalize/10-comprehensive-spec-coverage.md +24 -0
  16. data/TODO.finalize/11-config-driven-flavor-metadata.md +20 -0
  17. data/TODO.finalize/12-add-frozen-string-literal.md +6 -0
  18. data/TODO.finalize/13-extract-composition-from-god-class.md +96 -0
  19. data/TODO.finalize/14-config-driven-section-ordering.md +31 -0
  20. data/TODO.finalize/15-theme-directories-per-flavor.md +46 -0
  21. data/TODO.finalize/16-theme-lutaml-model-schema.md +44 -0
  22. data/data/javascripts/components/mn-footnotes.js +68 -0
  23. data/data/javascripts/components/mn-lightbox.js +18 -2
  24. data/data/javascripts/components/mn-shortcuts.js +2 -0
  25. data/data/javascripts/core/mn-navigation.js +8 -0
  26. data/data/javascripts/core/mn-theme.js +9 -9
  27. data/data/logos/oiml-logo-full-dark.svg +1 -0
  28. data/data/logos/oiml-logo-full-light.svg +1 -0
  29. data/data/logos/oiml-logo-icon-dark.svg +1 -0
  30. data/data/logos/oiml-logo-icon-light.svg +1 -0
  31. data/data/logos/oiml-logo.svg +1 -0
  32. data/data/logos/pdfa-logo.svg +31 -94
  33. data/data/stylesheets/base/_layout.css +3 -0
  34. data/data/stylesheets/base/_reset.css +3 -3
  35. data/data/stylesheets/base/_typography.css +0 -5
  36. data/data/stylesheets/components/bibliography.css +3 -3
  37. data/data/stylesheets/components/cover.css +3 -1
  38. data/data/stylesheets/components/figure.css +13 -1
  39. data/data/stylesheets/components/footnote.css +65 -5
  40. data/data/stylesheets/components/formula.css +44 -0
  41. data/data/stylesheets/components/header.css +17 -2
  42. data/data/stylesheets/components/inline.css +1 -1
  43. data/data/stylesheets/components/table.css +15 -2
  44. data/data/stylesheets/components/term.css +0 -1
  45. data/data/themes/bipm.yaml +60 -0
  46. data/data/themes/cc.yaml +43 -0
  47. data/data/themes/icc.yaml +39 -0
  48. data/data/themes/iec.yaml +44 -0
  49. data/data/themes/ieee.yaml +43 -0
  50. data/data/themes/ietf.yaml +45 -0
  51. data/data/themes/iho.yaml +46 -0
  52. data/data/themes/iso.yaml +40 -0
  53. data/data/themes/itu.yaml +45 -0
  54. data/data/themes/ogc.yaml +51 -0
  55. data/data/themes/oiml.yaml +56 -0
  56. data/data/themes/pdfa.yaml +44 -0
  57. data/data/themes/ribose.yaml +43 -0
  58. data/exe/metanorma-document +11 -0
  59. data/frontend/e2e/sample-html.test.ts +256 -0
  60. data/frontend/index.html +12 -0
  61. data/frontend/package-lock.json +4725 -0
  62. data/frontend/package.json +32 -0
  63. data/frontend/postcss.config.js +6 -0
  64. data/frontend/src/App.vue +79 -0
  65. data/frontend/src/app.ts +21 -0
  66. data/frontend/src/components/AppSidebar.vue +77 -0
  67. data/frontend/src/components/DocumentReader.vue +180 -0
  68. data/frontend/src/components/MirrorRenderer.vue +23 -0
  69. data/frontend/src/components/ReadingProgressBar.vue +29 -0
  70. data/frontend/src/components/TextRenderer.vue +131 -0
  71. data/frontend/src/components/TocItem.vue +57 -0
  72. data/frontend/src/components/TopBar.vue +49 -0
  73. data/frontend/src/components/__tests__/MirrorRenderer.test.ts +178 -0
  74. data/frontend/src/components/blocks/AdmonitionBlock.vue +74 -0
  75. data/frontend/src/components/blocks/ClauseBlock.vue +53 -0
  76. data/frontend/src/components/blocks/ExampleBlock.vue +21 -0
  77. data/frontend/src/components/blocks/FallbackBlock.vue +18 -0
  78. data/frontend/src/components/blocks/FigureBlock.vue +26 -0
  79. data/frontend/src/components/blocks/FootnotesBlock.vue +25 -0
  80. data/frontend/src/components/blocks/ListBlocks.vue +62 -0
  81. data/frontend/src/components/blocks/NoteBlock.vue +21 -0
  82. data/frontend/src/components/blocks/ParagraphBlock.vue +25 -0
  83. data/frontend/src/components/blocks/QuoteBlock.vue +25 -0
  84. data/frontend/src/components/blocks/ReviewBlock.vue +39 -0
  85. data/frontend/src/components/blocks/SourcecodeBlock.vue +52 -0
  86. data/frontend/src/components/blocks/TableBlock.vue +48 -0
  87. data/frontend/src/components/blocks/index.ts +36 -0
  88. data/frontend/src/stores/__tests__/documentStore.fixture.test.ts +272 -0
  89. data/frontend/src/stores/__tests__/documentStore.test.ts +143 -0
  90. data/frontend/src/stores/__tests__/ebookStore.test.ts +145 -0
  91. data/frontend/src/stores/__tests__/uiStore.test.ts +42 -0
  92. data/frontend/src/stores/documentStore.ts +153 -0
  93. data/frontend/src/stores/ebookStore.ts +179 -0
  94. data/frontend/src/stores/uiStore.ts +32 -0
  95. data/frontend/src/styles.css +156 -0
  96. data/frontend/src/types/__tests__/rendering-contract.test.ts +172 -0
  97. data/frontend/src/types/rendering-contract.ts +209 -0
  98. data/frontend/src/utils/__tests__/typeMetadata.test.ts +102 -0
  99. data/frontend/src/utils/typeMetadata.ts +121 -0
  100. data/frontend/tailwind.config.js +18 -0
  101. data/frontend/test/fixtures/iso-document.json +271 -0
  102. data/frontend/tmp/debug_e2e.cjs +46 -0
  103. data/frontend/tsconfig.json +25 -0
  104. data/frontend/tsconfig.node.json +11 -0
  105. data/frontend/vite.config.ts +37 -0
  106. data/frontend/vitest.e2e.config.ts +9 -0
  107. data/lib/metanorma/bipm_document/metadata/bipm_bibliographic_item.rb +0 -14
  108. data/lib/metanorma/bipm_document/metadata/depiction_element.rb +18 -0
  109. data/lib/metanorma/bipm_document/metadata.rb +1 -1
  110. data/lib/metanorma/document/cli.rb +116 -0
  111. data/lib/metanorma/document/components/ancillary_blocks/callout_annotation.rb +27 -0
  112. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_block.rb +0 -65
  113. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +18 -0
  114. data/lib/metanorma/document/components/ancillary_blocks.rb +4 -0
  115. data/lib/metanorma/document/components/bib_data/bib_data.rb +7 -0
  116. data/lib/metanorma/document/components/blocks/classification_value.rb +21 -0
  117. data/lib/metanorma/document/components/blocks/permission_model.rb +28 -0
  118. data/lib/metanorma/document/components/blocks/recommendation_model.rb +28 -0
  119. data/lib/metanorma/document/components/blocks/requirement_base.rb +30 -0
  120. data/lib/metanorma/document/components/blocks/requirement_classification.rb +20 -0
  121. data/lib/metanorma/document/components/blocks/requirement_description.rb +43 -0
  122. data/lib/metanorma/document/components/blocks/requirement_inherit.rb +24 -0
  123. data/lib/metanorma/document/components/blocks/requirement_model.rb +0 -131
  124. data/lib/metanorma/document/components/blocks.rb +8 -7
  125. data/lib/metanorma/document/components/id_elements/image.rb +2 -0
  126. data/lib/metanorma/document/components/inline/asciimath_element.rb +2 -0
  127. data/lib/metanorma/document/components/inline/attribution_element.rb +2 -0
  128. data/lib/metanorma/document/components/inline/bcp14_element.rb +2 -0
  129. data/lib/metanorma/document/components/inline/biblio_tag_element.rb +2 -0
  130. data/lib/metanorma/document/components/inline/br_element.rb +2 -0
  131. data/lib/metanorma/document/components/inline/comma_element.rb +2 -0
  132. data/lib/metanorma/document/components/inline/concept_element.rb +2 -0
  133. data/lib/metanorma/document/components/inline/display_text_element.rb +2 -0
  134. data/lib/metanorma/document/components/inline/em_raw_element.rb +2 -0
  135. data/lib/metanorma/document/components/inline/enum_comma_element.rb +2 -0
  136. data/lib/metanorma/document/components/inline/eref_element.rb +2 -0
  137. data/lib/metanorma/document/components/inline/fmt_admitted_element.rb +4 -0
  138. data/lib/metanorma/document/components/inline/fmt_annotation_body_element.rb +4 -0
  139. data/lib/metanorma/document/components/inline/fmt_annotation_end_element.rb +4 -0
  140. data/lib/metanorma/document/components/inline/fmt_annotation_start_element.rb +4 -0
  141. data/lib/metanorma/document/components/inline/fmt_concept_element.rb +4 -0
  142. data/lib/metanorma/document/components/inline/fmt_definition_element.rb +4 -0
  143. data/lib/metanorma/document/components/inline/fmt_fn_body_element.rb +4 -0
  144. data/lib/metanorma/document/components/inline/fmt_fn_label_element.rb +4 -0
  145. data/lib/metanorma/document/components/inline/fmt_footnote_container_element.rb +4 -0
  146. data/lib/metanorma/document/components/inline/fmt_identifier_element.rb +4 -0
  147. data/lib/metanorma/document/components/inline/fmt_name_element.rb +4 -0
  148. data/lib/metanorma/document/components/inline/fmt_preferred_element.rb +4 -0
  149. data/lib/metanorma/document/components/inline/fmt_sourcecode_element.rb +4 -0
  150. data/lib/metanorma/document/components/inline/fmt_stem_element.rb +10 -0
  151. data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +4 -0
  152. data/lib/metanorma/document/components/inline/fmt_title_element.rb +4 -0
  153. data/lib/metanorma/document/components/inline/fmt_xref_element.rb +4 -0
  154. data/lib/metanorma/document/components/inline/fmt_xref_label_element.rb +4 -0
  155. data/lib/metanorma/document/components/inline/fn_element.rb +2 -0
  156. data/lib/metanorma/document/components/inline/link_element.rb +2 -0
  157. data/lib/metanorma/document/components/inline/localized_string_element.rb +2 -0
  158. data/lib/metanorma/document/components/inline/localized_strings_element.rb +2 -0
  159. data/lib/metanorma/document/components/inline/location_element.rb +24 -0
  160. data/lib/metanorma/document/components/inline/math_element.rb +2 -0
  161. data/lib/metanorma/document/components/inline/name_with_id_element.rb +4 -0
  162. data/lib/metanorma/document/components/inline/rendered_display.rb +23 -0
  163. data/lib/metanorma/document/components/inline/rendered_math_element.rb +21 -0
  164. data/lib/metanorma/document/components/inline/semantic_content.rb +51 -0
  165. data/lib/metanorma/document/components/inline/semantic_math_element.rb +20 -0
  166. data/lib/metanorma/document/components/inline/semx_child_element.rb +2 -0
  167. data/lib/metanorma/document/components/inline/semx_element.rb +13 -1
  168. data/lib/metanorma/document/components/inline/small_cap_element.rb +2 -0
  169. data/lib/metanorma/document/components/inline/span_element.rb +10 -15
  170. data/lib/metanorma/document/components/inline/stem_inline_element.rb +7 -1
  171. data/lib/metanorma/document/components/inline/strong_raw_element.rb +2 -0
  172. data/lib/metanorma/document/components/inline/sub_element.rb +2 -0
  173. data/lib/metanorma/document/components/inline/sup_element.rb +2 -0
  174. data/lib/metanorma/document/components/inline/tab_element.rb +2 -0
  175. data/lib/metanorma/document/components/inline/title_with_annotation_element.rb +4 -0
  176. data/lib/metanorma/document/components/inline/tt_element.rb +2 -0
  177. data/lib/metanorma/document/components/inline/variant_title_element.rb +2 -0
  178. data/lib/metanorma/document/components/inline/vocabulary.rb +184 -0
  179. data/lib/metanorma/document/components/inline/xref_element.rb +10 -0
  180. data/lib/metanorma/document/components/inline.rb +12 -0
  181. data/lib/metanorma/document/components/multi_paragraph/quote_author_element.rb +20 -0
  182. data/lib/metanorma/document/components/multi_paragraph/quote_block.rb +0 -29
  183. data/lib/metanorma/document/components/multi_paragraph.rb +2 -0
  184. data/lib/metanorma/document/components/reference_elements/source_element.rb +0 -31
  185. data/lib/metanorma/document/components/reference_elements/source_modification.rb +21 -0
  186. data/lib/metanorma/document/components/reference_elements/source_origin.rb +28 -0
  187. data/lib/metanorma/document/components/reference_elements.rb +2 -2
  188. data/lib/metanorma/document/components/tables/col_element.rb +18 -0
  189. data/lib/metanorma/document/components/tables/col_group_element.rb +18 -0
  190. data/lib/metanorma/document/components/tables/table_block.rb +0 -22
  191. data/lib/metanorma/document/components/tables/table_body_section.rb +16 -0
  192. data/lib/metanorma/document/components/tables/table_foot_section.rb +16 -0
  193. data/lib/metanorma/document/components/tables/table_head_section.rb +16 -0
  194. data/lib/metanorma/document/components/tables/table_section.rb +0 -25
  195. data/lib/metanorma/document/components/tables.rb +5 -3
  196. data/lib/metanorma/document/relaton/address.rb +0 -27
  197. data/lib/metanorma/document/relaton/bib_item_locality.rb +3 -12
  198. data/lib/metanorma/document/relaton/copyright_association.rb +0 -10
  199. data/lib/metanorma/document/relaton/copyright_owner.rb +16 -0
  200. data/lib/metanorma/document/relaton/edition.rb +4 -7
  201. data/lib/metanorma/document/relaton/formatted_address.rb +29 -0
  202. data/lib/metanorma/document/relaton/logo_element.rb +18 -0
  203. data/lib/metanorma/document/relaton/organization.rb +0 -14
  204. data/lib/metanorma/document/relaton/phone.rb +3 -9
  205. data/lib/metanorma/document/relaton/place_type.rb +0 -13
  206. data/lib/metanorma/document/relaton/price_type.rb +3 -9
  207. data/lib/metanorma/document/relaton/region_element.rb +18 -0
  208. data/lib/metanorma/document/relaton.rb +6 -1
  209. data/lib/metanorma/document/version.rb +1 -1
  210. data/lib/metanorma/document.rb +2 -0
  211. data/lib/metanorma/html/asset_pipeline.rb +1 -0
  212. data/lib/metanorma/html/base_renderer.rb +261 -1008
  213. data/lib/metanorma/html/bipm_renderer.rb +15 -144
  214. data/lib/metanorma/html/cc_renderer.rb +1 -45
  215. data/lib/metanorma/html/component/base.rb +2 -7
  216. data/lib/metanorma/html/component/index_section.rb +41 -43
  217. data/lib/metanorma/html/drops/admonition_drop.rb +1 -4
  218. data/lib/metanorma/html/drops/biblio_entry_drop.rb +50 -0
  219. data/lib/metanorma/html/drops/block_element_drop.rb +7 -2
  220. data/lib/metanorma/html/drops/example_drop.rb +1 -4
  221. data/lib/metanorma/html/drops/figure_drop.rb +31 -22
  222. data/lib/metanorma/html/drops/footnote_drop.rb +1 -1
  223. data/lib/metanorma/html/drops/formula_drop.rb +45 -16
  224. data/lib/metanorma/html/drops/note_drop.rb +9 -7
  225. data/lib/metanorma/html/drops/sourcecode_drop.rb +12 -5
  226. data/lib/metanorma/html/drops.rb +2 -0
  227. data/lib/metanorma/html/generator.rb +2 -2
  228. data/lib/metanorma/html/icc_renderer.rb +0 -39
  229. data/lib/metanorma/html/iec_renderer.rb +1 -44
  230. data/lib/metanorma/html/ieee_renderer.rb +1 -45
  231. data/lib/metanorma/html/ietf_renderer.rb +1 -48
  232. data/lib/metanorma/html/iho_renderer.rb +1 -46
  233. data/lib/metanorma/html/iso_renderer.rb +290 -343
  234. data/lib/metanorma/html/itu_renderer.rb +1 -45
  235. data/lib/metanorma/html/ogc_renderer.rb +15 -76
  236. data/lib/metanorma/html/oiml_renderer.rb +18 -38
  237. data/lib/metanorma/html/pdfa_renderer.rb +0 -39
  238. data/lib/metanorma/html/renderers/block_renderer.rb +372 -0
  239. data/lib/metanorma/html/renderers/inline_renderer.rb +655 -0
  240. data/lib/metanorma/html/renderers/pubid_renderer.rb +69 -0
  241. data/lib/metanorma/html/renderers/section_renderer.rb +193 -0
  242. data/lib/metanorma/html/renderers.rb +8 -0
  243. data/lib/metanorma/html/ribose_renderer.rb +1 -45
  244. data/lib/metanorma/html/standard_renderer.rb +341 -229
  245. data/lib/metanorma/html/templates/_audio.html.liquid +1 -0
  246. data/lib/metanorma/html/templates/_biblio_entry.html.liquid +1 -0
  247. data/lib/metanorma/html/templates/_bipm_cover.html.liquid +27 -0
  248. data/lib/metanorma/html/templates/_boilerplate.html.liquid +1 -0
  249. data/lib/metanorma/html/templates/_bookmark.html.liquid +1 -0
  250. data/lib/metanorma/html/templates/_br.html.liquid +1 -0
  251. data/lib/metanorma/html/templates/_definition_list.html.liquid +6 -0
  252. data/lib/metanorma/html/templates/_element.html.liquid +1 -0
  253. data/lib/metanorma/html/templates/_fn_marker.html.liquid +1 -0
  254. data/lib/metanorma/html/templates/_footnotes.html.liquid +6 -6
  255. data/lib/metanorma/html/templates/_formula.html.liquid +1 -1
  256. data/lib/metanorma/html/templates/_header.html.liquid +1 -1
  257. data/lib/metanorma/html/templates/_heading.html.liquid +1 -0
  258. data/lib/metanorma/html/templates/_image.html.liquid +1 -0
  259. data/lib/metanorma/html/templates/_inline_span.html.liquid +1 -0
  260. data/lib/metanorma/html/templates/_iso_cover.html.liquid +1 -1
  261. data/lib/metanorma/html/templates/_link.html.liquid +1 -0
  262. data/lib/metanorma/html/templates/_list.html.liquid +3 -0
  263. data/lib/metanorma/html/templates/_list_item.html.liquid +1 -0
  264. data/lib/metanorma/html/templates/_main_content.html.liquid +1 -0
  265. data/lib/metanorma/html/templates/_math_container.html.liquid +1 -0
  266. data/lib/metanorma/html/templates/_paragraph.html.liquid +1 -0
  267. data/lib/metanorma/html/templates/_prefatory_section.html.liquid +1 -0
  268. data/lib/metanorma/html/templates/_pubid_identifier.html.liquid +1 -0
  269. data/lib/metanorma/html/templates/_quote.html.liquid +4 -0
  270. data/lib/metanorma/html/templates/_ref_date.html.liquid +1 -0
  271. data/lib/metanorma/html/templates/_ref_title.html.liquid +1 -0
  272. data/lib/metanorma/html/templates/_section.html.liquid +5 -0
  273. data/lib/metanorma/html/templates/_standard_cover.html.liquid +31 -0
  274. data/lib/metanorma/html/templates/_standard_doc_title.html.liquid +3 -0
  275. data/lib/metanorma/html/templates/_stem_span.html.liquid +1 -0
  276. data/lib/metanorma/html/templates/_table.html.liquid +9 -0
  277. data/lib/metanorma/html/templates/_term_domain.html.liquid +1 -0
  278. data/lib/metanorma/html/templates/_term_example.html.liquid +1 -0
  279. data/lib/metanorma/html/templates/_term_note.html.liquid +1 -0
  280. data/lib/metanorma/html/templates/_term_number.html.liquid +1 -0
  281. data/lib/metanorma/html/templates/_term_source.html.liquid +1 -0
  282. data/lib/metanorma/html/templates/_term_text_note.html.liquid +1 -0
  283. data/lib/metanorma/html/templates/_video.html.liquid +1 -0
  284. data/lib/metanorma/html/templates/_wrapped_preface.html.liquid +1 -0
  285. data/lib/metanorma/html/templates/document.html.liquid +4 -0
  286. data/lib/metanorma/html/theme.rb +180 -107
  287. data/lib/metanorma/html.rb +13 -0
  288. data/lib/metanorma/ietf_document/metadata/ietf_bib_data_extension_type.rb +0 -25
  289. data/lib/metanorma/ietf_document/metadata/pi_settings.rb +32 -0
  290. data/lib/metanorma/ietf_document/metadata.rb +2 -0
  291. data/lib/metanorma/iso_document/metadata/doctype_element.rb +20 -0
  292. data/lib/metanorma/iso_document/metadata/iso_bib_data_extension_type.rb +0 -48
  293. data/lib/metanorma/iso_document/metadata/iso_bibliographic_item.rb +0 -72
  294. data/lib/metanorma/iso_document/metadata/iso_document_status.rb +0 -30
  295. data/lib/metanorma/iso_document/metadata/iso_localized_title.rb +4 -1
  296. data/lib/metanorma/iso_document/metadata/language_element.rb +18 -0
  297. data/lib/metanorma/iso_document/metadata/project_number.rb +22 -0
  298. data/lib/metanorma/iso_document/metadata/script_element.rb +18 -0
  299. data/lib/metanorma/iso_document/metadata/stage_element.rb +26 -0
  300. data/lib/metanorma/iso_document/metadata/stagename_element.rb +18 -0
  301. data/lib/metanorma/iso_document/metadata/structured_identifier.rb +0 -17
  302. data/lib/metanorma/iso_document/metadata/title_collection.rb +1 -0
  303. data/lib/metanorma/iso_document/metadata.rb +9 -4
  304. data/lib/metanorma/iso_document/terms/iso_term.rb +0 -13
  305. data/lib/metanorma/iso_document/terms/term_domain_element.rb +18 -0
  306. data/lib/metanorma/iso_document/terms.rb +1 -0
  307. data/lib/metanorma/itu_document/metadata/itu_bib_data_extension_type.rb +0 -37
  308. data/lib/metanorma/itu_document/metadata/meeting_date.rb +18 -0
  309. data/lib/metanorma/itu_document/metadata/meeting_element.rb +18 -0
  310. data/lib/metanorma/itu_document/metadata/study_period.rb +18 -0
  311. data/lib/metanorma/itu_document/metadata.rb +3 -4
  312. data/lib/metanorma/mirror/default_registry.rb +170 -0
  313. data/lib/metanorma/mirror/handler_registry.rb +60 -0
  314. data/lib/metanorma/mirror/handler_result.rb +37 -0
  315. data/lib/metanorma/mirror/handlers/admonition.rb +18 -0
  316. data/lib/metanorma/mirror/handlers/example.rb +21 -0
  317. data/lib/metanorma/mirror/handlers/figure.rb +53 -0
  318. data/lib/metanorma/mirror/handlers/formula.rb +35 -0
  319. data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +159 -0
  320. data/lib/metanorma/mirror/handlers/inline/text_extractor.rb +116 -0
  321. data/lib/metanorma/mirror/handlers/inline.rb +225 -0
  322. data/lib/metanorma/mirror/handlers/list.rb +110 -0
  323. data/lib/metanorma/mirror/handlers/note.rb +26 -0
  324. data/lib/metanorma/mirror/handlers/paragraph.rb +19 -0
  325. data/lib/metanorma/mirror/handlers/quote.rb +16 -0
  326. data/lib/metanorma/mirror/handlers/review.rb +19 -0
  327. data/lib/metanorma/mirror/handlers/section.rb +149 -0
  328. data/lib/metanorma/mirror/handlers/sourcecode.rb +27 -0
  329. data/lib/metanorma/mirror/handlers/structural.rb +68 -0
  330. data/lib/metanorma/mirror/handlers/table.rb +81 -0
  331. data/lib/metanorma/mirror/handlers/term.rb +100 -0
  332. data/lib/metanorma/mirror/handlers.rb +60 -0
  333. data/lib/metanorma/mirror/id_strategy/positional.rb +166 -0
  334. data/lib/metanorma/mirror/id_strategy/preserve.rb +12 -0
  335. data/lib/metanorma/mirror/id_strategy.rb +34 -0
  336. data/lib/metanorma/mirror/math_util.rb +53 -0
  337. data/lib/metanorma/mirror/metadata.rb +37 -0
  338. data/lib/metanorma/mirror/metanorma_to_mirror.rb +101 -0
  339. data/lib/metanorma/mirror/model/container.rb +47 -0
  340. data/lib/metanorma/mirror/model/factory.rb +52 -0
  341. data/lib/metanorma/mirror/model/guide.rb +25 -0
  342. data/lib/metanorma/mirror/model/leaf.rb +17 -0
  343. data/lib/metanorma/mirror/model/mark.rb +53 -0
  344. data/lib/metanorma/mirror/model/node.rb +47 -0
  345. data/lib/metanorma/mirror/model/soft_break.rb +33 -0
  346. data/lib/metanorma/mirror/model/text.rb +34 -0
  347. data/lib/metanorma/mirror/model.rb +16 -0
  348. data/lib/metanorma/mirror/output/builder.rb +39 -0
  349. data/lib/metanorma/mirror/output/formats/base_format.rb +90 -0
  350. data/lib/metanorma/mirror/output/formats/inline_format.rb +81 -0
  351. data/lib/metanorma/mirror/output/formats.rb +53 -0
  352. data/lib/metanorma/mirror/output/html_renderer.rb +95 -0
  353. data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +178 -0
  354. data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +61 -0
  355. data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +96 -0
  356. data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +64 -0
  357. data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +129 -0
  358. data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +80 -0
  359. data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +87 -0
  360. data/lib/metanorma/mirror/output/html_renderers.rb +76 -0
  361. data/lib/metanorma/mirror/output/pipeline.rb +92 -0
  362. data/lib/metanorma/mirror/output/pipeline_context.rb +22 -0
  363. data/lib/metanorma/mirror/output.rb +14 -0
  364. data/lib/metanorma/mirror/rewriter.rb +121 -0
  365. data/lib/metanorma/mirror/safe_attr.rb +16 -0
  366. data/lib/metanorma/mirror/serialization/json_serializer.rb +25 -0
  367. data/lib/metanorma/mirror/serialization/yaml_serializer.rb +20 -0
  368. data/lib/metanorma/mirror/serialization.rb +10 -0
  369. data/lib/metanorma/mirror/transformer.rb +22 -0
  370. data/lib/metanorma/mirror.rb +32 -0
  371. data/lib/metanorma/standard_document/annotation.rb +37 -0
  372. data/lib/metanorma/standard_document/block_attributes.rb +15 -2
  373. data/lib/metanorma/standard_document/blocks/block_source.rb +0 -23
  374. data/lib/metanorma/standard_document/blocks/source_origin_element.rb +23 -0
  375. data/lib/metanorma/standard_document/blocks.rb +1 -1
  376. data/lib/metanorma/standard_document/terms/term_expression.rb +0 -31
  377. data/lib/metanorma/standard_document/terms/term_name_element.rb +29 -0
  378. data/lib/metanorma/standard_document/terms.rb +1 -0
  379. data/script/render_both.rb +18 -0
  380. metadata +262 -13
  381. data/.github/workflows/rake.yml +0 -21
  382. data/.github/workflows/release.yml +0 -25
  383. data/.github/workflows/roundtrip-samples.yml +0 -132
  384. data/.gitignore +0 -19
  385. data/.rspec +0 -3
  386. data/.rubocop.yml +0 -22
  387. data/Gemfile +0 -28
  388. data/bin/console +0 -15
  389. data/bin/setup +0 -8
  390. data/metanorma-document.gemspec +0 -35
@@ -2,52 +2,8 @@
2
2
 
3
3
  module Metanorma
4
4
  module Html
5
- # ITU brand: #0e99d5 blue from logo
6
5
  class ItuRenderer < IsoRenderer
7
- def flavor_publishers(_doc_id)
8
- ["ITU"]
9
- end
10
-
11
- def flavor_publisher_name
12
- "ITU"
13
- end
14
-
15
- def publisher_logo_map
16
- { "ITU" => "itu-logo.svg" }
17
- end
18
-
19
- def theme
20
- @theme ||= Theme.new.tap do |t|
21
- t.primary = "#0a7bac"
22
- t.accent = "#0e99d5"
23
- t.accent_deep = "#0a7bac"
24
- t.gradient = "linear-gradient(135deg, #065a83 0%, #0a7bac 50%, #0e99d5 100%)"
25
- t.primary_light = "#e8f4fa"
26
- t.accent_light = "#daedf8"
27
- t.warm = "#b8860b"
28
- t.warm_light = "#fdf5e6"
29
- t.sidebar_bg = "#f2f6fa"
30
- t.font_body = '"Noto Serif", "Georgia", serif'
31
- t.font_sans = '"Noto Sans", "Helvetica Neue", Arial, sans-serif'
32
- t.font_mono = '"Noto Sans Mono", "Menlo", monospace'
33
- t.font_url = "https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700&family=Noto+Serif:ital,wght@0,400;0,500;0,600;1,400&family=Noto+Sans+Mono:wght@400;500&display=swap"
34
- t.header_background = "linear-gradient(135deg, #065a83 0%, #0a7bac 40%, #0e99d5 100%)"
35
- t.cover_background = "linear-gradient(175deg, #043d5c 0%, #065a83 25%, #0a7bac 55%, #0e99d5 85%, #47b5e3 100%)"
36
- t.cover_before_bg = "background: radial-gradient(ellipse at 30% 15%, rgba(14,153,213,0.2) 0%, transparent 50%), radial-gradient(ellipse at 70% 80%, rgba(184,134,11,0.1) 0%, transparent 40%)"
37
- t.cover_after_bg = "height: 3px; background: linear-gradient(90deg, transparent, #0e99d5, #b8860b, transparent)"
38
- t.progress_bar_color = "#0e99d5"
39
- t.note_border = "#0e99d5"
40
- t.note_bg = "#daedf8"
41
- t.note_color = "#0e99d5"
42
- t.example_border = "#0a7bac"
43
- t.example_bg = "#e8f4fa"
44
- t.example_color = "#0a7bac"
45
- t.admonition_border = "#b8860b"
46
- t.admonition_color = "#b8860b"
47
- t.footer_border_color = "#0e99d5"
48
- t.cover_separator_color = "rgba(14,153,213,0.25)"
49
- end
50
- end
6
+ register_render Metanorma::ItuDocument::Root, :render_document
51
7
  end
52
8
  end
53
9
  end
@@ -2,83 +2,22 @@
2
2
 
3
3
  module Metanorma
4
4
  module Html
5
- # Renders OgcDocument components to HTML.
6
- # Extends IsoRenderer with OGC-specific branding (geospatial, OGC cyan-blue #00b1ff).
7
5
  class OgcRenderer < IsoRenderer
8
- def flavor_publishers(_doc_id)
9
- ["OGC"]
10
- end
11
-
12
- def flavor_publisher_name
13
- "OGC"
14
- end
15
-
16
- def publisher_logo_map
17
- { "OGC" => "ogc-logo.svg" }
18
- end
19
-
20
- def theme
21
- @theme ||= Theme.new.tap do |t|
22
- t.primary = "#0077b3"
23
- t.accent = "#00b1ff"
24
- t.gradient = "linear-gradient(135deg, #003d5c 0%, #0077b3 50%, #00b1ff 100%)"
25
- t.accent_deep = "#006699"
26
- t.primary_light = "#e8f4fa"
27
- t.accent_light = "#e0f4ff"
28
- t.warm = "#e8812e"
29
- t.warm_light = "#fff5eb"
30
- t.sidebar_bg = "#f2f8fc"
31
- t.font_body = '"Space Grotesk", "Helvetica Neue", Arial, sans-serif'
32
- t.font_sans = '"Space Grotesk", "Helvetica Neue", Arial, sans-serif'
33
- t.font_mono = '"JetBrains Mono", "Fira Code", monospace'
34
- t.font_url = "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
35
- t.header_background = "linear-gradient(135deg, #003d5c 0%, #0077b3 40%, #00b1ff 100%)"
36
- t.cover_background = "linear-gradient(175deg, #001f30 0%, #003d5c 25%, #0077b3 55%, #00b1ff 85%, #66d9ff 100%)"
37
- t.cover_before_bg = "background: radial-gradient(ellipse at 25% 15%, rgba(0,177,255,0.2) 0%, transparent 45%), radial-gradient(ellipse at 75% 85%, rgba(0,119,179,0.25) 0%, transparent 40%)"
38
- t.cover_after_bg = "height: 4px; background: linear-gradient(90deg, transparent, #00b1ff 30%, #0077b3 50%, #00b1ff 70%, transparent)"
39
- t.progress_bar_color = "#00b1ff"
40
- t.note_border = "#00b1ff"
41
- t.note_bg = "#e0f4ff"
42
- t.note_color = "#0077b3"
43
- t.example_border = "#0077b3"
44
- t.example_bg = "#e8f4fa"
45
- t.example_color = "#004d73"
46
- t.admonition_border = "#e8812e"
47
- t.admonition_bg = "#fff5eb"
48
- t.admonition_color = "#c06a1a"
49
- t.footer_border_color = "#00b1ff"
50
- t.cover_separator_color = "rgba(0,177,255,0.25)"
51
- t.dark_primary_light = "#0a2538"
52
- t.dark_accent_light = "#0d2030"
53
- t.extra_css = <<~CSS
54
- .sourcecode pre { background: #0a1a28; border-color: #1a3050; }
55
- .reading-progress { box-shadow: 0 0 10px rgba(0,177,255,0.3); }
56
- mark.search-match { background: #e0f4ff; box-shadow: 0 0 0 1px rgba(0,177,255,0.3); }
57
- mark.search-match.search-current { background: #00b1ff; color: #fff; }
58
- [data-theme="dark"] .sourcecode pre { background: #060e18; border-color: #0d2030; }
59
- CSS
60
- end
61
- end
62
-
63
- # OGC preface: wrap all preface clauses (except ToC) under a "Preface" heading.
64
- # OGC documents have no foreword/introduction — their preface clauses are
65
- # security, submitting_orgs, submission contacts, etc.
66
- def render_preface(preface, **_opts)
67
- preface_clauses = preface.clause&.reject { |cl| cl.type == "toc" } || []
68
-
69
- return if preface_clauses.empty? &&
70
- !preface.foreword && !preface.introduction &&
71
- !preface.abstract && !preface.acknowledgements &&
72
- !preface.executivesummary
73
-
74
- @output << "<div id=\"preface\" class=\"preface-section\">"
75
- register_toc_entry(id: "preface", level: 1, text: "Preface")
76
- @output << "<h1 class=\"foreword-title\">Preface</h1>"
77
-
78
- preface_clauses.each { |cl| render(cl, level: 2) }
79
-
80
- @output << "</div>"
81
- end
6
+ register_render Metanorma::OgcDocument::Root, :render_document
7
+ register_render Metanorma::StandardDocument::Sections::Preface,
8
+ :render_preface
9
+ register_render Metanorma::StandardDocument::Sections::ClauseSection,
10
+ :render_clause
11
+ register_render Metanorma::StandardDocument::Sections::AnnexSection,
12
+ :render_annex
13
+ register_render Metanorma::StandardDocument::Sections::ContentSection,
14
+ :render_clause
15
+ register_render Metanorma::StandardDocument::Sections::TermsSection,
16
+ :render_terms_section
17
+ register_render Metanorma::StandardDocument::Sections::BibliographySection,
18
+ :render_clause
19
+ register_render Metanorma::StandardDocument::Sections::DefinitionSection,
20
+ :render_clause
82
21
  end
83
22
  end
84
23
  end
@@ -2,50 +2,30 @@
2
2
 
3
3
  module Metanorma
4
4
  module Html
5
- # Renders OimlDocument (OIML) components to HTML.
6
- # Extends IsoRenderer with OIML branding.
7
5
  class OimlRenderer < IsoRenderer
8
- def flavor_publishers(_doc_id)
9
- ["OIML"]
10
- end
6
+ register_render Metanorma::OimlDocument::Root, :render_document
7
+
8
+ DOCTYPE_ID_PATTERN = /\b([RGBDEVS])\s*\d/
11
9
 
12
- def flavor_publisher_name
13
- "OIML"
10
+ def extract_doctype(bibdata)
11
+ oiml_doctype_from_doc_id(bibdata) || super
14
12
  end
15
13
 
16
- def publisher_logo_map
17
- {}
14
+ def extract_stage(bibdata)
15
+ oiml_doctype_from_doc_id(bibdata) || super
18
16
  end
19
17
 
20
- def theme
21
- @theme ||= Theme.new.tap do |t|
22
- t.font_body = '"Merriweather", "Georgia", serif'
23
- t.font_sans = '"Montserrat", "Helvetica Neue", Arial, sans-serif'
24
- t.font_mono = '"JetBrains Mono", "Fira Code", monospace'
25
- t.font_url = "https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;1,400&family=Montserrat:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
26
- t.primary = "#1a5c3a"
27
- t.accent = "#2e8b57"
28
- t.accent_deep = "#247049"
29
- t.gradient = "linear-gradient(135deg, #0d3d22 0%, #1a5c3a 50%, #2e8b57 100%)"
30
- t.primary_light = "#edf7f1"
31
- t.accent_light = "#e0f5e8"
32
- t.warm = "#d4a017"
33
- t.warm_light = "#fdf6e8"
34
- t.header_background = "linear-gradient(135deg, #0d3d22 0%, #1a5c3a 50%, #2e8b57 100%)"
35
- t.cover_background = "linear-gradient(175deg, #071f14 0%, #0d3d22 30%, #1a5c3a 65%, #2e8b57 100%)"
36
- t.cover_before_bg = "background: radial-gradient(ellipse at 30% 20%, rgba(46,139,87,0.2) 0%, transparent 50%), radial-gradient(ellipse at 70% 80%, rgba(212,160,23,0.15) 0%, transparent 40%)"
37
- t.cover_after_bg = "height: 3px; background: linear-gradient(90deg, transparent, #d4a017, transparent)"
38
- t.progress_bar_color = "#2e8b57"
39
- t.note_border = "#2e8b57"
40
- t.note_bg = "#e0f5e8"
41
- t.note_color = "#2e8b57"
42
- t.example_border = "#1a5c3a"
43
- t.example_bg = "#edf7f1"
44
- t.example_color = "#1a5c3a"
45
- t.admonition_border = "#d4a017"
46
- t.admonition_color = "#d4a017"
47
- t.footer_border_color = "#2e8b57"
48
- t.cover_separator_color = "rgba(46,139,87,0.25)"
18
+ private
19
+
20
+ def oiml_doctype_from_doc_id(bibdata)
21
+ labels = theme.doctype_labels
22
+ return nil if labels.empty?
23
+
24
+ doc_id = formatted_doc_id(bibdata).to_s
25
+ return nil if doc_id.empty?
26
+
27
+ if (match = doc_id.match(DOCTYPE_ID_PATTERN))
28
+ labels[match[1]]
49
29
  end
50
30
  end
51
31
  end
@@ -5,45 +5,6 @@ module Metanorma
5
5
  # Renders PDF Association (PDFA) taste documents to HTML.
6
6
  # PDFA brand: #cf9c1d gold + #d03544 red + #4992b2 steel blue from logo
7
7
  class PdfaRenderer < IsoRenderer
8
- def flavor_publishers(_doc_id)
9
- ["PDF Association"]
10
- end
11
-
12
- def flavor_publisher_name
13
- "PDF Association"
14
- end
15
-
16
- def publisher_logo_map
17
- { "PDF Association" => "pdfa-logo.svg" }
18
- end
19
-
20
- def theme
21
- @theme ||= Theme.new.tap do |t|
22
- t.primary = "#3a6e85"
23
- t.accent = "#cf9c1d"
24
- t.accent_deep = "#b08518"
25
- t.gradient = "linear-gradient(135deg, #2a5268 0%, #3a6e85 50%, #4992b2 100%)"
26
- t.primary_light = "#eef5f8"
27
- t.accent_light = "#fdf6e6"
28
- t.warm = "#d03544"
29
- t.warm_light = "#fdeef0"
30
- t.header_background = "linear-gradient(135deg, #2a5268 0%, #3a6e85 40%, #4992b2 100%)"
31
- t.cover_background = "linear-gradient(175deg, #1a3848 0%, #2a5268 25%, #3a6e85 55%, #4992b2 85%, #cf9c1d 100%)"
32
- t.cover_before_bg = "background: radial-gradient(ellipse at 25% 20%, rgba(207,156,29,0.15) 0%, transparent 50%), radial-gradient(ellipse at 70% 80%, rgba(73,146,178,0.2) 0%, transparent 40%)"
33
- t.cover_after_bg = "height: 3px; background: linear-gradient(90deg, transparent, #cf9c1d, #d03544, transparent)"
34
- t.progress_bar_color = "#cf9c1d"
35
- t.note_border = "#4992b2"
36
- t.note_bg = "#eef5f8"
37
- t.note_color = "#4992b2"
38
- t.example_border = "#3a6e85"
39
- t.example_bg = "#eef5f8"
40
- t.example_color = "#3a6e85"
41
- t.admonition_border = "#d03544"
42
- t.admonition_color = "#d03544"
43
- t.footer_border_color = "#cf9c1d"
44
- t.cover_separator_color = "rgba(207,156,29,0.25)"
45
- end
46
- end
47
8
  end
48
9
  end
49
10
  end
@@ -0,0 +1,372 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module Metanorma
6
+ module Html
7
+ module Renderers
8
+ class BlockRenderer
9
+ BLOCK_CHILDREN = {
10
+ paragraphs: :render_paragraph,
11
+ ul: :render_unordered_list,
12
+ ol: :render_ordered_list,
13
+ dl: :render_definition_list,
14
+ sourcecode: :render_sourcecode,
15
+ table: :render_table,
16
+ figure: :render_figure,
17
+ quote: :render_quote,
18
+ formula: :render_formula,
19
+ }.freeze
20
+
21
+ SIMPLE_CHILDREN = {
22
+ paragraphs: :render_paragraph,
23
+ }.freeze
24
+
25
+ NOTE_CHILDREN = {
26
+ paragraphs: :render_paragraph,
27
+ ul: :render_unordered_list,
28
+ ol: :render_ordered_list,
29
+ dl: :render_definition_list,
30
+ quote: :render_quote,
31
+ }.freeze
32
+
33
+ def initialize(coordinator)
34
+ @coordinator = coordinator
35
+ end
36
+
37
+ def render_paragraph(p, **_opts)
38
+ attrs = element_attrs(id: safe_attr(p, :id),
39
+ style: coordinator.alignment_style(safe_attr(p,
40
+ :alignment)))
41
+ content = coordinator.render_mixed_inline(p)
42
+ render_liquid("_paragraph.html.liquid", {
43
+ "attrs" => attrs,
44
+ "content" => content,
45
+ })
46
+ end
47
+
48
+ def render_table(table, **_opts)
49
+ attrs = element_attrs(id: safe_attr(table, :id), class: "table-block")
50
+ table_id = safe_attr(table, :id)
51
+ name_el = safe_attr(table, :fmt_name) || safe_attr(table, :name)
52
+ if table_id && name_el
53
+ register_table_entry(id: table_id,
54
+ text: coordinator.extract_plain_text(name_el))
55
+ end
56
+ col_count = table_column_count(table)
57
+
58
+ caption_html = if name_el
59
+ coordinator.render_inline_element(name_el)
60
+ end
61
+
62
+ colgroup_html = if table.colgroup
63
+ render_table_colgroup(table.colgroup)
64
+ end
65
+
66
+ thead_html = if table.thead
67
+ render_table_section(table.thead, "thead")
68
+ end
69
+
70
+ tbody_html = if table.tbody
71
+ render_table_section(table.tbody, "tbody")
72
+ end
73
+
74
+ tfoot_html = nil
75
+ if table.tfoot || (table.note && !table.note.empty?)
76
+ tfoot_inner = render_table_section_rows(table.tfoot) if table.tfoot
77
+ notes_html = nil
78
+ if table.note && !table.note.empty?
79
+ notes_inner = table.note.filter_map { |n| render_note(n) }.join
80
+ notes_html = render_liquid("_element.html.liquid", "tag" => "tr", "extra_attrs" => "",
81
+ "content" => render_liquid("_element.html.liquid", "tag" => "td", "extra_attrs" => %( colspan="#{col_count}" class="table-notes"), "content" => notes_inner))
82
+ end
83
+ tfoot_html = render_liquid("_element.html.liquid", "tag" => "tfoot", "extra_attrs" => "",
84
+ "content" => "#{tfoot_inner}#{notes_html}")
85
+ end
86
+
87
+ render_liquid("_table.html.liquid", {
88
+ "attrs" => attrs,
89
+ "caption" => caption_html,
90
+ "colgroup_html" => colgroup_html,
91
+ "thead_html" => thead_html,
92
+ "tbody_html" => tbody_html,
93
+ "tfoot_html" => tfoot_html,
94
+ })
95
+ end
96
+
97
+ def table_column_count(table)
98
+ if table.colgroup&.col && !table.colgroup.col.empty?
99
+ return table.colgroup.col.size
100
+ end
101
+
102
+ max_cols = 0
103
+ %i[thead tbody tfoot].each do |section|
104
+ sec = table.public_send(section)
105
+ next unless sec&.tr
106
+
107
+ sec.tr.each do |tr|
108
+ cols = 0
109
+ Array(tr.th).each do |th|
110
+ cols += th.colspan && th.colspan > 1 ? th.colspan : 1
111
+ end
112
+ Array(tr.td).each do |td|
113
+ cols += td.colspan && td.colspan > 1 ? td.colspan : 1
114
+ end
115
+ max_cols = cols if cols > max_cols
116
+ end
117
+ end
118
+ max_cols.positive? ? max_cols : 1
119
+ end
120
+
121
+ def render_table_colgroup(colgroup)
122
+ colgroup.col&.filter_map do |col|
123
+ attrs = element_attrs(style: col.width ? "width: #{col.width}" : nil)
124
+ render_liquid("_element.html.liquid", "tag" => "col",
125
+ "extra_attrs" => attrs, "content" => "")
126
+ end&.join
127
+ end
128
+
129
+ def render_table_section(section, tag_name)
130
+ inner = render_table_section_rows(section)
131
+ render_liquid("_element.html.liquid", "tag" => tag_name,
132
+ "extra_attrs" => "", "content" => inner)
133
+ end
134
+
135
+ def render_table_section_rows(section)
136
+ section.tr&.filter_map do |tr|
137
+ inner_parts = []
138
+ walked = coordinator.walk_ordered(tr) do |type, obj|
139
+ next unless type == :element
140
+
141
+ inner_parts << (render_table_cell(obj) || "")
142
+ end
143
+ unless walked
144
+ Array(tr.th).each do |th|
145
+ inner_parts << (render_table_cell(th, "th") || "")
146
+ end
147
+ Array(tr.td).each do |td|
148
+ inner_parts << (render_table_cell(td, "td") || "")
149
+ end
150
+ end
151
+ render_liquid("_element.html.liquid", "tag" => "tr",
152
+ "extra_attrs" => "", "content" => inner_parts.join)
153
+ end&.join
154
+ end
155
+
156
+ def render_unordered_list(ul, **_opts)
157
+ attrs = element_attrs(id: safe_attr(ul, :id))
158
+ items = ul.listitem&.filter_map do |li|
159
+ render_list_item_content(li)
160
+ end || []
161
+ render_liquid("_list.html.liquid", {
162
+ "list_tag" => "ul",
163
+ "attrs" => attrs,
164
+ "items" => items,
165
+ })
166
+ end
167
+
168
+ def render_table_cell(cell, force_tag = nil)
169
+ tag_name = force_tag || (cell.is_a?(Metanorma::Document::Components::Tables::HeaderTableCell) ? "th" : "td")
170
+ attrs = element_attrs(
171
+ colspan: safe_attr(cell, :colspan),
172
+ rowspan: safe_attr(cell, :rowspan),
173
+ align: safe_attr(cell, :alignment),
174
+ valign: safe_attr(cell, :vertical_alignment),
175
+ )
176
+ content = coordinator.render_cell_content(cell)
177
+ render_liquid("_element.html.liquid", {
178
+ "tag" => tag_name,
179
+ "extra_attrs" => attrs,
180
+ "content" => content,
181
+ })
182
+ end
183
+
184
+ def render_ordered_list(ol, **_opts)
185
+ attrs = element_attrs(id: safe_attr(ol, :id),
186
+ start: safe_attr(ol, :start), type: safe_attr(ol, :type_attr))
187
+ items = ol.listitem&.filter_map do |li|
188
+ render_list_item_content(li)
189
+ end || []
190
+ render_liquid("_list.html.liquid", {
191
+ "list_tag" => "ol",
192
+ "attrs" => attrs,
193
+ "items" => items,
194
+ })
195
+ end
196
+
197
+ def render_list_item_content(li)
198
+ li_id = safe_attr(li, :id)
199
+ attrs = li_id ? %( id="#{escape_html(li_id)}") : ""
200
+ inner = coordinator.render_mixed_content_in_order(li)
201
+ render_liquid("_list_item.html.liquid", "attrs" => attrs,
202
+ "content" => inner)
203
+ end
204
+
205
+ def render_definition_list(dl, **_opts)
206
+ attrs = element_attrs(id: safe_attr(dl, :id))
207
+ terms = dl.dt&.each_with_index&.map do |dt, i|
208
+ dt_html = coordinator.render_mixed_inline(dt)
209
+ dd = dl.dd&.[](i)
210
+ dd_html = dd ? coordinator.render_mixed_inline(dd) : nil
211
+ { "dt" => dt_html, "dd" => dd_html }
212
+ end || []
213
+ render_liquid("_definition_list.html.liquid", {
214
+ "attrs" => attrs,
215
+ "terms" => terms,
216
+ })
217
+ end
218
+
219
+ def render_figure(figure, **_opts)
220
+ drop = Drops::FigureDrop.from_model(figure,
221
+ renderer: coordinator.renderer_context)
222
+ render_liquid("_figure.html.liquid", { "block" => drop })
223
+ end
224
+
225
+ def render_image(image)
226
+ src_val = image_source(image)
227
+ attrs = element_attrs(
228
+ id: safe_attr(image, :id),
229
+ src: src_val,
230
+ alt: safe_attr(image, :alt),
231
+ height: safe_attr(image, :height),
232
+ width: safe_attr(image, :width),
233
+ )
234
+ render_liquid("_image.html.liquid", { "attrs" => attrs })
235
+ end
236
+
237
+ def image_source(image)
238
+ svg_xml = safe_attr(image, :inline_svg)
239
+ if svg_xml && !svg_xml.empty?
240
+ "data:image/svg+xml;base64,#{Base64.strict_encode64(svg_xml)}"
241
+ else
242
+ safe_attr(image, :source)
243
+ end
244
+ end
245
+
246
+ def render_video(video)
247
+ attrs = element_attrs(
248
+ id: safe_attr(video, :id),
249
+ src: safe_attr(video, :src),
250
+ )
251
+ render_liquid("_video.html.liquid", { "attrs" => attrs })
252
+ end
253
+
254
+ def render_audio(audio)
255
+ attrs = element_attrs(
256
+ id: safe_attr(audio, :id),
257
+ src: safe_attr(audio, :src),
258
+ )
259
+ render_liquid("_audio.html.liquid", { "attrs" => attrs })
260
+ end
261
+
262
+ def render_note(note, **_opts)
263
+ drop = Drops::NoteDrop.from_model(note,
264
+ renderer: coordinator.renderer_context)
265
+ render_liquid("_note.html.liquid", { "block" => drop })
266
+ end
267
+
268
+ def render_example(example, **_opts)
269
+ drop = Drops::ExampleDrop.from_model(example,
270
+ renderer: coordinator.renderer_context)
271
+ render_liquid("_example.html.liquid", { "block" => drop })
272
+ end
273
+
274
+ def render_sourcecode(sc, **_opts)
275
+ drop = Drops::SourcecodeDrop.from_model(sc,
276
+ renderer: coordinator.renderer_context)
277
+ render_liquid("_sourcecode.html.liquid", { "block" => drop })
278
+ end
279
+
280
+ def render_formula(formula, **_opts)
281
+ drop = Drops::FormulaDrop.from_model(formula,
282
+ renderer: coordinator.renderer_context)
283
+ render_liquid("_formula.html.liquid", { "block" => drop })
284
+ end
285
+
286
+ def render_quote(quote, **_opts)
287
+ attrs = element_attrs(id: safe_attr(quote, :id), class: "quote")
288
+ content_parts = []
289
+ quote.paragraphs&.each do |para|
290
+ content_parts << (render_paragraph(para) || "")
291
+ end
292
+ quote.ul&.each do |ul|
293
+ content_parts << (render_unordered_list(ul) || "")
294
+ end
295
+ quote.ol&.each do |ol|
296
+ content_parts << (render_ordered_list(ol) || "")
297
+ end
298
+ content = content_parts.join
299
+ attribution_html = if quote.attribution
300
+ coordinator.render_mixed_inline(quote.attribution)
301
+ end
302
+ render_liquid("_quote.html.liquid", {
303
+ "attrs" => attrs,
304
+ "content" => content,
305
+ "attribution" => attribution_html,
306
+ })
307
+ end
308
+
309
+ def render_admonition(admonition, **_opts)
310
+ drop = Drops::AdmonitionDrop.from_model(admonition,
311
+ renderer: coordinator.renderer_context)
312
+ render_liquid("_admonition.html.liquid", { "block" => drop })
313
+ end
314
+
315
+ def render_bookmark(bookmark, **_opts)
316
+ render_liquid("_bookmark.html.liquid", {
317
+ "id" => escape_html(safe_attr(bookmark, :id).to_s),
318
+ })
319
+ end
320
+
321
+ def render_block_children(model, children:)
322
+ parts = []
323
+ children.each do |attr, render_method|
324
+ values = safe_attr(model, attr)
325
+ next if values.nil?
326
+
327
+ Array(values).each do |v|
328
+ parts << (public_send(render_method, v) || "")
329
+ end
330
+ end
331
+ parts.join
332
+ end
333
+
334
+ def render_note_children(model)
335
+ render_block_children(model, children: NOTE_CHILDREN)
336
+ end
337
+
338
+ def render_simple_children(model)
339
+ render_block_children(model, children: SIMPLE_CHILDREN)
340
+ end
341
+
342
+ def render_full_block_children(model)
343
+ render_block_children(model, children: BLOCK_CHILDREN)
344
+ end
345
+
346
+ private
347
+
348
+ attr_reader :coordinator
349
+
350
+ def safe_attr(obj, method_name)
351
+ coordinator.safe_attr(obj, method_name)
352
+ end
353
+
354
+ def escape_html(text)
355
+ coordinator.escape_html(text)
356
+ end
357
+
358
+ def element_attrs(**attrs)
359
+ coordinator.element_attrs(**attrs)
360
+ end
361
+
362
+ def render_liquid(template_name, assigns)
363
+ coordinator.render_liquid(template_name, assigns)
364
+ end
365
+
366
+ def register_table_entry(id:, text:)
367
+ coordinator.register_table_entry(id: id, text: text)
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end