Class: Coradoc::Html::Converters::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/coradoc/html/converters/base.rb

Overview

Base class for HTML output converters

This class handles ONLY CoreModel types for HTML output. Source models should be transformed to CoreModel before HTML conversion:

core_model = Coradoc::Transform::SourceToCoreModel.transform(source_model)
html = Coradoc::Html::Static.convert(core_model)

Class Method Summary collapse

Class Method Details

.build_class_attribute(class_name) ⇒ Object

Build class attribute



498
499
500
# File 'lib/coradoc/html/converters/base.rb', line 498

def build_class_attribute(class_name)
  class_name ? " class=\"#{escape_attribute(class_name)}\"" : ''
end

.build_element(tag, content = nil, attributes = {}) ⇒ Object

Build HTML element



530
531
532
# File 'lib/coradoc/html/converters/base.rb', line 530

def build_element(tag, content = nil, attributes = {})
  Coradoc::Html::Base.build_element(tag, content, attributes)
end

.build_html_attributes(id, title) ⇒ Object

Build HTML attributes string



490
491
492
493
494
495
# File 'lib/coradoc/html/converters/base.rb', line 490

def build_html_attributes(id, title)
  attrs = ''
  attrs += " id=\"#{escape_attribute(id)}\"" if id && !id.to_s.empty?
  attrs += " title=\"#{escape_attribute(title)}\"" if title && !title.to_s.empty?
  attrs
end

.convert_content_to_html(content, state = {}) ⇒ String

Convert content to HTML (CoreModel → HTML)

Parameters:

  • content (various)

    Content to convert

  • state (Hash) (defaults to: {})

    Conversion state

Returns:

  • (String)

    HTML string



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/coradoc/html/converters/base.rb', line 28

def convert_content_to_html(content, state = {})
  return '' if content.nil?

  # Handle primitives first
  case content
  when String
    return escape_html(content)
  when Array
    return content.map { |item| convert_content_to_html(item, state) }.join
  when Numeric
    return escape_html(content.to_s)
  when TrueClass, FalseClass
    return escape_html(content.to_s)
  end

  # Handle CoreModel types
  # NOTE: AnnotationBlock must be checked before Block since AnnotationBlock < Block.
  # We use is_a? directly instead of defined?() because CoreModel uses autoload.
  # The defined?() check doesn't trigger autoload, so it returns nil even when
  # the class is available via autoload. Using is_a? triggers the autoload.
  return render_core_inline_element(content, state) if content.is_a?(Coradoc::CoreModel::InlineElement)

  return render_core_annotation_block(content, state) if content.is_a?(Coradoc::CoreModel::AnnotationBlock)

  return render_core_block(content, state) if content.is_a?(Coradoc::CoreModel::Block)

  if content.is_a?(Coradoc::CoreModel::StructuralElement)
    # Use Section converter for sections
    return Coradoc::Html::Converters::Section.to_html(content, state) if content.section?

    return render_core_structural_element(content, state)
  end

  return render_core_list_block(content, state) if content.is_a?(Coradoc::CoreModel::ListBlock)

  return render_core_list_item(content, state) if content.is_a?(Coradoc::CoreModel::ListItem)

  return Coradoc::Html::Converters::Table.to_html(content, state) if content.is_a?(Coradoc::CoreModel::Table)

  return render_core_table_row(content, state) if content.is_a?(Coradoc::CoreModel::TableRow)

  return render_core_table_cell(content, state) if content.is_a?(Coradoc::CoreModel::TableCell)

  return render_core_term(content, state) if content.is_a?(Coradoc::CoreModel::Term)

  if content.is_a?(Coradoc::CoreModel::Image)
    return render_core_inline_image(content, state) if content.inline

    return render_core_block_image(content, state)

  end

  return render_core_footnote(content, state) if content.is_a?(Coradoc::CoreModel::Footnote)

  if content.is_a?(Coradoc::CoreModel::FootnoteReference)
    return render_core_footnote_reference(content,
                                          state)
  end

  return render_core_abbreviation(content, state) if content.is_a?(Coradoc::CoreModel::Abbreviation)

  return render_core_definition_list(content, state) if content.is_a?(Coradoc::CoreModel::DefinitionList)

  return render_core_definition_item(content, state) if content.is_a?(Coradoc::CoreModel::DefinitionItem)

  return render_core_toc(content, state) if content.is_a?(Coradoc::CoreModel::Toc)

  return render_core_toc_entry(content, state) if content.is_a?(Coradoc::CoreModel::TocEntry)

  return render_core_bibliography(content, state) if content.is_a?(Coradoc::CoreModel::Bibliography)

  if content.is_a?(Coradoc::CoreModel::BibliographyEntry)
    return render_core_bibliography_entry(content,
                                          state)
  end

  # Handle unknown types gracefully
  handle_unknown_content(content, state)
