Module: Metanorma::Mirror::Handlers::Inline

Defined in:
lib/metanorma/mirror/handlers/inline.rb,
lib/metanorma/mirror/handlers/inline/catalog.rb,
lib/metanorma/mirror/handlers/inline/text_extractor.rb,
lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb

Defined Under Namespace

Modules: Catalog, RichHtmlRenderer, TextExtractor

Constant Summary collapse

SEMX_MARK_CONFIG =
{
  "xref" => { mark_type: "xref", target_attr: :target,
              fmt_attr: :fmt_xref },
  "eref" => { mark_type: "eref", target_attr: :target,
              fmt_attr: :fmt_xref },
  "link" => { mark_type: "link", target_attr: :target,
              fmt_attr: :fmt_link },
}.freeze
CROSSREF_MARKS =
%w[xref eref link].to_set.freeze

Class Method Summary collapse

Class Method Details

.block_in_inline?(element, context) ⇒ Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/metanorma/mirror/handlers/inline.rb', line 142

def self.block_in_inline?(element, context)
  context.registry.registered?(element.class)
end

.extract_element_text(element) ⇒ Object

Delegates to TextExtractor



23
24
25
# File 'lib/metanorma/mirror/handlers/inline.rb', line 23

def self.extract_element_text(element)
  TextExtractor.extract_element_text(element)
end

.extract_formatted_text(element) ⇒ Object



27
28
29
# File 'lib/metanorma/mirror/handlers/inline.rb', line 27

def self.extract_formatted_text(element)
  TextExtractor.extract_formatted_text(element)
end

.extract_inline(element, context:) ⇒ Object

Primary entry point: extract inline content from a mixed-content element. Iterates semantic children only — fmt-* rendered siblings are skipped to avoid emitting duplicate marks for the same logical content.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/metanorma/mirror/handlers/inline.rb', line 43

def self.extract_inline(element, context:)
  nodes = []
  iterator = Mirror.inline_content_iterator or
    raise Error, "no inline content iterator registered; " \
                 "the document seed must provide one"
  iterator.each(element) do |node|
    case node
    when String
      nodes << context.text_node(node) unless node.empty?
    else
      handle_inline_element(node, nodes, context:)
    end
  end
  filter_empty_crossrefs(nodes)
end

.extract_marked_text(mark_type, element) ⇒ Object



131
132
133
134
135
136
# File 'lib/metanorma/mirror/handlers/inline.rb', line 131

def self.extract_marked_text(mark_type, element)
  extractor = mark_text_extractors[mark_type]
  return extractor.call(element) if extractor

  TextExtractor.extract_element_text(element)
end

.extract_rich_html(element) ⇒ Object

Delegates to RichHtmlRenderer



36
37
38
# File 'lib/metanorma/mirror/handlers/inline.rb', line 36

def self.extract_rich_html(element)
  RichHtmlRenderer.extract(element)
end

.extract_text_from_model(node) ⇒ Object



31
32
33
# File 'lib/metanorma/mirror/handlers/inline.rb', line 31

def self.extract_text_from_model(node)
  TextExtractor.extract_text_from_model(node)
end

.filter_empty_crossrefs(nodes) ⇒ Object



146
147
148
149
150
151
# File 'lib/metanorma/mirror/handlers/inline.rb', line 146

def self.filter_empty_crossrefs(nodes)
  nodes.reject do |n|
    n.is_a?(Model::Text) && n.text.strip.empty? &&
      n.marks.any? { |m| CROSSREF_MARKS.include?(m.type) }
  end
end

.handle_block_in_inline(element, nodes, context:) ⇒ Object



108
109
110
111
# File 'lib/metanorma/mirror/handlers/inline.rb', line 108

def self.handle_block_in_inline(element, nodes, context:)
  result = context.registry.handle(element, context: context)
  result.append_to(nodes)
end

.handle_inline_element(element, nodes, context:) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/metanorma/mirror/handlers/inline.rb', line 59

def self.handle_inline_element(element, nodes, context:)
  if semx_element?(element)
    handle_semx(element, nodes, context:)
    return
  end

  if block_in_inline?(element, context)
    handle_block_in_inline(element, nodes, context:)
    return
  end

  mark_builder = Mirror.mark_builders.lookup(element)
  if mark_builder
    mark = mark_builder.call(element)
    text = extract_marked_text(mark.type, element)
    nodes << context.text_node(text, marks: [mark])
  else
    handle_structured_inline(element, nodes, context:)
  end
end

.handle_semx(element, nodes, context:) ⇒ Object



80
81
82
83
84
85
86
87
# File 'lib/metanorma/mirror/handlers/inline.rb', line 80

def self.handle_semx(element, nodes, context:)
  config = SEMX_MARK_CONFIG[element.element_attr]
  if config
    handle_semx_crossref(element, config, nodes, context:)
  else
    handle_structured_inline(element, nodes, context:)
  end
end

.handle_semx_crossref(element, config, nodes, context:) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/metanorma/mirror/handlers/inline.rb', line 89

def self.handle_semx_crossref(element, config, nodes, context:)
  fmt_children = SafeAttr.read(element, config[:fmt_attr])
  fmt_child = fmt_children&.first

  mark_attrs = {}
  if fmt_child && config[:target_attr]
    mark_attrs[config[:target_attr]] = SafeAttr.read(fmt_child, :target)
  end

  if element.element_attr == "eref" && fmt_child
    mark_attrs[:citeas] = extract_formatted_text(fmt_child)
  end

  text = extract_formatted_text(fmt_child || element)
  mark = Handlers.build_mark(config[:mark_type],
                             attrs: mark_attrs.compact)
  nodes << context.text_node(text, marks: [mark])
end

.handle_structured_inline(element, nodes, context:) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/metanorma/mirror/handlers/inline.rb', line 113

def self.handle_structured_inline(element, nodes, context:)
  if element.is_a?(Lutaml::Model::Serializable)
    inner_nodes = extract_inline(element, context:)
    nodes.concat(inner_nodes)
  else
    text = element.text
    nodes << context.text_node(text) if text.is_a?(String) && !text.strip.empty?
  end
end

.mark_text_extractors ⇒ Object



123
124
125
126
127
128
129
# File 'lib/metanorma/mirror/handlers/inline.rb', line 123

def self.mark_text_extractors
  @mark_text_extractors ||= {
    "footnote" => TextExtractor.method(:extract_fn_label),
    "stem" => TextExtractor.method(:extract_stem_text),
    "span" => TextExtractor.method(:extract_span_text),
  }
end

.semx_element?(element) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/metanorma/mirror/handlers/inline.rb', line 138

def self.semx_element?(element)
  !Mirror.semx_elements.lookup(element).nil?
end