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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +164 -66
- data/CLAUDE.md +26 -0
- data/README.adoc +144 -0
- data/Rakefile +14 -2
- data/TODO.finalize/01-split-multi-class-files.md +8 -0
- data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +6 -0
- data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +11 -0
- data/TODO.finalize/04-theme-per-flavor-config-driven.md +9 -0
- data/TODO.finalize/05-migrate-all-html-to-liquid.md +16 -0
- data/TODO.finalize/06-integrate-pubid-library.md +16 -0
- data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +39 -0
- data/TODO.finalize/08-refactor-renderer-architecture.md +25 -0
- data/TODO.finalize/09-eliminate-code-smells.md +14 -0
- data/TODO.finalize/10-comprehensive-spec-coverage.md +24 -0
- data/TODO.finalize/11-config-driven-flavor-metadata.md +20 -0
- data/TODO.finalize/12-add-frozen-string-literal.md +6 -0
- data/TODO.finalize/13-extract-composition-from-god-class.md +96 -0
- data/TODO.finalize/14-config-driven-section-ordering.md +31 -0
- data/TODO.finalize/15-theme-directories-per-flavor.md +46 -0
- data/TODO.finalize/16-theme-lutaml-model-schema.md +44 -0
- data/data/javascripts/components/mn-footnotes.js +68 -0
- data/data/javascripts/components/mn-lightbox.js +18 -2
- data/data/javascripts/components/mn-shortcuts.js +2 -0
- data/data/javascripts/core/mn-navigation.js +8 -0
- data/data/javascripts/core/mn-theme.js +9 -9
- data/data/logos/oiml-logo-full-dark.svg +1 -0
- data/data/logos/oiml-logo-full-light.svg +1 -0
- data/data/logos/oiml-logo-icon-dark.svg +1 -0
- data/data/logos/oiml-logo-icon-light.svg +1 -0
- data/data/logos/oiml-logo.svg +1 -0
- data/data/logos/pdfa-logo.svg +31 -94
- data/data/stylesheets/base/_layout.css +3 -0
- data/data/stylesheets/base/_reset.css +3 -3
- data/data/stylesheets/base/_typography.css +0 -5
- data/data/stylesheets/components/bibliography.css +3 -3
- data/data/stylesheets/components/cover.css +3 -1
- data/data/stylesheets/components/figure.css +13 -1
- data/data/stylesheets/components/footnote.css +65 -5
- data/data/stylesheets/components/formula.css +44 -0
- data/data/stylesheets/components/header.css +17 -2
- data/data/stylesheets/components/inline.css +1 -1
- data/data/stylesheets/components/table.css +15 -2
- data/data/stylesheets/components/term.css +0 -1
- data/data/themes/bipm.yaml +60 -0
- data/data/themes/cc.yaml +43 -0
- data/data/themes/icc.yaml +39 -0
- data/data/themes/iec.yaml +44 -0
- data/data/themes/ieee.yaml +43 -0
- data/data/themes/ietf.yaml +45 -0
- data/data/themes/iho.yaml +46 -0
- data/data/themes/iso.yaml +40 -0
- data/data/themes/itu.yaml +45 -0
- data/data/themes/ogc.yaml +51 -0
- data/data/themes/oiml.yaml +56 -0
- data/data/themes/pdfa.yaml +44 -0
- data/data/themes/ribose.yaml +43 -0
- data/exe/metanorma-document +11 -0
- data/frontend/e2e/sample-html.test.ts +256 -0
- data/frontend/index.html +12 -0
- data/frontend/package-lock.json +4725 -0
- data/frontend/package.json +32 -0
- data/frontend/postcss.config.js +6 -0
- data/frontend/src/App.vue +79 -0
- data/frontend/src/app.ts +21 -0
- data/frontend/src/components/AppSidebar.vue +77 -0
- data/frontend/src/components/DocumentReader.vue +180 -0
- data/frontend/src/components/MirrorRenderer.vue +23 -0
- data/frontend/src/components/ReadingProgressBar.vue +29 -0
- data/frontend/src/components/TextRenderer.vue +131 -0
- data/frontend/src/components/TocItem.vue +57 -0
- data/frontend/src/components/TopBar.vue +49 -0
- data/frontend/src/components/__tests__/MirrorRenderer.test.ts +178 -0
- data/frontend/src/components/blocks/AdmonitionBlock.vue +74 -0
- data/frontend/src/components/blocks/ClauseBlock.vue +53 -0
- data/frontend/src/components/blocks/ExampleBlock.vue +21 -0
- data/frontend/src/components/blocks/FallbackBlock.vue +18 -0
- data/frontend/src/components/blocks/FigureBlock.vue +26 -0
- data/frontend/src/components/blocks/FootnotesBlock.vue +25 -0
- data/frontend/src/components/blocks/ListBlocks.vue +62 -0
- data/frontend/src/components/blocks/NoteBlock.vue +21 -0
- data/frontend/src/components/blocks/ParagraphBlock.vue +25 -0
- data/frontend/src/components/blocks/QuoteBlock.vue +25 -0
- data/frontend/src/components/blocks/ReviewBlock.vue +39 -0
- data/frontend/src/components/blocks/SourcecodeBlock.vue +52 -0
- data/frontend/src/components/blocks/TableBlock.vue +48 -0
- data/frontend/src/components/blocks/index.ts +36 -0
- data/frontend/src/stores/__tests__/documentStore.fixture.test.ts +272 -0
- data/frontend/src/stores/__tests__/documentStore.test.ts +143 -0
- data/frontend/src/stores/__tests__/ebookStore.test.ts +145 -0
- data/frontend/src/stores/__tests__/uiStore.test.ts +42 -0
- data/frontend/src/stores/documentStore.ts +153 -0
- data/frontend/src/stores/ebookStore.ts +179 -0
- data/frontend/src/stores/uiStore.ts +32 -0
- data/frontend/src/styles.css +156 -0
- data/frontend/src/types/__tests__/rendering-contract.test.ts +172 -0
- data/frontend/src/types/rendering-contract.ts +209 -0
- data/frontend/src/utils/__tests__/typeMetadata.test.ts +102 -0
- data/frontend/src/utils/typeMetadata.ts +121 -0
- data/frontend/tailwind.config.js +18 -0
- data/frontend/test/fixtures/iso-document.json +271 -0
- data/frontend/tmp/debug_e2e.cjs +46 -0
- data/frontend/tsconfig.json +25 -0
- data/frontend/tsconfig.node.json +11 -0
- data/frontend/vite.config.ts +37 -0
- data/frontend/vitest.e2e.config.ts +9 -0
- data/lib/metanorma/bipm_document/metadata/bipm_bibliographic_item.rb +0 -14
- data/lib/metanorma/bipm_document/metadata/depiction_element.rb +18 -0
- data/lib/metanorma/bipm_document/metadata.rb +1 -1
- data/lib/metanorma/document/cli.rb +116 -0
- data/lib/metanorma/document/components/ancillary_blocks/callout_annotation.rb +27 -0
- data/lib/metanorma/document/components/ancillary_blocks/sourcecode_block.rb +0 -65
- data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +18 -0
- data/lib/metanorma/document/components/ancillary_blocks.rb +4 -0
- data/lib/metanorma/document/components/bib_data/bib_data.rb +7 -0
- data/lib/metanorma/document/components/blocks/classification_value.rb +21 -0
- data/lib/metanorma/document/components/blocks/permission_model.rb +28 -0
- data/lib/metanorma/document/components/blocks/recommendation_model.rb +28 -0
- data/lib/metanorma/document/components/blocks/requirement_base.rb +30 -0
- data/lib/metanorma/document/components/blocks/requirement_classification.rb +20 -0
- data/lib/metanorma/document/components/blocks/requirement_description.rb +43 -0
- data/lib/metanorma/document/components/blocks/requirement_inherit.rb +24 -0
- data/lib/metanorma/document/components/blocks/requirement_model.rb +0 -131
- data/lib/metanorma/document/components/blocks.rb +8 -7
- data/lib/metanorma/document/components/id_elements/image.rb +2 -0
- data/lib/metanorma/document/components/inline/asciimath_element.rb +2 -0
- data/lib/metanorma/document/components/inline/attribution_element.rb +2 -0
- data/lib/metanorma/document/components/inline/bcp14_element.rb +2 -0
- data/lib/metanorma/document/components/inline/biblio_tag_element.rb +2 -0
- data/lib/metanorma/document/components/inline/br_element.rb +2 -0
- data/lib/metanorma/document/components/inline/comma_element.rb +2 -0
- data/lib/metanorma/document/components/inline/concept_element.rb +2 -0
- data/lib/metanorma/document/components/inline/display_text_element.rb +2 -0
- data/lib/metanorma/document/components/inline/em_raw_element.rb +2 -0
- data/lib/metanorma/document/components/inline/enum_comma_element.rb +2 -0
- data/lib/metanorma/document/components/inline/eref_element.rb +2 -0
- data/lib/metanorma/document/components/inline/fmt_admitted_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_annotation_body_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_annotation_end_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_annotation_start_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_concept_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_definition_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_fn_body_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_fn_label_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_footnote_container_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_identifier_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_name_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_preferred_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_sourcecode_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_stem_element.rb +10 -0
- data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_title_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_xref_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fmt_xref_label_element.rb +4 -0
- data/lib/metanorma/document/components/inline/fn_element.rb +2 -0
- data/lib/metanorma/document/components/inline/link_element.rb +2 -0
- data/lib/metanorma/document/components/inline/localized_string_element.rb +2 -0
- data/lib/metanorma/document/components/inline/localized_strings_element.rb +2 -0
- data/lib/metanorma/document/components/inline/location_element.rb +24 -0
- data/lib/metanorma/document/components/inline/math_element.rb +2 -0
- data/lib/metanorma/document/components/inline/name_with_id_element.rb +4 -0
- data/lib/metanorma/document/components/inline/rendered_display.rb +23 -0
- data/lib/metanorma/document/components/inline/rendered_math_element.rb +21 -0
- data/lib/metanorma/document/components/inline/semantic_content.rb +51 -0
- data/lib/metanorma/document/components/inline/semantic_math_element.rb +20 -0
- data/lib/metanorma/document/components/inline/semx_child_element.rb +2 -0
- data/lib/metanorma/document/components/inline/semx_element.rb +13 -1
- data/lib/metanorma/document/components/inline/small_cap_element.rb +2 -0
- data/lib/metanorma/document/components/inline/span_element.rb +10 -15
- data/lib/metanorma/document/components/inline/stem_inline_element.rb +7 -1
- data/lib/metanorma/document/components/inline/strong_raw_element.rb +2 -0
- data/lib/metanorma/document/components/inline/sub_element.rb +2 -0
- data/lib/metanorma/document/components/inline/sup_element.rb +2 -0
- data/lib/metanorma/document/components/inline/tab_element.rb +2 -0
- data/lib/metanorma/document/components/inline/title_with_annotation_element.rb +4 -0
- data/lib/metanorma/document/components/inline/tt_element.rb +2 -0
- data/lib/metanorma/document/components/inline/variant_title_element.rb +2 -0
- data/lib/metanorma/document/components/inline/vocabulary.rb +184 -0
- data/lib/metanorma/document/components/inline/xref_element.rb +10 -0
- data/lib/metanorma/document/components/inline.rb +12 -0
- data/lib/metanorma/document/components/multi_paragraph/quote_author_element.rb +20 -0
- data/lib/metanorma/document/components/multi_paragraph/quote_block.rb +0 -29
- data/lib/metanorma/document/components/multi_paragraph.rb +2 -0
- data/lib/metanorma/document/components/reference_elements/source_element.rb +0 -31
- data/lib/metanorma/document/components/reference_elements/source_modification.rb +21 -0
- data/lib/metanorma/document/components/reference_elements/source_origin.rb +28 -0
- data/lib/metanorma/document/components/reference_elements.rb +2 -2
- data/lib/metanorma/document/components/tables/col_element.rb +18 -0
- data/lib/metanorma/document/components/tables/col_group_element.rb +18 -0
- data/lib/metanorma/document/components/tables/table_block.rb +0 -22
- data/lib/metanorma/document/components/tables/table_body_section.rb +16 -0
- data/lib/metanorma/document/components/tables/table_foot_section.rb +16 -0
- data/lib/metanorma/document/components/tables/table_head_section.rb +16 -0
- data/lib/metanorma/document/components/tables/table_section.rb +0 -25
- data/lib/metanorma/document/components/tables.rb +5 -3
- data/lib/metanorma/document/relaton/address.rb +0 -27
- data/lib/metanorma/document/relaton/bib_item_locality.rb +3 -12
- data/lib/metanorma/document/relaton/copyright_association.rb +0 -10
- data/lib/metanorma/document/relaton/copyright_owner.rb +16 -0
- data/lib/metanorma/document/relaton/edition.rb +4 -7
- data/lib/metanorma/document/relaton/formatted_address.rb +29 -0
- data/lib/metanorma/document/relaton/logo_element.rb +18 -0
- data/lib/metanorma/document/relaton/organization.rb +0 -14
- data/lib/metanorma/document/relaton/phone.rb +3 -9
- data/lib/metanorma/document/relaton/place_type.rb +0 -13
- data/lib/metanorma/document/relaton/price_type.rb +3 -9
- data/lib/metanorma/document/relaton/region_element.rb +18 -0
- data/lib/metanorma/document/relaton.rb +6 -1
- data/lib/metanorma/document/version.rb +1 -1
- data/lib/metanorma/document.rb +2 -0
- data/lib/metanorma/html/asset_pipeline.rb +1 -0
- data/lib/metanorma/html/base_renderer.rb +261 -1008
- data/lib/metanorma/html/bipm_renderer.rb +15 -144
- data/lib/metanorma/html/cc_renderer.rb +1 -45
- data/lib/metanorma/html/component/base.rb +2 -7
- data/lib/metanorma/html/component/index_section.rb +41 -43
- data/lib/metanorma/html/drops/admonition_drop.rb +1 -4
- data/lib/metanorma/html/drops/biblio_entry_drop.rb +50 -0
- data/lib/metanorma/html/drops/block_element_drop.rb +7 -2
- data/lib/metanorma/html/drops/example_drop.rb +1 -4
- data/lib/metanorma/html/drops/figure_drop.rb +31 -22
- data/lib/metanorma/html/drops/footnote_drop.rb +1 -1
- data/lib/metanorma/html/drops/formula_drop.rb +45 -16
- data/lib/metanorma/html/drops/note_drop.rb +9 -7
- data/lib/metanorma/html/drops/sourcecode_drop.rb +12 -5
- data/lib/metanorma/html/drops.rb +2 -0
- data/lib/metanorma/html/generator.rb +2 -2
- data/lib/metanorma/html/icc_renderer.rb +0 -39
- data/lib/metanorma/html/iec_renderer.rb +1 -44
- data/lib/metanorma/html/ieee_renderer.rb +1 -45
- data/lib/metanorma/html/ietf_renderer.rb +1 -48
- data/lib/metanorma/html/iho_renderer.rb +1 -46
- data/lib/metanorma/html/iso_renderer.rb +290 -343
- data/lib/metanorma/html/itu_renderer.rb +1 -45
- data/lib/metanorma/html/ogc_renderer.rb +15 -76
- data/lib/metanorma/html/oiml_renderer.rb +18 -38
- data/lib/metanorma/html/pdfa_renderer.rb +0 -39
- data/lib/metanorma/html/renderers/block_renderer.rb +372 -0
- data/lib/metanorma/html/renderers/inline_renderer.rb +655 -0
- data/lib/metanorma/html/renderers/pubid_renderer.rb +69 -0
- data/lib/metanorma/html/renderers/section_renderer.rb +193 -0
- data/lib/metanorma/html/renderers.rb +8 -0
- data/lib/metanorma/html/ribose_renderer.rb +1 -45
- data/lib/metanorma/html/standard_renderer.rb +341 -229
- data/lib/metanorma/html/templates/_audio.html.liquid +1 -0
- data/lib/metanorma/html/templates/_biblio_entry.html.liquid +1 -0
- data/lib/metanorma/html/templates/_bipm_cover.html.liquid +27 -0
- data/lib/metanorma/html/templates/_boilerplate.html.liquid +1 -0
- data/lib/metanorma/html/templates/_bookmark.html.liquid +1 -0
- data/lib/metanorma/html/templates/_br.html.liquid +1 -0
- data/lib/metanorma/html/templates/_definition_list.html.liquid +6 -0
- data/lib/metanorma/html/templates/_element.html.liquid +1 -0
- data/lib/metanorma/html/templates/_fn_marker.html.liquid +1 -0
- data/lib/metanorma/html/templates/_footnotes.html.liquid +6 -6
- data/lib/metanorma/html/templates/_formula.html.liquid +1 -1
- data/lib/metanorma/html/templates/_header.html.liquid +1 -1
- data/lib/metanorma/html/templates/_heading.html.liquid +1 -0
- data/lib/metanorma/html/templates/_image.html.liquid +1 -0
- data/lib/metanorma/html/templates/_inline_span.html.liquid +1 -0
- data/lib/metanorma/html/templates/_iso_cover.html.liquid +1 -1
- data/lib/metanorma/html/templates/_link.html.liquid +1 -0
- data/lib/metanorma/html/templates/_list.html.liquid +3 -0
- data/lib/metanorma/html/templates/_list_item.html.liquid +1 -0
- data/lib/metanorma/html/templates/_main_content.html.liquid +1 -0
- data/lib/metanorma/html/templates/_math_container.html.liquid +1 -0
- data/lib/metanorma/html/templates/_paragraph.html.liquid +1 -0
- data/lib/metanorma/html/templates/_prefatory_section.html.liquid +1 -0
- data/lib/metanorma/html/templates/_pubid_identifier.html.liquid +1 -0
- data/lib/metanorma/html/templates/_quote.html.liquid +4 -0
- data/lib/metanorma/html/templates/_ref_date.html.liquid +1 -0
- data/lib/metanorma/html/templates/_ref_title.html.liquid +1 -0
- data/lib/metanorma/html/templates/_section.html.liquid +5 -0
- data/lib/metanorma/html/templates/_standard_cover.html.liquid +31 -0
- data/lib/metanorma/html/templates/_standard_doc_title.html.liquid +3 -0
- data/lib/metanorma/html/templates/_stem_span.html.liquid +1 -0
- data/lib/metanorma/html/templates/_table.html.liquid +9 -0
- data/lib/metanorma/html/templates/_term_domain.html.liquid +1 -0
- data/lib/metanorma/html/templates/_term_example.html.liquid +1 -0
- data/lib/metanorma/html/templates/_term_note.html.liquid +1 -0
- data/lib/metanorma/html/templates/_term_number.html.liquid +1 -0
- data/lib/metanorma/html/templates/_term_source.html.liquid +1 -0
- data/lib/metanorma/html/templates/_term_text_note.html.liquid +1 -0
- data/lib/metanorma/html/templates/_video.html.liquid +1 -0
- data/lib/metanorma/html/templates/_wrapped_preface.html.liquid +1 -0
- data/lib/metanorma/html/templates/document.html.liquid +4 -0
- data/lib/metanorma/html/theme.rb +180 -107
- data/lib/metanorma/html.rb +13 -0
- data/lib/metanorma/ietf_document/metadata/ietf_bib_data_extension_type.rb +0 -25
- data/lib/metanorma/ietf_document/metadata/pi_settings.rb +32 -0
- data/lib/metanorma/ietf_document/metadata.rb +2 -0
- data/lib/metanorma/iso_document/metadata/doctype_element.rb +20 -0
- data/lib/metanorma/iso_document/metadata/iso_bib_data_extension_type.rb +0 -48
- data/lib/metanorma/iso_document/metadata/iso_bibliographic_item.rb +0 -72
- data/lib/metanorma/iso_document/metadata/iso_document_status.rb +0 -30
- data/lib/metanorma/iso_document/metadata/iso_localized_title.rb +4 -1
- data/lib/metanorma/iso_document/metadata/language_element.rb +18 -0
- data/lib/metanorma/iso_document/metadata/project_number.rb +22 -0
- data/lib/metanorma/iso_document/metadata/script_element.rb +18 -0
- data/lib/metanorma/iso_document/metadata/stage_element.rb +26 -0
- data/lib/metanorma/iso_document/metadata/stagename_element.rb +18 -0
- data/lib/metanorma/iso_document/metadata/structured_identifier.rb +0 -17
- data/lib/metanorma/iso_document/metadata/title_collection.rb +1 -0
- data/lib/metanorma/iso_document/metadata.rb +9 -4
- data/lib/metanorma/iso_document/terms/iso_term.rb +0 -13
- data/lib/metanorma/iso_document/terms/term_domain_element.rb +18 -0
- data/lib/metanorma/iso_document/terms.rb +1 -0
- data/lib/metanorma/itu_document/metadata/itu_bib_data_extension_type.rb +0 -37
- data/lib/metanorma/itu_document/metadata/meeting_date.rb +18 -0
- data/lib/metanorma/itu_document/metadata/meeting_element.rb +18 -0
- data/lib/metanorma/itu_document/metadata/study_period.rb +18 -0
- data/lib/metanorma/itu_document/metadata.rb +3 -4
- data/lib/metanorma/mirror/default_registry.rb +170 -0
- data/lib/metanorma/mirror/handler_registry.rb +60 -0
- data/lib/metanorma/mirror/handler_result.rb +37 -0
- data/lib/metanorma/mirror/handlers/admonition.rb +18 -0
- data/lib/metanorma/mirror/handlers/example.rb +21 -0
- data/lib/metanorma/mirror/handlers/figure.rb +53 -0
- data/lib/metanorma/mirror/handlers/formula.rb +35 -0
- data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +159 -0
- data/lib/metanorma/mirror/handlers/inline/text_extractor.rb +116 -0
- data/lib/metanorma/mirror/handlers/inline.rb +225 -0
- data/lib/metanorma/mirror/handlers/list.rb +110 -0
- data/lib/metanorma/mirror/handlers/note.rb +26 -0
- data/lib/metanorma/mirror/handlers/paragraph.rb +19 -0
- data/lib/metanorma/mirror/handlers/quote.rb +16 -0
- data/lib/metanorma/mirror/handlers/review.rb +19 -0
- data/lib/metanorma/mirror/handlers/section.rb +149 -0
- data/lib/metanorma/mirror/handlers/sourcecode.rb +27 -0
- data/lib/metanorma/mirror/handlers/structural.rb +68 -0
- data/lib/metanorma/mirror/handlers/table.rb +81 -0
- data/lib/metanorma/mirror/handlers/term.rb +100 -0
- data/lib/metanorma/mirror/handlers.rb +60 -0
- data/lib/metanorma/mirror/id_strategy/positional.rb +166 -0
- data/lib/metanorma/mirror/id_strategy/preserve.rb +12 -0
- data/lib/metanorma/mirror/id_strategy.rb +34 -0
- data/lib/metanorma/mirror/math_util.rb +53 -0
- data/lib/metanorma/mirror/metadata.rb +37 -0
- data/lib/metanorma/mirror/metanorma_to_mirror.rb +101 -0
- data/lib/metanorma/mirror/model/container.rb +47 -0
- data/lib/metanorma/mirror/model/factory.rb +52 -0
- data/lib/metanorma/mirror/model/guide.rb +25 -0
- data/lib/metanorma/mirror/model/leaf.rb +17 -0
- data/lib/metanorma/mirror/model/mark.rb +53 -0
- data/lib/metanorma/mirror/model/node.rb +47 -0
- data/lib/metanorma/mirror/model/soft_break.rb +33 -0
- data/lib/metanorma/mirror/model/text.rb +34 -0
- data/lib/metanorma/mirror/model.rb +16 -0
- data/lib/metanorma/mirror/output/builder.rb +39 -0
- data/lib/metanorma/mirror/output/formats/base_format.rb +90 -0
- data/lib/metanorma/mirror/output/formats/inline_format.rb +81 -0
- data/lib/metanorma/mirror/output/formats.rb +53 -0
- data/lib/metanorma/mirror/output/html_renderer.rb +95 -0
- data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +178 -0
- data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +61 -0
- data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +96 -0
- data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +64 -0
- data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +129 -0
- data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +80 -0
- data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +87 -0
- data/lib/metanorma/mirror/output/html_renderers.rb +76 -0
- data/lib/metanorma/mirror/output/pipeline.rb +92 -0
- data/lib/metanorma/mirror/output/pipeline_context.rb +22 -0
- data/lib/metanorma/mirror/output.rb +14 -0
- data/lib/metanorma/mirror/rewriter.rb +121 -0
- data/lib/metanorma/mirror/safe_attr.rb +16 -0
- data/lib/metanorma/mirror/serialization/json_serializer.rb +25 -0
- data/lib/metanorma/mirror/serialization/yaml_serializer.rb +20 -0
- data/lib/metanorma/mirror/serialization.rb +10 -0
- data/lib/metanorma/mirror/transformer.rb +22 -0
- data/lib/metanorma/mirror.rb +32 -0
- data/lib/metanorma/standard_document/annotation.rb +37 -0
- data/lib/metanorma/standard_document/block_attributes.rb +15 -2
- data/lib/metanorma/standard_document/blocks/block_source.rb +0 -23
- data/lib/metanorma/standard_document/blocks/source_origin_element.rb +23 -0
- data/lib/metanorma/standard_document/blocks.rb +1 -1
- data/lib/metanorma/standard_document/terms/term_expression.rb +0 -31
- data/lib/metanorma/standard_document/terms/term_name_element.rb +29 -0
- data/lib/metanorma/standard_document/terms.rb +1 -0
- data/script/render_both.rb +18 -0
- metadata +262 -13
- data/.github/workflows/rake.yml +0 -21
- data/.github/workflows/release.yml +0 -25
- data/.github/workflows/roundtrip-samples.yml +0 -132
- data/.gitignore +0 -19
- data/.rspec +0 -3
- data/.rubocop.yml +0 -22
- data/Gemfile +0 -28
- data/bin/console +0 -15
- data/bin/setup +0 -8
- 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,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
|