coradoc-html 1.1.7 → 1.1.13
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/lib/coradoc/html/config.rb +36 -12
- data/lib/coradoc/html/converter_base.rb +26 -68
- data/lib/coradoc/html/drop/annotation_drop.rb +31 -0
- data/lib/coradoc/html/drop/base.rb +77 -0
- data/lib/coradoc/html/drop/bibliography_drop.rb +15 -0
- data/lib/coradoc/html/drop/bibliography_entry_drop.rb +24 -0
- data/lib/coradoc/html/drop/block_drop.rb +69 -0
- data/lib/coradoc/html/drop/definition_item_drop.rb +36 -0
- data/lib/coradoc/html/drop/definition_list_drop.rb +15 -0
- data/lib/coradoc/html/drop/document_drop.rb +52 -0
- data/lib/coradoc/html/drop/drop_factory.rb +73 -0
- data/lib/coradoc/html/drop/footnote_drop.rb +24 -0
- data/lib/coradoc/html/drop/image_drop.rb +35 -0
- data/lib/coradoc/html/drop/inline_element_drop.rb +64 -0
- data/lib/coradoc/html/drop/list_block_drop.rb +23 -0
- data/lib/coradoc/html/drop/list_item_drop.rb +20 -0
- data/lib/coradoc/html/drop/table_cell_drop.rb +35 -0
- data/lib/coradoc/html/drop/table_drop.rb +15 -0
- data/lib/coradoc/html/drop/table_row_drop.rb +23 -0
- data/lib/coradoc/html/drop/term_drop.rb +24 -0
- data/lib/coradoc/html/drop/text_content_drop.rb +15 -0
- data/lib/coradoc/html/drop/toc_drop.rb +15 -0
- data/lib/coradoc/html/drop/toc_entry_drop.rb +32 -0
- data/lib/coradoc/html/escape.rb +29 -0
- data/lib/coradoc/html/input/cleaner.rb +4 -33
- data/lib/coradoc/html/input/config.rb +4 -3
- data/lib/coradoc/html/input/converters/a.rb +8 -19
- data/lib/coradoc/html/input/converters/aside.rb +4 -5
- data/lib/coradoc/html/input/converters/audio.rb +8 -35
- data/lib/coradoc/html/input/converters/base.rb +29 -27
- data/lib/coradoc/html/input/converters/blockquote.rb +4 -2
- data/lib/coradoc/html/input/converters/br.rb +4 -4
- data/lib/coradoc/html/input/converters/bypass.rb +68 -67
- data/lib/coradoc/html/input/converters/code.rb +7 -5
- data/lib/coradoc/html/input/converters/div.rb +4 -4
- data/lib/coradoc/html/input/converters/dl.rb +3 -25
- data/lib/coradoc/html/input/converters/drop.rb +13 -13
- data/lib/coradoc/html/input/converters/em.rb +5 -3
- data/lib/coradoc/html/input/converters/figure.rb +3 -26
- data/lib/coradoc/html/input/converters/h.rb +9 -11
- data/lib/coradoc/html/input/converters/head.rb +5 -4
- data/lib/coradoc/html/input/converters/hr.rb +4 -5
- data/lib/coradoc/html/input/converters/img.rb +4 -9
- data/lib/coradoc/html/input/converters/li.rb +3 -1
- data/lib/coradoc/html/input/converters/mark.rb +3 -1
- data/lib/coradoc/html/input/converters/markup.rb +4 -8
- data/lib/coradoc/html/input/converters/math.rb +7 -14
- data/lib/coradoc/html/input/converters/media_base.rb +50 -0
- data/lib/coradoc/html/input/converters/ol.rb +6 -8
- data/lib/coradoc/html/input/converters/p.rb +43 -34
- data/lib/coradoc/html/input/converters/pass_through.rb +2 -4
- data/lib/coradoc/html/input/converters/positional_formatting.rb +37 -0
- data/lib/coradoc/html/input/converters/pre.rb +3 -3
- data/lib/coradoc/html/input/converters/q.rb +6 -3
- data/lib/coradoc/html/input/converters/strong.rb +4 -2
- data/lib/coradoc/html/input/converters/sub.rb +7 -23
- data/lib/coradoc/html/input/converters/sup.rb +7 -23
- data/lib/coradoc/html/input/converters/table.rb +3 -1
- data/lib/coradoc/html/input/converters/td.rb +4 -30
- data/lib/coradoc/html/input/converters/text.rb +4 -3
- data/lib/coradoc/html/input/converters/tr.rb +3 -2
- data/lib/coradoc/html/input/converters/video.rb +14 -36
- data/lib/coradoc/html/input/converters.rb +17 -35
- data/lib/coradoc/html/input/html_converter.rb +2 -74
- data/lib/coradoc/html/input/plugin.rb +8 -50
- data/lib/coradoc/html/input/plugins/plateau.rb +4 -19
- data/lib/coradoc/html/input/postprocessor.rb +3 -9
- data/lib/coradoc/html/input.rb +26 -8
- data/lib/coradoc/html/layout_renderer.rb +163 -0
- data/lib/coradoc/html/output.rb +6 -12
- data/lib/coradoc/html/renderer.rb +84 -350
- data/lib/coradoc/html/section_numberable.rb +9 -0
- data/lib/coradoc/html/spa.rb +29 -270
- data/lib/coradoc/html/static.rb +29 -238
- data/lib/coradoc/html/template_caching.rb +31 -0
- data/lib/coradoc/html/template_config.rb +11 -70
- data/lib/coradoc/html/template_helpers.rb +39 -31
- data/lib/coradoc/html/template_locator.rb +17 -11
- data/lib/coradoc/html/theme.rb +1 -7
- data/lib/coradoc/html/title_text.rb +57 -0
- data/lib/coradoc/html/toc_builder.rb +105 -0
- data/lib/coradoc/html/toc_serializer.rb +33 -0
- data/lib/coradoc/html/transform/from_core_model.rb +13 -12
- data/lib/coradoc/html/transform/to_core_model.rb +10 -12
- data/lib/coradoc/html/version.rb +1 -1
- data/lib/coradoc/html.rb +43 -88
- metadata +37 -70
- data/lib/coradoc/html/base.rb +0 -157
- data/lib/coradoc/html/converters/admonition.rb +0 -180
- data/lib/coradoc/html/converters/attribute.rb +0 -68
- data/lib/coradoc/html/converters/attribute_reference.rb +0 -60
- data/lib/coradoc/html/converters/audio.rb +0 -165
- data/lib/coradoc/html/converters/base.rb +0 -615
- data/lib/coradoc/html/converters/bibliography.rb +0 -82
- data/lib/coradoc/html/converters/bibliography_entry.rb +0 -108
- data/lib/coradoc/html/converters/block_image.rb +0 -72
- data/lib/coradoc/html/converters/bold.rb +0 -34
- data/lib/coradoc/html/converters/break.rb +0 -32
- data/lib/coradoc/html/converters/comment_block.rb +0 -42
- data/lib/coradoc/html/converters/comment_line.rb +0 -54
- data/lib/coradoc/html/converters/cross_reference.rb +0 -59
- data/lib/coradoc/html/converters/document.rb +0 -108
- data/lib/coradoc/html/converters/example.rb +0 -114
- data/lib/coradoc/html/converters/highlight.rb +0 -34
- data/lib/coradoc/html/converters/include.rb +0 -68
- data/lib/coradoc/html/converters/inline_image.rb +0 -41
- data/lib/coradoc/html/converters/italic.rb +0 -34
- data/lib/coradoc/html/converters/line_break.rb +0 -31
- data/lib/coradoc/html/converters/link.rb +0 -46
- data/lib/coradoc/html/converters/list_item.rb +0 -75
- data/lib/coradoc/html/converters/listing.rb +0 -99
- data/lib/coradoc/html/converters/literal.rb +0 -102
- data/lib/coradoc/html/converters/monospace.rb +0 -34
- data/lib/coradoc/html/converters/open.rb +0 -78
- data/lib/coradoc/html/converters/ordered.rb +0 -53
- data/lib/coradoc/html/converters/paragraph.rb +0 -46
- data/lib/coradoc/html/converters/quote.rb +0 -113
- data/lib/coradoc/html/converters/reviewer_comment.rb +0 -74
- data/lib/coradoc/html/converters/reviewer_note.rb +0 -134
- data/lib/coradoc/html/converters/section.rb +0 -90
- data/lib/coradoc/html/converters/sidebar.rb +0 -113
- data/lib/coradoc/html/converters/source.rb +0 -137
- data/lib/coradoc/html/converters/source_code.rb +0 -16
- data/lib/coradoc/html/converters/span.rb +0 -61
- data/lib/coradoc/html/converters/strikethrough.rb +0 -34
- data/lib/coradoc/html/converters/subscript.rb +0 -34
- data/lib/coradoc/html/converters/superscript.rb +0 -34
- data/lib/coradoc/html/converters/table.rb +0 -85
- data/lib/coradoc/html/converters/table_cell.rb +0 -203
- data/lib/coradoc/html/converters/table_row.rb +0 -45
- data/lib/coradoc/html/converters/template_html_converter.rb +0 -105
- data/lib/coradoc/html/converters/term.rb +0 -58
- data/lib/coradoc/html/converters/text_element.rb +0 -44
- data/lib/coradoc/html/converters/underline.rb +0 -34
- data/lib/coradoc/html/converters/unordered.rb +0 -47
- data/lib/coradoc/html/converters/verse.rb +0 -105
- data/lib/coradoc/html/converters/video.rb +0 -179
- data/lib/coradoc/html/element_mapping.rb +0 -210
- data/lib/coradoc/html/entity.rb +0 -137
- data/lib/coradoc/html/input/converters/ignore.rb +0 -22
- data/lib/coradoc/html/input/converters/th.rb +0 -20
- data/lib/coradoc/html/theme/base.rb +0 -231
- data/lib/coradoc/html/theme/classic_renderer.rb +0 -390
- data/lib/coradoc/html/theme/modern/components/ui_components.rb +0 -344
- data/lib/coradoc/html/theme/modern/css_generator.rb +0 -311
- data/lib/coradoc/html/theme/modern/javascript_generator.rb +0 -314
- data/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +0 -382
- data/lib/coradoc/html/theme/modern/tailwind_config_builder.rb +0 -164
- data/lib/coradoc/html/theme/modern/vue_template_generator.rb +0 -374
- data/lib/coradoc/html/theme/modern_renderer.rb +0 -250
- data/lib/coradoc/html/theme/registry.rb +0 -153
|
@@ -1,409 +1,143 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'liquid'
|
|
4
|
-
|
|
5
|
-
require_relative 'template_helpers'
|
|
4
|
+
require 'nokogiri'
|
|
6
5
|
|
|
7
6
|
module Coradoc
|
|
8
7
|
module Html
|
|
9
|
-
# Unified template renderer using Liquid templates for CoreModel types
|
|
10
|
-
#
|
|
11
|
-
# This class provides a template-based rendering system where:
|
|
12
|
-
# - Users provide template directories (checked in order, first wins)
|
|
13
|
-
# - System falls back to default templates if enabled
|
|
14
|
-
# - CoreModel objects are automatically converted to Liquid drops via to_liquid
|
|
15
|
-
#
|
|
16
|
-
# @example Basic usage with default templates only
|
|
17
|
-
# renderer = Coradoc::Html::Renderer.new
|
|
18
|
-
# html = renderer.render(bibliography_element)
|
|
19
|
-
#
|
|
20
|
-
# @example With custom template directory (falls back to defaults)
|
|
21
|
-
# renderer = Coradoc::Html::Renderer.new(
|
|
22
|
-
# template_dirs: ["./my_templates"]
|
|
23
|
-
# )
|
|
24
|
-
# html = renderer.render(bibliography_element)
|
|
25
|
-
#
|
|
26
|
-
# @example Multiple template dirs with priority (first wins)
|
|
27
|
-
# renderer = Coradoc::Html::Renderer.new(
|
|
28
|
-
# template_dirs: ["./project_templates", "./shared_templates"],
|
|
29
|
-
# include_default_templates: true
|
|
30
|
-
# )
|
|
31
|
-
#
|
|
32
|
-
# @example Custom templates only, no defaults
|
|
33
|
-
# renderer = Coradoc::Html::Renderer.new(
|
|
34
|
-
# template_dirs: ["./my_templates"],
|
|
35
|
-
# include_default_templates: false
|
|
36
|
-
# )
|
|
37
|
-
#
|
|
38
8
|
class Renderer
|
|
39
|
-
|
|
40
|
-
TEMPLATE_TYPE_MAP = {
|
|
41
|
-
'Coradoc::CoreModel::Bibliography' => 'bibliography',
|
|
42
|
-
'Coradoc::CoreModel::BibliographyEntry' => 'bibliography_entry',
|
|
43
|
-
'Coradoc::CoreModel::StructuralElement' => 'structural_element',
|
|
44
|
-
'Coradoc::CoreModel::Block' => 'block',
|
|
45
|
-
'Coradoc::CoreModel::SourceBlock' => 'source_block',
|
|
46
|
-
'Coradoc::CoreModel::ExampleBlock' => 'example_block',
|
|
47
|
-
'Coradoc::CoreModel::QuoteBlock' => 'quote_block',
|
|
48
|
-
'Coradoc::CoreModel::SidebarBlock' => 'sidebar_block',
|
|
49
|
-
'Coradoc::CoreModel::LiteralBlock' => 'literal_block',
|
|
50
|
-
'Coradoc::CoreModel::PassBlock' => 'pass_block',
|
|
51
|
-
'Coradoc::CoreModel::ListingBlock' => 'listing_block',
|
|
52
|
-
'Coradoc::CoreModel::OpenBlock' => 'open_block',
|
|
53
|
-
'Coradoc::CoreModel::VerseBlock' => 'verse_block',
|
|
54
|
-
'Coradoc::CoreModel::ReviewerBlock' => 'reviewer_block',
|
|
55
|
-
'Coradoc::CoreModel::AnnotationBlock' => 'annotation_block',
|
|
56
|
-
'Coradoc::CoreModel::ListBlock' => 'list_block',
|
|
57
|
-
'Coradoc::CoreModel::ListItem' => 'list_item',
|
|
58
|
-
'Coradoc::CoreModel::Table' => 'table',
|
|
59
|
-
'Coradoc::CoreModel::TableRow' => 'table_row',
|
|
60
|
-
'Coradoc::CoreModel::TableCell' => 'table_cell',
|
|
61
|
-
'Coradoc::CoreModel::Image' => 'image',
|
|
62
|
-
'Coradoc::CoreModel::InlineElement' => 'inline_element',
|
|
63
|
-
'Coradoc::CoreModel::Paragraph' => 'paragraph',
|
|
64
|
-
'Coradoc::CoreModel::Term' => 'term',
|
|
65
|
-
'Coradoc::CoreModel::Footnote' => 'footnote',
|
|
66
|
-
'Coradoc::CoreModel::FootnoteReference' => 'footnote_reference',
|
|
67
|
-
'Coradoc::CoreModel::Toc' => 'toc',
|
|
68
|
-
'Coradoc::CoreModel::TocEntry' => 'toc_entry',
|
|
69
|
-
'Coradoc::CoreModel::DefinitionList' => 'definition_list',
|
|
70
|
-
'Coradoc::CoreModel::DefinitionItem' => 'definition_item',
|
|
71
|
-
'Coradoc::CoreModel::Abbreviation' => 'abbreviation'
|
|
72
|
-
}.freeze
|
|
9
|
+
DEFAULT_TEMPLATE_DIR = TemplateLocator::DEFAULT_TEMPLATE_DIR
|
|
73
10
|
|
|
74
|
-
|
|
75
|
-
DEFAULT_TEMPLATE_DIR = Pathname.new(File.join(File.dirname(__FILE__), 'templates', 'core_model'))
|
|
11
|
+
include TemplateCaching
|
|
76
12
|
|
|
77
|
-
attr_reader :template_dirs, :
|
|
13
|
+
attr_reader :template_dirs, :options
|
|
78
14
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# Searched in order (first match wins). Can be a single path or array.
|
|
83
|
-
# @param include_default_templates [Boolean] Whether to fall back to built-in
|
|
84
|
-
# templates when not found in template_dirs. Default: true
|
|
85
|
-
# @param options [Hash] Additional options
|
|
86
|
-
# @option options [Boolean] :strict Raise error if template not found (default: false)
|
|
87
|
-
# @option options [Boolean] :cache_templates Cache parsed templates (default: true)
|
|
88
|
-
#
|
|
89
|
-
def initialize(template_dirs: nil, include_default_templates: true, options: {})
|
|
90
|
-
@template_dirs = normalize_template_dirs(template_dirs)
|
|
91
|
-
@include_default_templates = include_default_templates
|
|
92
|
-
@options = { cache_templates: true, strict: false }.merge(options)
|
|
15
|
+
def initialize(template_dirs: nil, **options)
|
|
16
|
+
@template_dirs = normalize_dirs(template_dirs)
|
|
17
|
+
@options = options
|
|
93
18
|
@template_cache = {}
|
|
94
|
-
|
|
19
|
+
@locator = TemplateLocator.new(
|
|
20
|
+
user_dirs: @template_dirs,
|
|
21
|
+
default_dir: DEFAULT_TEMPLATE_DIR
|
|
22
|
+
)
|
|
23
|
+
@section_numbers = {}
|
|
24
|
+
@layout_renderer = LayoutRenderer.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render(element)
|
|
28
|
+
result = Drop::DropFactory.create(element)
|
|
29
|
+
case result
|
|
30
|
+
when Drop::Base then render_drop(result)
|
|
31
|
+
when Array then result.map { |r| r.is_a?(Drop::Base) ? render_drop(r) : r }.join("\n")
|
|
32
|
+
when nil then ''
|
|
33
|
+
else result
|
|
34
|
+
end
|
|
95
35
|
end
|
|
96
36
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# @param context [Hash] Additional context for the template
|
|
101
|
-
# @return [String] Rendered HTML
|
|
102
|
-
def render(element, context = {})
|
|
103
|
-
return '' if element.nil?
|
|
104
|
-
|
|
105
|
-
# Ensure liquid drop class exists for lutaml-model elements
|
|
106
|
-
ensure_drop_class(element)
|
|
37
|
+
def render_drop(drop)
|
|
38
|
+
return '' if drop.nil?
|
|
39
|
+
return drop.to_s unless drop.is_a?(Drop::Base)
|
|
107
40
|
|
|
108
|
-
|
|
109
|
-
return element.map { |e| render(e, context) }.join("\n") if element.is_a?(Array)
|
|
41
|
+
annotate_section_number(drop)
|
|
110
42
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return escape_html(element)
|
|
115
|
-
when Numeric, TrueClass, FalseClass
|
|
116
|
-
return element.to_s
|
|
117
|
-
when NilClass
|
|
118
|
-
return ''
|
|
119
|
-
end
|
|
43
|
+
template_type = drop.template_type
|
|
44
|
+
template = find_and_load_template(template_type)
|
|
45
|
+
return render_fallback_drop(drop) unless template
|
|
120
46
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
47
|
+
assigns = { 'element' => drop }
|
|
48
|
+
template.render(assigns, registers: { renderer: self, section_numbers: @section_numbers }).strip
|
|
49
|
+
end
|
|
124
50
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
51
|
+
def render_html5(document, **options)
|
|
52
|
+
@section_numbers = compute_section_numbers(document, options)
|
|
53
|
+
body_html = render(document)
|
|
128
54
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if template
|
|
132
|
-
render_with_template(template, element, context)
|
|
55
|
+
if options[:layout] == :spa
|
|
56
|
+
render_spa_layout(document, body_html, options)
|
|
133
57
|
else
|
|
134
|
-
|
|
58
|
+
render_static_layout(document, body_html, options)
|
|
135
59
|
end
|
|
136
60
|
end
|
|
137
61
|
|
|
138
|
-
# Render a CoreModel element as a complete HTML5 document
|
|
139
|
-
#
|
|
140
|
-
# Wraps the fragment output of #render in a proper HTML5 document
|
|
141
|
-
# with DOCTYPE, charset, and viewport meta tags.
|
|
142
|
-
#
|
|
143
|
-
# @param element [Coradoc::CoreModel::Base] The element to render
|
|
144
|
-
# @param options [Hash] Document-level options
|
|
145
|
-
# @option options [String] :lang Document language (default: "en")
|
|
146
|
-
# @option options [String] :title Document title (default: extracted from element)
|
|
147
|
-
# @return [String] Complete HTML5 document
|
|
148
|
-
def render_html5(element, options = {})
|
|
149
|
-
body_html = render(element)
|
|
150
|
-
|
|
151
|
-
lang = options[:lang] || 'en'
|
|
152
|
-
title = options[:title] || extract_title(element) || 'Untitled Document'
|
|
153
|
-
|
|
154
|
-
<<~HTML
|
|
155
|
-
<!DOCTYPE html>
|
|
156
|
-
<html lang="#{lang}">
|
|
157
|
-
<head>
|
|
158
|
-
<meta charset="UTF-8">
|
|
159
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
160
|
-
<title>#{escape_html(title)}</title>
|
|
161
|
-
</head>
|
|
162
|
-
<body>
|
|
163
|
-
#{body_html}
|
|
164
|
-
</body>
|
|
165
|
-
</html>
|
|
166
|
-
HTML
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Get list of all available template names
|
|
170
|
-
#
|
|
171
|
-
# @return [Array<String>] List of template names (without .liquid extension)
|
|
172
62
|
def available_templates
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# Scan user template directories
|
|
176
|
-
@template_dirs.each do |dir|
|
|
177
|
-
core_model_dir = File.join(dir, 'core_model')
|
|
178
|
-
next unless File.directory?(core_model_dir)
|
|
179
|
-
|
|
180
|
-
Dir.glob(File.join(core_model_dir, '*.liquid')).each do |file|
|
|
181
|
-
templates << File.basename(file, '.liquid')
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Scan default templates if included
|
|
186
|
-
if @include_default_templates && File.directory?(DEFAULT_TEMPLATE_DIR)
|
|
187
|
-
Dir.glob(File.join(DEFAULT_TEMPLATE_DIR, '*.liquid')).each do |file|
|
|
188
|
-
templates << File.basename(file, '.liquid')
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
templates.to_a.sort
|
|
63
|
+
@locator.available_templates
|
|
193
64
|
end
|
|
194
65
|
|
|
195
|
-
# Check if a template exists for a given type
|
|
196
|
-
#
|
|
197
|
-
# @param type_name [String] Template type name (e.g., "bibliography")
|
|
198
|
-
# @return [Boolean] True if template exists
|
|
199
66
|
def template_exists?(type_name)
|
|
200
|
-
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Register a custom template type mapping
|
|
204
|
-
#
|
|
205
|
-
# @param class_name [String] Full class name (e.g., "Coradoc::CoreModel::Bibliography")
|
|
206
|
-
# @param type_name [String] Template type name (e.g., "bibliography")
|
|
207
|
-
def self.register_type(class_name, type_name)
|
|
208
|
-
@custom_type_map ||= {}
|
|
209
|
-
@custom_type_map[class_name] = type_name
|
|
67
|
+
@locator.exists?(type_name)
|
|
210
68
|
end
|
|
211
69
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@custom_type_map ||= {}
|
|
70
|
+
def find_template(type_name)
|
|
71
|
+
find_and_load_template(type_name)
|
|
215
72
|
end
|
|
216
73
|
|
|
217
74
|
private
|
|
218
75
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def ensure_drop_class(element)
|
|
222
|
-
klass = element.class
|
|
223
|
-
if klass.public_methods.include?(:register_class_if_liquid_defined) &&
|
|
224
|
-
klass.public_methods.include?(:base_drop_class) && !klass.base_drop_class
|
|
225
|
-
klass.register_class_if_liquid_defined
|
|
226
|
-
end
|
|
76
|
+
def build_toc(_document, options)
|
|
77
|
+
TocBuilder.from_options(options)
|
|
227
78
|
end
|
|
228
79
|
|
|
229
|
-
def
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
next unless klass.is_a?(Class)
|
|
235
|
-
next unless klass.public_methods.include?(:register_class_if_liquid_defined)
|
|
236
|
-
next if klass.public_methods.include?(:base_drop_class) && klass.base_drop_class
|
|
237
|
-
|
|
238
|
-
klass.register_class_if_liquid_defined
|
|
239
|
-
rescue StandardError
|
|
240
|
-
nil
|
|
80
|
+
def compute_section_numbers(document, options)
|
|
81
|
+
if options[:section_numbers] && document.is_a?(CoreModel::StructuralElement)
|
|
82
|
+
build_toc(document, options).section_number_map(document)
|
|
83
|
+
else
|
|
84
|
+
{}
|
|
241
85
|
end
|
|
242
86
|
end
|
|
243
87
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Array(dirs).map do |dir|
|
|
249
|
-
path = Pathname.new(dir)
|
|
250
|
-
path.absolute? ? path.to_s : File.expand_path(dir)
|
|
251
|
-
end
|
|
88
|
+
def render_toc_for(document, options)
|
|
89
|
+
toc = build_toc(document, options).build(document)
|
|
90
|
+
render(toc)
|
|
252
91
|
end
|
|
253
92
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
# @return [String, nil] Path to template file or nil
|
|
259
|
-
def find_template(type_name)
|
|
260
|
-
template_file = "#{type_name}.liquid"
|
|
261
|
-
|
|
262
|
-
# Search user template directories in order
|
|
263
|
-
@template_dirs.each do |dir|
|
|
264
|
-
# Check core_model subdirectory first
|
|
265
|
-
core_model_dir = File.join(dir, 'core_model')
|
|
266
|
-
path = File.join(core_model_dir, template_file)
|
|
267
|
-
return path if File.file?(path)
|
|
268
|
-
|
|
269
|
-
# Also check the directory itself
|
|
270
|
-
path = File.join(dir, template_file)
|
|
271
|
-
return path if File.file?(path)
|
|
93
|
+
def render_static_layout(document, body_html, options)
|
|
94
|
+
if options[:toc] && document.is_a?(CoreModel::StructuralElement)
|
|
95
|
+
toc_html = render_toc_for(document, options)
|
|
96
|
+
body_html = "#{toc_html}\n#{body_html}" unless toc_html.empty?
|
|
272
97
|
end
|
|
273
|
-
|
|
274
|
-
# Fall back to default templates if enabled
|
|
275
|
-
if @include_default_templates
|
|
276
|
-
path = File.join(DEFAULT_TEMPLATE_DIR, template_file)
|
|
277
|
-
return path if File.file?(path)
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
nil
|
|
98
|
+
@layout_renderer.render_static(document, body_html, options)
|
|
281
99
|
end
|
|
282
100
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
# @return [String, nil] Template type name or nil
|
|
287
|
-
def template_type_for(element)
|
|
288
|
-
class_name = element.class.name
|
|
289
|
-
|
|
290
|
-
# Check custom registrations first
|
|
291
|
-
self.class.custom_type_map[class_name] ||
|
|
292
|
-
TEMPLATE_TYPE_MAP[class_name] ||
|
|
293
|
-
derive_type_name(class_name)
|
|
101
|
+
def render_spa_layout(document, body_html, options)
|
|
102
|
+
toc_data = TocSerializer.new.build_json(document, options)
|
|
103
|
+
@layout_renderer.render_spa(document, options, body_html, toc_data)
|
|
294
104
|
end
|
|
295
105
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def derive_type_name(class_name)
|
|
301
|
-
parts = class_name.split('::')
|
|
302
|
-
return nil unless parts.length >= 2
|
|
303
|
-
|
|
304
|
-
# Just use the class name, underscored
|
|
305
|
-
parts.last
|
|
306
|
-
.gsub(/([A-Z])/, '_\1')
|
|
307
|
-
.downcase
|
|
308
|
-
.sub(/^_/, '')
|
|
106
|
+
def find_and_load_template(type_name)
|
|
107
|
+
cache_key = type_name.to_s
|
|
108
|
+
path = @locator.find(type_name)
|
|
109
|
+
load_template(cache: @template_cache, cache_key: cache_key, path: path)
|
|
309
110
|
end
|
|
310
111
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
# @return [Liquid::Template, nil] Parsed template or nil
|
|
315
|
-
def load_template(path)
|
|
316
|
-
cache_key = path.to_s
|
|
317
|
-
return @template_cache[cache_key] if @template_cache.key?(cache_key)
|
|
112
|
+
def annotate_section_number(drop)
|
|
113
|
+
return unless drop.is_a?(SectionNumberable)
|
|
114
|
+
return if @section_numbers.empty?
|
|
318
115
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
template
|
|
323
|
-
rescue Liquid::SyntaxError => e
|
|
324
|
-
warn "Template syntax error in #{path}: #{e.message}"
|
|
325
|
-
nil
|
|
116
|
+
id = drop.id
|
|
117
|
+
number = @section_numbers[id]
|
|
118
|
+
drop.section_number = number if number
|
|
326
119
|
end
|
|
327
120
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
# @param context [Hash] Additional context
|
|
333
|
-
# @return [String] Rendered HTML
|
|
334
|
-
def render_with_template(template, element, context)
|
|
335
|
-
# Convert element to Liquid Drop
|
|
336
|
-
liquid_drop = element.to_liquid
|
|
121
|
+
def render_fallback_drop(drop)
|
|
122
|
+
type = drop.template_type
|
|
123
|
+
resolved = TitleText.resolve(drop.model)
|
|
124
|
+
text = resolved ? Escape.escape_html(resolved) : ''
|
|
337
125
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
registers: { renderer: self }
|
|
342
|
-
}).strip
|
|
126
|
+
doc = Nokogiri::HTML::Document.new
|
|
127
|
+
fragment = Nokogiri::HTML::Builder.with(doc) do |builder|
|
|
128
|
+
builder.div(class: "element element-#{type}") { builder.text text }
|
|
343
129
|
end
|
|
344
|
-
|
|
345
|
-
# Build hash from drop's known attributes
|
|
346
|
-
# The Drop exposes attributes via method calls
|
|
347
|
-
assigns = build_assigns_from_drop(liquid_drop).merge(context)
|
|
348
|
-
|
|
349
|
-
# Render with registers containing renderer for recursive calls
|
|
350
|
-
template.render(assigns, {
|
|
351
|
-
registers: { renderer: self }
|
|
352
|
-
}).strip
|
|
130
|
+
fragment.at_css('div').to_html
|
|
353
131
|
end
|
|
354
132
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
# Get all attribute names from the drop's class
|
|
358
|
-
# These are defined by the Lutaml::Model attributes
|
|
359
|
-
assigns = {}
|
|
133
|
+
def normalize_dirs(dirs)
|
|
134
|
+
return [] if dirs.nil?
|
|
360
135
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
text href alt src level entries items rows cells
|
|
365
|
-
anchor term definition abbreviations].each do |key|
|
|
366
|
-
assigns[key] = drop[key] if drop.key?(key)
|
|
136
|
+
Array(dirs).map do |dir|
|
|
137
|
+
path = Pathname.new(dir)
|
|
138
|
+
path.absolute? ? path.to_s : File.expand_path(dir)
|
|
367
139
|
end
|
|
368
|
-
|
|
369
|
-
assigns
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
# Fallback rendering when no template found
|
|
373
|
-
#
|
|
374
|
-
# @param element [Object] The element
|
|
375
|
-
# @param context [Hash] Context
|
|
376
|
-
# @return [String] Rendered HTML
|
|
377
|
-
def render_fallback(element, _context)
|
|
378
|
-
raise "No template found for #{element.class.name}" if @options[:strict]
|
|
379
|
-
|
|
380
|
-
# Simple fallback - convert to string
|
|
381
|
-
class_name = element.class.name
|
|
382
|
-
simple_name = class_name.split('::').last
|
|
383
|
-
underscored = simple_name&.gsub(/([A-Z])/, '_\1')&.downcase&.sub(/^_/, '') || 'unknown'
|
|
384
|
-
|
|
385
|
-
"<div class=\"element element-#{underscored}\">#{escape_html(element.to_s)}</div>"
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
# Escape HTML entities
|
|
389
|
-
def escape_html(text)
|
|
390
|
-
text.to_s
|
|
391
|
-
.gsub(/&/, '&')
|
|
392
|
-
.gsub(/</, '<')
|
|
393
|
-
.gsub(/>/, '>')
|
|
394
|
-
.gsub(/"/, '"')
|
|
395
|
-
.gsub(/'/, ''')
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def extract_title(element)
|
|
399
|
-
return nil unless element
|
|
400
|
-
return element.title if element.is_a?(Coradoc::CoreModel::StructuralElement) && element.title
|
|
401
|
-
|
|
402
|
-
nil
|
|
403
140
|
end
|
|
404
141
|
end
|
|
405
|
-
|
|
406
|
-
# Backwards compatibility alias
|
|
407
|
-
TemplateRenderer = Renderer
|
|
408
142
|
end
|
|
409
143
|
end
|