end

.convert_element_to_core(node, state = {}) ⇒ Coradoc::CoreModel::Base, ...

Convert HTML element to CoreModel

Parameters:

  • node (Nokogiri::XML::Node)

    Element node

  • state (Hash) (defaults to: {})

    Conversion state

Returns:

  • (Coradoc::CoreModel::Base, Array, nil)


577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/coradoc/html/converters/base.rb', line 577

def convert_element_to_core(node, state = {})
  # Delegate to Html::Input::Converters for HTML input
  # This maintains separation between input and output converters
  if defined?(Coradoc::Html::Input::Converters)
    converter = Coradoc::Html::Input::Converters.lookup(node.name)
    if converter
      result = converter.to_coradoc(node, state)
      # Transform to CoreModel if needed
      return transform_to_coremodel(result) if result
    end
  end

  # Fallback: treat children
  treat_children(node, state)
end

.convert_node_to_core(node, state = {}) ⇒ Coradoc::CoreModel::Base, ...

Convert HTML node to CoreModel

Parameters:

  • node (Nokogiri::XML::Node)

    Node to convert

  • state (Hash) (defaults to: {})

    Conversion state

Returns:

  • (Coradoc::CoreModel::Base, String, nil)


559
560
561
562
563
564
565
566
567
568
569
570
571
# File 'lib/coradoc/html/converters/base.rb', line 559

def convert_node_to_core(node, state = {})
  case node.type
  when Nokogiri::XML::Node::TEXT_NODE
    text = node.text
    return nil if text.strip.empty? && !state[:preserve_whitespace]

    text
  when Nokogiri::XML::Node::ELEMENT_NODE
    convert_element_to_core(node, state)
  when Nokogiri::XML::Node::COMMENT_NODE
    nil # Skip comments
  end
end

.escape_attribute(value) ⇒ Object

Escape HTML attribute values



523
524
525
526
527
# File 'lib/coradoc/html/converters/base.rb', line 523

