coradoc-html 1.1.7 → 1.1.14
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 +72 -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 +72 -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/drop.rb +18 -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 +6 -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 +5 -23
- data/lib/coradoc/html/input/converters/sup.rb +5 -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 +12 -36
- data/lib/coradoc/html/input/converters.rb +55 -70
- data/lib/coradoc/html/input/html_converter.rb +2 -74
- data/lib/coradoc/html/input/plugin.rb +8 -57
- 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 +86 -357
- 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 +112 -0
- data/lib/coradoc/html/toc_serializer.rb +31 -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 +41 -88
- metadata +38 -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,138 @@
|
|
|
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?
|
|
37
|
+
def render_drop(drop)
|
|
38
|
+
return '' if drop.nil?
|
|
39
|
+
return drop.to_s unless drop.is_a?(Drop::Base)
|
|
104
40
|
|
|
105
|
-
|
|
106
|
-
ensure_drop_class(element)
|
|
41
|
+
annotate_section_number(drop)
|
|
107
42
|
|
|
108
|
-
|
|
109
|
-
|
|
43
|
+
template_type = drop.template_type
|
|
44
|
+
template = find_and_load_template(template_type)
|
|
45
|
+
return render_fallback_drop(drop) unless template
|
|
110
46
|
|
|
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
|
|
47
|
+
assigns = { 'element' => drop }
|
|
48
|
+
template.render(assigns, registers: { renderer: self, section_numbers: @section_numbers }).strip
|
|
49
|
+
end
|
|
120
50
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
51
|
+
def render_html5(document, **options)
|
|
52
|
+
builder = TocBuilder.from_options(options)
|
|
53
|
+
@toc, @section_numbers = if document.is_a?(CoreModel::StructuralElement)
|
|
54
|
+
builder.build_with_numbers(document)
|
|
55
|
+
else
|
|
56
|
+
[nil, {}]
|
|
57
|
+
end
|
|
124
58
|
|
|
125
|
-
|
|
126
|
-
template_path = find_template(type_name)
|
|
127
|
-
return render_fallback(element, context) if template_path.nil?
|
|
59
|
+
body_html = render(document)
|
|
128
60
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if template
|
|
132
|
-
render_with_template(template, element, context)
|
|
61
|
+
if options[:layout] == :spa
|
|
62
|
+
render_spa_layout(document, body_html, options)
|
|
133
63
|
else
|
|
134
|
-
|
|
64
|
+
render_static_layout(document, body_html, options)
|
|
135
65
|
end
|
|
136
66
|
end
|
|
137
67
|
|
|
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
68
|
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
|
|
69
|
+
@locator.available_templates
|
|
193
70
|
end
|
|
194
71
|
|
|
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
72
|
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
|
|
73
|
+
@locator.exists?(type_name)
|
|
210
74
|
end
|
|
211
75
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@custom_type_map ||= {}
|
|
76
|
+
def find_template(type_name)
|
|
77
|
+
find_and_load_template(type_name)
|
|
215
78
|
end
|
|
216
79
|
|
|
217
80
|
private
|
|
218
81
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
def ensure_core_model_drops
|
|
230
|
-
return unless defined?(Coradoc::CoreModel)
|
|
231
|
-
|
|
232
|
-
CoreModel.constants(false).each do |const_name|
|
|
233
|
-
klass = CoreModel.const_get(const_name)
|
|
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
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Normalize template_dirs to an array of absolute paths
|
|
245
|
-
def normalize_template_dirs(dirs)
|
|
246
|
-
return [] if dirs.nil?
|
|
247
|
-
|
|
248
|
-
Array(dirs).map do |dir|
|
|
249
|
-
path = Pathname.new(dir)
|
|
250
|
-
path.absolute? ? path.to_s : File.expand_path(dir)
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# Find template file for a type name
|
|
255
|
-
# Searches template_dirs in order, then defaults if enabled
|
|
256
|
-
#
|
|
257
|
-
# @param type_name [String] Template type name
|
|
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)
|
|
272
|
-
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)
|
|
82
|
+
def render_static_layout(document, body_html, options)
|
|
83
|
+
if options[:toc] && @toc
|
|
84
|
+
toc_html = render(@toc)
|
|
85
|
+
body_html = "#{toc_html}\n#{body_html}" unless toc_html.empty?
|
|
278
86
|
end
|
|
279
|
-
|
|
280
|
-
nil
|
|
87
|
+
@layout_renderer.render_static(document, body_html, options)
|
|
281
88
|
end
|
|
282
89
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
self.class.custom_type_map[class_name] ||
|
|
292
|
-
TEMPLATE_TYPE_MAP[class_name] ||
|
|
293
|
-
derive_type_name(class_name)
|
|
90
|
+
def render_spa_layout(document, body_html, options)
|
|
91
|
+
numbered = options[:section_numbers] == true
|
|
92
|
+
toc_data = if @toc
|
|
93
|
+
{ entries: TocSerializer.new.serialize_entries(@toc.entries), numbered: numbered }
|
|
94
|
+
else
|
|
95
|
+
{ entries: [], numbered: false }
|
|
96
|
+
end
|
|
97
|
+
@layout_renderer.render_spa(document, options, body_html, toc_data)
|
|
294
98
|
end
|
|
295
99
|
|
|
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(/^_/, '')
|
|
100
|
+
def find_and_load_template(type_name)
|
|
101
|
+
cache_key = type_name.to_s
|
|
102
|
+
path = @locator.find(type_name)
|
|
103
|
+
load_template(cache: @template_cache, cache_key: cache_key, path: path)
|
|
309
104
|
end
|
|
310
105
|
|
|
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)
|
|
106
|
+
def annotate_section_number(drop)
|
|
107
|
+
return unless drop.is_a?(SectionNumberable)
|
|
108
|
+
return if @section_numbers.empty?
|
|
318
109
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
template
|
|
323
|
-
rescue Liquid::SyntaxError => e
|
|
324
|
-
warn "Template syntax error in #{path}: #{e.message}"
|
|
325
|
-
nil
|
|
110
|
+
id = drop.id
|
|
111
|
+
number = @section_numbers[id]
|
|
112
|
+
drop.section_number = number if number
|
|
326
113
|
end
|
|
327
114
|
|
|
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
|
|
115
|
+
def render_fallback_drop(drop)
|
|
116
|
+
type = drop.template_type
|
|
117
|
+
resolved = TitleText.resolve(drop.model)
|
|
118
|
+
text = resolved ? Escape.escape_html(resolved) : ''
|
|
337
119
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
|
120
|
+
fragment = Nokogiri::HTML.fragment
|
|
121
|
+
div = Nokogiri::XML::Node.new('div', fragment.document)
|
|
122
|
+
div['class'] = "element element-#{type}"
|
|
123
|
+
div.content = text
|
|
124
|
+
fragment.add_child(div)
|
|
125
|
+
fragment.to_html
|
|
353
126
|
end
|
|
354
127
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
# Get all attribute names from the drop's class
|
|
358
|
-
# These are defined by the Lutaml::Model attributes
|
|
359
|
-
assigns = {}
|
|
128
|
+
def normalize_dirs(dirs)
|
|
129
|
+
return [] if dirs.nil?
|
|
360
130
|
|
|
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)
|
|
131
|
+
Array(dirs).map do |dir|
|
|
132
|
+
path = Pathname.new(dir)
|
|
133
|
+
path.absolute? ? path.to_s : File.expand_path(dir)
|
|
367
134
|
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
135
|
end
|
|
404
136
|
end
|
|
405
|
-
|
|
406
|
-
# Backwards compatibility alias
|
|
407
|
-
TemplateRenderer = Renderer
|
|
408
137
|
end
|
|
409
138
|
end
|