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
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Handlers
6
+ module Table
7
+ def self.call(element, context:)
8
+ attrs = table_attrs(element)
9
+ content = []
10
+
11
+ thead = SafeAttr.read(element, :thead)
12
+ if thead
13
+ rows = extract_rows(thead, context:)
14
+ unless rows.empty?
15
+ content << Handlers.build_node("table_head",
16
+ content: rows)
17
+ end
18
+ end
19
+
20
+ tbody = SafeAttr.read(element, :tbody)
21
+ if tbody
22
+ rows = extract_rows(tbody, context:)
23
+ unless rows.empty?
24
+ content << Handlers.build_node("table_body",
25
+ content: rows)
26
+ end
27
+ end
28
+
29
+ tfoot = SafeAttr.read(element, :tfoot)
30
+ if tfoot
31
+ rows = extract_rows(tfoot, context:)
32
+ unless rows.empty?
33
+ content << Handlers.build_node("table_foot",
34
+ content: rows)
35
+ end
36
+ end
37
+
38
+ Handlers.build_node("table", attrs: attrs, content: content)
39
+ end
40
+
41
+ def self.extract_rows(table_section, context:)
42
+ Array(table_section.tr).map do |tr|
43
+ cells = Array(tr.td).map { |td| build_cell(td, context:) }
44
+ Array(tr.th).each { |th| cells << build_cell(th, context:) }
45
+
46
+ row_attrs = {}
47
+ row_attrs[:id] = SafeAttr.read(tr, :id)
48
+ Handlers.build_node("table_row", attrs: row_attrs.compact,
49
+ content: cells)
50
+ end
51
+ end
52
+
53
+ def self.build_cell(cell, context:)
54
+ attrs = {}
55
+ attrs[:colspan] = SafeAttr.read(cell, :colspan)
56
+ attrs[:rowspan] = SafeAttr.read(cell, :rowspan)
57
+ attrs[:align] = SafeAttr.read(cell, :align)
58
+ attrs[:valign] = SafeAttr.read(cell, :valign)
59
+
60
+ content = Inline.extract_inline(cell, context:)
61
+
62
+ Handlers.build_node("table_cell", attrs: attrs.compact,
63
+ content: content)
64
+ end
65
+
66
+ def self.table_attrs(element)
67
+ attrs = {}
68
+ attrs[:id] = SafeAttr.read(element, :id)
69
+ attrs[:width] = SafeAttr.read(element, :width)
70
+ attrs[:align] = SafeAttr.read(element, :align)
71
+ attrs[:unnumbered] = SafeAttr.read(element, :unnumbered)
72
+ attrs[:semx_id] = SafeAttr.read(element, :semx_id)
73
+
74
+ name = SafeAttr.read(element, :name)
75
+ attrs[:title] = Handlers.extract_name_text(name) if name
76
+ attrs.compact
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Handlers
6
+ module Term
7
+ def self.call(element, context:)
8
+ attrs = {}
9
+ attrs[:id] = SafeAttr.read(element, :id)
10
+ attrs[:anchor] = SafeAttr.read(element, :anchor)
11
+
12
+ fmt_name = SafeAttr.read(element, :fmt_name)
13
+ if fmt_name
14
+ number = Inline.extract_formatted_text(fmt_name)
15
+ attrs[:number] = number unless number.empty?
16
+ end
17
+
18
+ content = []
19
+
20
+ # Term name from fmt_preferred paragraphs
21
+ extract_fmt_paragraphs(element, :fmt_preferred, content, context)
22
+
23
+ # Definition from fmt_definition → semx → p
24
+ fmt_def = SafeAttr.read(element, :fmt_definition)
25
+ if fmt_def
26
+ extract_semx_paragraphs(fmt_def, content, context)
27
+ end
28
+
29
+ # Source from fmt_termsource
30
+ fmt_ts_list = SafeAttr.read(element, :fmt_termsource)
31
+ if fmt_ts_list && !fmt_ts_list.empty?
32
+ source_text = Inline.extract_formatted_text(fmt_ts_list.first)
33
+ unless source_text.empty?
34
+ content << Handlers.build_node("paragraph",
35
+ attrs: { class: "source" },
36
+ content: [context.text_node(source_text)])
37
+ end
38
+ end
39
+
40
+ # Term notes
41
+ SafeAttr.read(element, :termnote)&.each do |tn|
42
+ note_attrs = { id: SafeAttr.read(tn, :id) }
43
+ fn = SafeAttr.read(tn, :fmt_name)
44
+ if fn
45
+ num = Inline.extract_formatted_text(fn)
46
+ note_attrs[:number] = num unless num.empty?
47
+ end
48
+ note_content = context.extract_named_collections(tn, %i[p ul ol dl])
49
+ content << Handlers.build_node("note", attrs: note_attrs.compact,
50
+ content: note_content)
51
+ end
52
+
53
+ # Term examples
54
+ SafeAttr.read(element, :termexample)&.each do |te|
55
+ ex_attrs = { id: SafeAttr.read(te, :id) }
56
+ ex_content = context.extract_named_collections(te, %i[p ul ol dl])
57
+ content << Handlers.build_node("example", attrs: ex_attrs.compact,
58
+ content: ex_content)
59
+ end
60
+
61
+ # Nested terms
62
+ SafeAttr.read(element, :term)&.each do |t|
63
+ result = context.registry.handle(t, context: context)
64
+ result.append_to(content)
65
+ end
66
+
67
+ Handlers.build_node("term", attrs: attrs.compact, content: content)
68
+ end
69
+
70
+ def self.extract_fmt_paragraphs(element, attr_name, content, context)
71
+ fmt_list = SafeAttr.read(element, attr_name)
72
+ return unless fmt_list
73
+
74
+ fmt_list.each do |fmt_el|
75
+ ps = SafeAttr.read(fmt_el, :p)
76
+ next unless ps
77
+
78
+ ps.each do |p|
79
+ result = context.registry.handle(p, context: context)
80
+ result.append_to(content)
81
+ end
82
+ end
83
+ end
84
+
85
+ # fmt_definition wraps content in semx elements → extract p from semx
86
+ def self.extract_semx_paragraphs(fmt_def, content, context)
87
+ semx_list = SafeAttr.read(fmt_def, :semx)
88
+ return unless semx_list
89
+
90
+ Array(semx_list).each do |s|
91
+ SafeAttr.read(s, :p)&.each do |p|
92
+ result = context.registry.handle(p, context: context)
93
+ result.append_to(content)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Handlers
6
+ autoload :Paragraph, "#{__dir__}/handlers/paragraph"
7
+ autoload :Section, "#{__dir__}/handlers/section"
8
+ autoload :List, "#{__dir__}/handlers/list"
9
+ autoload :Table, "#{__dir__}/handlers/table"
10
+ autoload :Figure, "#{__dir__}/handlers/figure"
11
+ autoload :Sourcecode, "#{__dir__}/handlers/sourcecode"
12
+ autoload :Admonition, "#{__dir__}/handlers/admonition"
13
+ autoload :Formula, "#{__dir__}/handlers/formula"
14
+ autoload :Example, "#{__dir__}/handlers/example"
15
+ autoload :Note, "#{__dir__}/handlers/note"
16
+ autoload :Quote, "#{__dir__}/handlers/quote"
17
+ autoload :Review, "#{__dir__}/handlers/review"
18
+ autoload :Inline, "#{__dir__}/handlers/inline"
19
+ autoload :Structural, "#{__dir__}/handlers/structural"
20
+ autoload :Term, "#{__dir__}/handlers/term"
21
+
22
+ COMMON_ATTRS = %i[id semx_id].freeze
23
+
24
+ def self.build_node(type, attrs: {}, content: [])
25
+ if content.nil? || content.empty?
26
+ Model::Leaf.new(type: type, attrs: attrs)
27
+ else
28
+ Model::Container.new(type: type, attrs: attrs, content: content)
29
+ end
30
+ end
31
+
32
+ def self.build_text(text, marks: [])
33
+ Model::Text.new(text: text, marks: marks)
34
+ end
35
+
36
+ def self.build_mark(type, attrs: {})
37
+ Model::Mark.new(type: type, attrs: attrs)
38
+ end
39
+
40
+ def self.extract_attrs(element, extra_attrs: {})
41
+ attrs = {}
42
+ COMMON_ATTRS.each do |attr|
43
+ attrs[attr] = SafeAttr.read(element, attr)
44
+ end
45
+ extra_attrs.each do |attr, source_attr|
46
+ attrs[attr] = SafeAttr.read(element, source_attr || attr)
47
+ end
48
+ attrs.compact
49
+ end
50
+
51
+ def self.extract_name_text(name)
52
+ Inline::TextExtractor.extract_name_text(name)
53
+ end
54
+
55
+ def self.extract_bibdata_title(bibdata)
56
+ Metadata.title_from_bibdata(bibdata)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module IdStrategy
6
+ # Assign positional IDs (sec-X.Y.Z, table-N, fig-N, anx-X) to elements
7
+ # that have UUID IDs. Elements with author-assigned explicit IDs are
8
+ # preserved. Cross-reference targets are translated to use the new IDs.
9
+ #
10
+ # Positional IDs are derived from the element's resolved section number
11
+ # (the "number" attribute on the presentation XML element), which
12
+ # represents the document's structural position.
13
+ #
14
+ # Example:
15
+ # UUID element with number "5.4" → id: "sec-5.4"
16
+ # UUID element with number "A.2" → id: "anx-A.2"
17
+ # Explicit id "sec-3.1.3.4" → id: "sec-3.1.3.4" (unchanged)
18
+ class Positional < Base
19
+ @categories = {}
20
+
21
+ class << self
22
+ # Registry of Metanorma model classes → positional-ID category
23
+ # (:section, :annex, :figure, :table). Adding new flavors or new
24
+ # model types requires only a new register_category call — no
25
+ # edits to derive/element_category (OCP).
26
+ def categories
27
+ @categories
28
+ end
29
+
30
+ def register_category(model_class, category)
31
+ categories[model_class] = category
32
+ self
33
+ end
34
+
35
+ def unregister_category(model_class)
36
+ categories.delete(model_class)
37
+ self
38
+ end
39
+
40
+ def category_for(element)
41
+ return categories[element.class] if categories.key?(element.class)
42
+
43
+ categories.each do |klass, category|
44
+ return category if element.is_a?(klass)
45
+ end
46
+ nil
47
+ end
48
+ end
49
+
50
+ register_category Metanorma::StandardDocument::Sections::ClauseSection,
51
+ :section
52
+ register_category Metanorma::StandardDocument::Sections::ContentSection,
53
+ :section
54
+ register_category Metanorma::StandardDocument::Sections::TermsSection,
55
+ :section
56
+ register_category Metanorma::StandardDocument::Sections::DefinitionSection,
57
+ :section
58
+ register_category Metanorma::StandardDocument::Sections::AnnexSection,
59
+ :annex
60
+ register_category Metanorma::Document::Components::AncillaryBlocks::FigureBlock,
61
+ :figure
62
+ register_category Metanorma::Document::Components::Tables::TableBlock,
63
+ :table
64
+
65
+ def initialize
66
+ @id_map = {}
67
+ end
68
+
69
+ def assign_id(element)
70
+ raw = raw_id(element)
71
+ return raw unless uuid?(raw)
72
+
73
+ positional = derive(element)
74
+ return raw unless positional
75
+
76
+ @id_map[raw] = positional
77
+ positional
78
+ end
79
+
80
+ def finalize!(document)
81
+ translate_targets(document)
82
+ document
83
+ end
84
+
85
+ private
86
+
87
+ def raw_id(element)
88
+ SafeAttr.read(element, :id)
89
+ end
90
+
91
+ def uuid?(id)
92
+ id&.start_with?("_")
93
+ end
94
+
95
+ def derive(element)
96
+ number = extract_number(element)
97
+ return nil unless number && !number.strip.empty?
98
+
99
+ case self.class.category_for(element)
100
+ when :section
101
+ number.match?(/\A[\d.]+\z/) ? "sec-#{number}" : nil
102
+ when :annex
103
+ number.match?(/\A[A-Z]/) ? "anx-#{number}" : nil
104
+ when :figure
105
+ "fig-#{number}"
106
+ when :table
107
+ "table-#{number}"
108
+ end
109
+ end
110
+
111
+ def extract_number(element)
112
+ number = SafeAttr.read(element, :number)
113
+ return number if number && !number.strip.empty?
114
+
115
+ attrs = element.class.attributes
116
+ return nil unless attrs.key?(:fmt_title)
117
+
118
+ fmt_title = element.fmt_title
119
+ return nil unless fmt_title
120
+
121
+ parts = collect_autonum_text(fmt_title)
122
+ parts.empty? ? nil : parts.join(".")
123
+ end
124
+
125
+ def collect_autonum_text(node)
126
+ parts = []
127
+ attrs = node.class.attributes
128
+
129
+ if attrs.key?(:semx) && node.semx
130
+ node.semx.each do |s|
131
+ next unless s.element_attr == "autonum"
132
+
133
+ text = s.text&.join
134
+ parts << text if text && !text.empty?
135
+ end
136
+ end
137
+
138
+ if attrs.key?(:span) && node.span
139
+ node.span.each { |sp| parts.concat(collect_autonum_text(sp)) }
140
+ end
141
+
142
+ parts
143
+ end
144
+
145
+ def translate_targets(node)
146
+ case node
147
+ when Model::Text
148
+ node.marks.each { |mark| translate_mark_target(mark) }
149
+ when Model::Container
150
+ node.content.each { |child| translate_targets(child) }
151
+ end
152
+ end
153
+
154
+ def translate_mark_target(mark)
155
+ return unless mark.type == "xref"
156
+
157
+ target = mark["target"]
158
+ return unless target
159
+
160
+ translated = @id_map[target]
161
+ mark["target"] = translated if translated
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module IdStrategy
6
+ # Preserve all IDs as-is (default behavior).
7
+ # UUID elements keep their UUID IDs. Explicit IDs are unchanged.
8
+ class Preserve < Base
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module IdStrategy
6
+ autoload :Preserve, "#{__dir__}/id_strategy/preserve"
7
+ autoload :Positional, "#{__dir__}/id_strategy/positional"
8
+
9
+ # Base class for ID assignment strategies.
10
+ #
11
+ # A strategy controls how element IDs are assigned during mirror
12
+ # generation. Subclasses override assign_id and finalize!.
13
+ #
14
+ # assign_id(element) — called per element during node construction
15
+ # finalize!(document) — called once after the full mirror tree is built
16
+ #
17
+ # Adding a new strategy = adding a new class. No changes to handlers
18
+ # or the converter (OCP).
19
+ class Base
20
+ # Returns the ID string to assign to the mirror node for this element.
21
+ def assign_id(element)
22
+ SafeAttr.read(element, :id)
23
+ end
24
+
25
+ # Post-process the completed mirror document after all IDs are assigned.
26
+ # Use this to translate cross-reference targets, etc.
27
+ # Returns the (possibly modified) document.
28
+ def finalize!(document)
29
+ document
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ # MathUtil is the single home for math-content extraction helpers.
6
+ #
7
+ # These helpers convert mathML XML objects into strings (stripped of
8
+ # XML declarations) and extract asciimath from stem elements. They
9
+ # are used by handlers and renderers that need to carry math content
10
+ # through the mirror pipeline.
11
+ module MathUtil
12
+ STRIP_XML_DECL_PATTERN = /\A<\?xml[^?]*\?>\s?/
13
+ private_constant :STRIP_XML_DECL_PATTERN
14
+
15
+ def self.strip_xml_decl(math_xml)
16
+ math_xml.sub(STRIP_XML_DECL_PATTERN, "")
17
+ end
18
+
19
+ def self.mathml_from_math(math)
20
+ xml = math.is_a?(Array) ? math.map(&:to_xml).join : math.to_xml
21
+ strip_xml_decl(xml)
22
+ end
23
+
24
+ # Extract asciimath text from a stem element. Returns nil if no
25
+ # asciimath is present.
26
+ def self.asciimath_from_stem(stem)
27
+ asciimath = SafeAttr.read(stem, :asciimath)
28
+ return nil unless asciimath.is_a?(Array)
29
+
30
+ parts = asciimath.filter_map do |a|
31
+ next a if a.is_a?(String)
32
+ next SafeAttr.read(a, :text).to_s if a.is_a?(Lutaml::Model::Serializable)
33
+
34
+ nil
35
+ end
36
+ joined = parts.join.strip
37
+ joined.empty? ? nil : joined
38
+ end
39
+
40
+ # Best-effort plain-text extraction from a stem element. Tries
41
+ # asciimath first; falls back to SafeAttr :text.
42
+ def self.text_from_stem(stem)
43
+ asciimath = asciimath_from_stem(stem)
44
+ return asciimath if asciimath
45
+
46
+ text = SafeAttr.read(stem, :text)
47
+ return text.to_s if text.is_a?(String) && !text.strip.empty?
48
+
49
+ ""
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ # Extracts metadata (title, etc.) from a parsed Metanorma document's
6
+ # bibdata. This is a standalone service that the pipeline and other
7
+ # consumers can use without depending on the Handlers layer.
8
+ module Metadata
9
+ # Returns the first title string from a bibdata object, or nil.
10
+ # The bibdata must respond to `title` and return a String, an Array
11
+ # of Strings, or an Array of Lutaml::Model::Serializables with
12
+ # `content`.
13
+ def self.title_from_bibdata(bibdata)
14
+ return nil unless bibdata
15
+
16
+ title = bibdata.title
17
+ return nil unless title
18
+
19
+ case title
20
+ when String then title
21
+ when Array
22
+ first = title.first
23
+ return nil unless first
24
+
25
+ if first.is_a?(String)
26
+ first
27
+ elsif first.is_a?(Lutaml::Model::Serializable)
28
+ Array(first.content).join
29
+ else
30
+ first.to_s
31
+ end
32
+ else title.to_s
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ class MetanormaToMirror
6
+ attr_reader :registry, :id_strategy
7
+
8
+ def initialize(registry: Mirror.default_registry,
9
+ id_strategy: Mirror.DEFAULT_ID_STRATEGY)
10
+ @registry = registry
11
+ @id_strategy = id_strategy
12
+ end
13
+
14
+ def call(root)
15
+ attrs = {}
16
+ attrs[:flavor] = root.flavor if root.flavor
17
+ attrs[:type] = root.type if root.type
18
+ attrs[:schema_version] = root.schema_version if root.schema_version
19
+
20
+ title = extract_root_title(root)
21
+ attrs[:title] = title if title
22
+
23
+ content = []
24
+
25
+ preface = root.preface
26
+ if preface
27
+ result = @registry.handle(preface, context: self)
28
+ result.append_to(content)
29
+ end
30
+
31
+ sections = root.sections
32
+ if sections
33
+ result = @registry.handle(sections, context: self)
34
+ result.append_to(content)
35
+ end
36
+
37
+ annex = root.annex
38
+ annex&.each do |a|
39
+ result = @registry.handle(a, context: self)
40
+ result.append_to(content)
41
+ end
42
+
43
+ bibliography = root.bibliography
44
+ if bibliography
45
+ result = @registry.handle(bibliography, context: self)
46
+ result.append_to(content)
47
+ end
48
+
49
+ Handlers.build_node("doc", attrs: attrs, content: content)
50
+ end
51
+
52
+ def extract_blocks(element)
53
+ content = []
54
+
55
+ element.each_mixed_content do |node|
56
+ next if node.is_a?(String)
57
+
58
+ result = @registry.handle(node, context: self)
59
+ result.append_to(content)
60
+ end
61
+ content
62
+ end
63
+
64
+ def extract_section_children(element)
65
+ content = []
66
+ %i[clause terms definitions references floating_title].each do |attr|
67
+ collection = SafeAttr.read(element, attr)
68
+ next unless collection
69
+
70
+ collection.each do |child|
71
+ result = @registry.handle(child, context: self)
72
+ result.append_to(content)
73
+ end
74
+ end
75
+ content
76
+ end
77
+
78
+ def extract_named_collections(element, collection_attrs)
79
+ content = []
80
+ collection_attrs.each do |attr|
81
+ collection = SafeAttr.read(element, attr)
82
+ next unless collection
83
+
84
+ collection.each do |child|
85
+ result = @registry.handle(child, context: self)
86
+ result.append_to(content)
87
+ end
88
+ end
89
+ content
90
+ end
91
+
92
+ def text_node(text, marks: [])
93
+ Handlers.build_text(text, marks: marks)
94
+ end
95
+
96
+ def extract_root_title(root)
97
+ Metadata.title_from_bibdata(SafeAttr.read(root, :bibdata))
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Container < Node
7
+ attr_reader :content
8
+
9
+ def initialize(type:, attrs: {}, content: [])
10
+ super(type: type, attrs: attrs)
11
+ @content = Array(content)
12
+ end
13
+
14
+ def container?
15
+ true
16
+ end
17
+
18
+ def to_h
19
+ h = super
20
+ unless @content.empty?
21
+ h["content"] = @content.map { |c| serialize_child(c) }
22
+ end
23
+ h
24
+ end
25
+
26
+ def text_content
27
+ @content.map do |item|
28
+ item.is_a?(String) ? item : item.text_content
29
+ end.join
30
+ end
31
+
32
+ def accept_rewriter(rewriter)
33
+ rewriter.rewrite_container(self)
34
+ end
35
+
36
+ private
37
+
38
+ def serialize_child(child)
39
+ case child
40
+ when String then child
41
+ else child.to_h
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end