def escape_attribute(value)
  return '' if value.nil?

  value.to_s.gsub(/&/, '&amp;').gsub(/"/, '&quot;').gsub(/</, '&lt;').gsub(/>/, '&gt;')
end

.escape_html(text) ⇒ Object

Escape HTML entities



518
519
520
# File 'lib/coradoc/html/converters/base.rb', line 518

def escape_html(text)
  Coradoc::Html::Base.escape_html(text)
end

.extract_model_attributes(model) ⇒ Hash

Extract attributes from a CoreModel

Parameters:

  • model (Coradoc::CoreModel::Base)

    Model to extract attributes from

Returns:

  • (Hash)

    Attributes hash



537
538
539
# File 'lib/coradoc/html/converters/base.rb', line 537

def extract_model_attributes(model)
  Coradoc::Html::Base.extract_attributes(model)
end

.extract_node_attributes(node) ⇒ Hash

Extract attributes from HTML node

Parameters:

  • node (Nokogiri::XML::Node)

    HTML node

Returns:

  • (Hash)

    Attributes hash



604
605
606
607
608
609
610
# File 'lib/coradoc/html/converters/base.rb', line 604

def extract_node_attributes(node)
  return {} unless node.is_a?(Nokogiri::XML::Node)

  node.attributes.each_with_object({}) do |(name, attr), hash|
    hash[name.to_sym] = attr.value
  end
end

.extract_text_fallback(content) ⇒ Object

Extract text from unknown model types as a fallback



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/coradoc/html/converters/base.rb', line 460

def extract_text_fallback(content)
  if content.is_a?(Coradoc::CoreModel::Base)
    if content.class.attributes.key?(:text) && content.text
      text_val = content.text
      return text_val if text_val.is_a?(String)

      return text_val.to_s
    end

    if content.class.attributes.key?(:content) && content.content
      content_val = content.content
      if content_val.is_a?(String)
        return content_val
      elsif content_val.is_a?(Array)
        return content_val.map { |item| convert_content_to_html(item, {}) }.join
      end
    end

    return content.href.to_s if content.class.attributes.key?(:href) && content.href
    return content.term.to_s if content.class.attributes.key?(:term) && content.term
    return content.id.to_s if content.class.attributes.key?(:id) && content.id
    return content.name.to_s if content.class.attributes.key?(:name) && content.name
  end

  ''
end

.find_converter_class_by_name(converter_name) ⇒ Object

Type-safe lookup of converter class by name



508
509
510
511
512
513
514
515
# File 'lib/coradoc/html/converters/base.rb', line 508

def find_converter_class_by_name(converter_name)
  klass = Coradoc::Html::Converters.const_get(converter_name, false)
  return klass if klass <= Coradoc::Html::Converters::Base

  nil
rescue NameError
  nil
end

.find_converter_for_model(model_class) ⇒ Object

Find converter for model class



503
504
505
# File 'lib/coradoc/html/converters/base.rb', line 503

def find_converter_for_model(model_class)
  Coradoc::Html::Base.find_converter(model_class)
end

.handle_unknown_content(content, _state = {}) ⇒ Object

Handle unknown content types



448
449
450
451
452
453
454
455
456
457
# File 'lib/coradoc/html/converters/base.rb', line 448

def handle_unknown_content(content, _state = {})
  if content.is_a?(Coradoc::CoreModel::Base)
    raise ArgumentError,
          "Unknown CoreModel type for HTML conversion: #{content.class}. " \
          'Expected a recognized CoreModel type.'
  end

  # Handle non-CoreModel types (strings from mixed content, etc.)
  escape_html(content.to_s)
end

.render_core_abbreviation(abbr, _state = {}) ⇒ Object

Render CoreModel abbreviation



374
375
376
377
378
# File 'lib/coradoc/html/converters/base.rb', line 374

def render_core_abbreviation(abbr, _state = {})
  term = abbr.term || ''
  definition = abbr.definition || ''
  %(<abbr title="#{escape_attribute(definition)}">#{escape_html(term)}</abbr>)
end

.render_core_annotation_block(block, state = {}) ⇒ Object

Render CoreModel annotation block (admonition)



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/coradoc/html/converters/base.rb', line 280

def render_core_annotation_block(block, state = {})
  attrs = build_html_attributes(block.id, block.title)
  type_class = block.annotation_type ? " #{escape_html(block.annotation_type)}" : ''
  label = block.annotation_label || block.annotation_type&.upcase

  html = "<div class=\"admonition#{type_class}\"#{attrs}>"
  html += "<div class=\"admonition-label\">#{escape_html(label)}</div>" if label
  renderable = block.renderable_content
  html += convert_content_to_html(renderable, state)
  html += '</div>'
  html
end

.render_core_bibliography(bib, state = {}) ⇒ Object



422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/coradoc/html/converters/base.rb', line 422

def render_core_bibliography(bib, state = {})
  attrs = %( class="bibliography")
  attrs += %( id="#{escape_attribute(bib.id)}") if bib.id

  title_html = (%(<h2 class="bibliography-title">#{escape_html(bib.title)}</h2>) if bib.title && !bib.title.to_s.empty?)

  entries_html = Array(bib.entries).map { |e| convert_content_to_html(e, state) }.join("\n")

  inner = ''
  inner += "#{title_html}\n" if title_html
  inner += "<div class=\"bibliography-entries\">\n#{entries_html}\n</div>" unless entries_html.empty?

  "<section#{attrs}>\n#{inner}\n</section>"
end

.render_core_bibliography_entry(entry, _state = {}) ⇒ Object



437
438
439
440
441
442
443
444
445
# File 'lib/coradoc/html/converters/base.rb', line 437

def render_core_bibliography_entry(entry, _state = {})
  entry_id = entry.anchor_name || entry.document_id
  anchor_html = entry_id ? %(<a id="#{escape_attribute(entry_id)}" class="bibliography-anchor"></a>) : ''
  label = entry.document_id || ''
  ref_text = entry.ref_text || ''
  label_html = label.empty? ? '' : %(<span class="bibliography-label">#{escape_html(label)}</span> )

  "<div class=\"bibliography-entry\">#{anchor_html}#{label_html}#{escape_html(ref_text)}</div>"
end

.render_core_block(block, state = {}) ⇒ Object

Render CoreModel block



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/coradoc/html/converters/base.rb', line 166

def render_core_block(block, state = {})
  attrs = build_html_attributes(block.id, block.title)

  # Get renderable content (children if present, otherwise content)
  renderable = block.renderable_content

  semantic = resolve_block_semantic_type(block)

  case semantic
  when :paragraph
    content = convert_content_to_html(renderable, state)
    return "<p#{attrs}>#{content}</p>" if content && !content.empty?

    ''
  when :source_code
    lang = block.language || block.&.dig(:language)
    lang_attr = lang ? " data-lang=\"#{escape_attribute(lang)}\"" : ''
    "<pre#{attrs}><code#{lang_attr}>#{escape_html(block.flat_text)}</code></pre>"
  when :quote, :verse
    "<blockquote#{attrs}>#{convert_content_to_html(renderable, state)}</blockquote>"
  when :example
    "<div class=\"example\"#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
  when :sidebar
    "<aside class=\"sidebar\"#{attrs}>#{convert_content_to_html(renderable, state)}</aside>"
  when :literal
    "<pre class=\"literal\"#{attrs}>#{escape_html(block.flat_text)}</pre>"
  when :pass
    block.flat_text
  when :listing
    "<pre#{attrs}>#{escape_html(block.flat_text)}</pre>"
  when :open
    "<div#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
  when :verse
    "<blockquote#{attrs}>#{convert_content_to_html(renderable, state)}</blockquote>"
  when :comment, :reviewer
    ''
  when :horizontal_rule
    "<hr#{attrs}>"
  else
    "<div#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
  end
end

.render_core_block_image(image, _state = {}) ⇒ Object

Render CoreModel block image



336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/coradoc/html/converters/base.rb', line 336

def render_core_block_image(image, _state = {})
  attrs = build_html_attributes(image.id, nil)
  img_attrs = "src=\"#{escape_attribute(image.src)}\""
  img_attrs += " alt=\"#{escape_attribute(image.alt)}\"" if image.alt
  img_attrs += " width=\"#{escape_attribute(image.width)}\"" if image.width
  img_attrs += " height=\"#{escape_attribute(image.height)}\"" if image.height

  html = "<figure#{attrs}>"
  html += %(<img #{img_attrs}>)
  html += "<figcaption>#{escape_html(image.caption)}</figcaption>" if image.caption
  html += '</figure>'
  html
end

.render_core_definition_item(item, state = {}) ⇒ Object

Render CoreModel definition item



388
389
390
391
392
# File 'lib/coradoc/html/converters/base.rb', line 388

def render_core_definition_item(item, state = {})
  term_html = convert_content_to_html(item.term, state)
  definitions_html = (item.definitions || []).map { |d| "<dd>#{convert_content_to_html(d, state)}</dd>" }.join
  "<dt>#{term_html}</dt>#{definitions_html}"
end

.render_core_definition_list(dl, state = {}) ⇒ Object

Render CoreModel definition list



381
382
383
384
385
# File 'lib/coradoc/html/converters/base.rb', line 381

def render_core_definition_list(dl, state = {})
  attrs = build_html_attributes(dl.id, dl.title)
  items_html = (dl.items || []).map { |i| convert_content_to_html(i, state) }.join
  "<dl#{attrs}>#{items_html}</dl>"
end

.render_core_footnote(footnote, state = {}) ⇒ Object

Render CoreModel footnote



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/coradoc/html/converters/base.rb', line 351

def render_core_footnote(footnote, state = {})
  footnote_id = footnote.id || ''
  content = footnote.content || footnote.inline_content

  if footnote_id.empty?
    # Anonymous footnote
    text = content.is_a?(Array) ? content.join : content.to_s
    title_text = text[0..50]
    %(<sup class="footnote" title="#{escape_attribute(title_text)}">#{convert_content_to_html(content,
                                                                                              state)}</sup>)
  else
    # Named footnote reference
    %(<sup class="footnote"><a href="#fn-#{escape_attribute(footnote_id)}" id="fnref-#{escape_attribute(footnote_id)}">#{escape_html(footnote_id)}</a></sup>)
  end
end

.render_core_footnote_reference(ref, _state = {}) ⇒ Object

Render CoreModel footnote reference



368
369
370
371
# File 'lib/coradoc/html/converters/base.rb', line 368

def render_core_footnote_reference(ref, _state = {})
  footnote_id = ref.id || ''
  %(<sup class="footnote"><a href="#fn-#{escape_attribute(footnote_id)}">[#{escape_html(footnote_id)}]</a></sup>)
end

.render_core_inline_element(element, state = {}) ⇒ Object

Render CoreModel inline element



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/coradoc/html/converters/base.rb', line 111

def render_core_inline_element(element, state = {})
  case element.format_type
  when 'bold'
    "<strong>#{convert_content_to_html(element.content, state)}</strong>"
  when 'italic'
    "<em>#{convert_content_to_html(element.content, state)}</em>"
  when 'monospace'
    "<code>#{convert_content_to_html(element.content, state)}</code>"
  when 'superscript'
    "<sup>#{convert_content_to_html(element.content, state)}</sup>"
  when 'subscript'
    "<sub>#{convert_content_to_html(element.content, state)}</sub>"
  when 'underline'
    "<u>#{convert_content_to_html(element.content, state)}</u>"
  when 'strikethrough'
    "<del>#{convert_content_to_html(element.content, state)}</del>"
  when 'highlight'
    "<mark>#{convert_content_to_html(element.content, state)}</mark>"
  when 'link'
    href = element.target || element.&.dig(:href) || '#'
    "<a href=\"#{escape_attribute(href)}\">#{convert_content_to_html(element.content, state)}</a>"
  when 'xref'
    href = element.target || element.&.dig(:href) || '#'
    "<a href=\"##{escape_attribute(href)}\">#{convert_content_to_html(element.content, state)}</a>"
  when 'footnote'
    footnote_id = element.target || element.&.dig(:id) || ''
    "<sup class=\"footnote\" id=\"fn-#{escape_attribute(footnote_id)}\">#{convert_content_to_html(
      element.content, state
    )}</sup>"
  when 'stem'
    "<code class=\"stem\">#{escape_html(element.content)}</code>"
  when 'term'
    # Term reference: term:[text] or term:[text,display]
    %(<span class="term" data-term-ref="#{escape_attribute(element.content)}">#{escape_html(element.content)}</span>)
  when 'break'
    break_type = element.&.dig(:break_type) || 'thematic'
    break_type == 'thematic' ? '<hr>' : '<br>'
  when 'quotation'
    "<q>#{convert_content_to_html(element.content, state)}</q>"
  when 'small'
    "<small>#{convert_content_to_html(element.content, state)}</small>"
  when 'span'
    render_core_span(element, state)
  else
    convert_content_to_html(element.content, state)
  end
end

.render_core_inline_image(image, _state = {}) ⇒ Object

Render CoreModel inline image



326
327
328
329
330
331
332
333
# File 'lib/coradoc/html/converters/base.rb', line 326

def render_core_inline_image(image, _state = {})
  attrs = "src=\"#{escape_attribute(image.src)}\""
  attrs += " alt=\"#{escape_attribute(image.alt)}\"" if image.alt
  attrs += " width=\"#{escape_attribute(image.width)}\"" if image.width
  attrs += " height=\"#{escape_attribute(image.height)}\"" if image.height

  %(<img #{attrs}>)
end

.render_core_list_block(list, state = {}) ⇒ Object

Render CoreModel list block



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/coradoc/html/converters/base.rb', line 251

def render_core_list_block(list, state = {})
  attrs = build_html_attributes(list.id, list.title)

  items_html = (list.items || []).map { |i| convert_content_to_html(i, state) }.join
  case list.marker_type
  when 'unordered'
    "<ul#{attrs}>#{items_html}</ul>"
  when 'ordered'
    "<ol#{attrs}>#{items_html}</ol>"
  when 'definition'
    "<dl#{attrs}>#{items_html}</dl>"
  else
    "<ul#{attrs}>#{items_html}</ul>"
  end
end

.render_core_list_item(item, state = {}) ⇒ Object

Render CoreModel list item



268
269
270
271
272
273
274
275
276
277
# File 'lib/coradoc/html/converters/base.rb', line 268

def render_core_list_item(item, state = {})
  # Use renderable_content to get children if present
  renderable = item.renderable_content
  content = convert_content_to_html(renderable, state)

  # Handle nested list
  content += convert_content_to_html(item.nested_list, state) if item.nested_list

  "<li>#{content}</li>"
end

.render_core_span(element, state = {}) ⇒ Object

Render CoreModel span



160
161
162
163
# File 'lib/coradoc/html/converters/base.rb', line 160

def render_core_span(element, state = {})
  attrs = build_class_attribute(element.&.dig(:class))
  "<span#{attrs}>#{convert_content_to_html(element.content, state)}</span>"
end

.render_core_structural_element(element, state = {}) ⇒ Object

Render CoreModel structural element



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/coradoc/html/converters/base.rb', line 231

def render_core_structural_element(element, state = {})
  attrs = build_html_attributes(element.id, nil)

  children_html = (element.children || []).map { |c| convert_content_to_html(c, state) }.join
  case element.element_type
  when 'document'
    "<article#{attrs}>#{children_html}</article>"
  when 'header'
    "<header#{attrs}>#{children_html}</header>"
  when 'section'
    level = element.heading_level
    level = [level, 6].min
    title_html = element.title ? "<h#{level}>#{escape_html(element.title)}</h#{level}>" : ''
    "<section#{attrs}>#{title_html}#{children_html}</section>"
  else
    "<div#{attrs}>#{children_html}</div>"
  end
end

.render_core_table_cell(cell, state = {}) ⇒ Object

Render CoreModel table cell



302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/coradoc/html/converters/base.rb', line 302

def render_core_table_cell(cell, state = {})
  tag = cell.header ? 'th' : 'td'
  attrs = ''
  attrs += " colspan=\"#{cell.colspan}\"" if cell.colspan
  attrs += " rowspan=\"#{cell.rowspan}\"" if cell.rowspan
  attrs += " style=\"text-align: #{escape_html(cell.alignment)}\"" if cell.alignment

  # Use renderable_content to get children if present, otherwise content
  renderable = cell.renderable_content
  content = convert_content_to_html(renderable, state)
  "<#{tag}#{attrs}>#{content}</#{tag}>"
end

.render_core_table_row(row, state = {}) ⇒ Object

Render CoreModel table row



294
295
296
297
298
299
# File 'lib/coradoc/html/converters/base.rb', line 294

def render_core_table_row(row, state = {})
  cells = row.cells || row.columns || []
  cells_html = cells.map { |c| convert_content_to_html(c, state) }.join
  tag = row.header ? 'thead' : 'tr'
  "<#{tag}>#{cells_html}</#{tag}>"
end

.render_core_term(term, _state = {}) ⇒ Object

Render CoreModel term



316
317
318
319
320
321
322
323
# File 'lib/coradoc/html/converters/base.rb', line 316

def render_core_term(term, _state = {})
  term_text = term.text || ''
  term_type = term.term_type || term.type || 'term'
  display_text = term.render_text&.strip&.empty? ? false : term.render_text
  display_text ||= term_text

  %(<span class="term term-#{escape_attribute(term_type)}" data-term-ref="#{escape_attribute(term_text)}">#{escape_html(display_text)}</span>)
end

.render_core_toc(toc, state = {}) ⇒ Object

Render CoreModel TOC



395
396
397
398
399
400
# File 'lib/coradoc/html/converters/base.rb', line 395

def render_core_toc(toc, state = {})
  attrs = build_html_attributes(nil, nil)
  attrs += ' class="toc"'
  entries_html = (toc.entries || []).map { |e| convert_content_to_html(e, state) }.join
  "<nav#{attrs}><h2>Table of Contents</h2><ul>#{entries_html}</ul></nav>"
end

.render_core_toc_entry(entry, state = {}) ⇒ Object

Render CoreModel TOC entry



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/coradoc/html/converters/base.rb', line 403

def render_core_toc_entry(entry, state = {})
  id = entry.id || ''
  title = entry.title || ''
  entry.level || 1
  number = entry.number
  display_title = number ? "#{number}. #{title}" : title

  item_html = if id.empty?
                escape_html(display_title)
              else
                %(<a href="##{escape_attribute(id)}">#{escape_html(display_title)}</a>)
              end

  children_html = (entry.children || []).map { |c| convert_content_to_html(c, state) }.join
  children_html = "<ul>#{children_html}</ul>" unless children_html.empty?

  "<li>#{item_html}#{children_html}</li>"
end

.resolve_block_semantic_type(block) ⇒ Object

Resolve the semantic type from a block via polymorphic dispatch. Block#resolve_semantic_type handles class-level semantic_type →block_semantic_type attribute → element_type → delimiter fallback.



212
213
214
215
# File 'lib/coradoc/html/converters/base.rb', line 212

def resolve_block_semantic_type(block)
  block.resolve_semantic_type ||
    resolve_format_specific_semantic(block)
end

.resolve_format_specific_semantic(block) ⇒ Object

Format-specific semantic mappings not covered by the core model



218
219
220
221
222
223
224
225
226
227
228
# File 'lib/coradoc/html/converters/base.rb', line 218

def resolve_format_specific_semantic(block)
  delim = block.delimiter_type
  return nil unless delim && !delim.empty?

  case delim
  when "'''", '---', '___', '***' then :horizontal_rule
  when '[verse]' then :verse
  when 'comment' then :comment
  when 'paragraph' then :paragraph
  end
end

.to_html(model, state = {}) ⇒ String

Convert CoreModel to HTML

Parameters:

  • model (Coradoc::CoreModel::Base)

    CoreModel to convert

  • state (Hash) (defaults to: {})

    Conversion state

Returns:

  • (String)

    HTML string

Raises:

  • (NotImplementedError)


20
21
22
# File 'lib/coradoc/html/converters/base.rb', line 20

def to_html(model, state = {})
  raise NotImplementedError, "#{self}.to_html must be implemented"
end

.transform_to_coremodel(model) ⇒ Coradoc::CoreModel::Base, Object

Transform model to CoreModel

Parameters:

  • model (Object)

    Model to transform

Returns:

  • (Coradoc::CoreModel::Base, Object)


596
597
598
599
# File 'lib/coradoc/html/converters/base.rb', line 596

def transform_to_coremodel(model)
  # Already a CoreModel type - return as-is
  model
end

.treat_children(node, state = {}) ⇒ Array

Process children of an HTML node

Parameters:

  • node (Nokogiri::XML::Node)

    Parent node

  • state (Hash) (defaults to: {})

    Conversion state

Returns:

  • (Array)

    Array of converted content



547
548
549
550
551
552
553
# File 'lib/coradoc/html/converters/base.rb', line 547

def treat_children(node, state = {})
  return [] unless node&.children

  node.children.flat_map do |child|
    convert_node_to_core(child, state)
  end.compact
end