coradoc-html 1.1.7
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 +7 -0
- data/LICENSE.txt +21 -0
- data/lib/coradoc/html/base.rb +157 -0
- data/lib/coradoc/html/config.rb +467 -0
- data/lib/coradoc/html/converter_base.rb +177 -0
- data/lib/coradoc/html/converters/admonition.rb +180 -0
- data/lib/coradoc/html/converters/attribute.rb +68 -0
- data/lib/coradoc/html/converters/attribute_reference.rb +60 -0
- data/lib/coradoc/html/converters/audio.rb +165 -0
- data/lib/coradoc/html/converters/base.rb +615 -0
- data/lib/coradoc/html/converters/bibliography.rb +82 -0
- data/lib/coradoc/html/converters/bibliography_entry.rb +108 -0
- data/lib/coradoc/html/converters/block_image.rb +72 -0
- data/lib/coradoc/html/converters/bold.rb +34 -0
- data/lib/coradoc/html/converters/break.rb +32 -0
- data/lib/coradoc/html/converters/comment_block.rb +42 -0
- data/lib/coradoc/html/converters/comment_line.rb +54 -0
- data/lib/coradoc/html/converters/cross_reference.rb +59 -0
- data/lib/coradoc/html/converters/document.rb +108 -0
- data/lib/coradoc/html/converters/example.rb +114 -0
- data/lib/coradoc/html/converters/highlight.rb +34 -0
- data/lib/coradoc/html/converters/include.rb +68 -0
- data/lib/coradoc/html/converters/inline_image.rb +41 -0
- data/lib/coradoc/html/converters/italic.rb +34 -0
- data/lib/coradoc/html/converters/line_break.rb +31 -0
- data/lib/coradoc/html/converters/link.rb +46 -0
- data/lib/coradoc/html/converters/list_item.rb +75 -0
- data/lib/coradoc/html/converters/listing.rb +99 -0
- data/lib/coradoc/html/converters/literal.rb +102 -0
- data/lib/coradoc/html/converters/monospace.rb +34 -0
- data/lib/coradoc/html/converters/open.rb +78 -0
- data/lib/coradoc/html/converters/ordered.rb +53 -0
- data/lib/coradoc/html/converters/paragraph.rb +46 -0
- data/lib/coradoc/html/converters/quote.rb +113 -0
- data/lib/coradoc/html/converters/reviewer_comment.rb +74 -0
- data/lib/coradoc/html/converters/reviewer_note.rb +134 -0
- data/lib/coradoc/html/converters/section.rb +90 -0
- data/lib/coradoc/html/converters/sidebar.rb +113 -0
- data/lib/coradoc/html/converters/source.rb +137 -0
- data/lib/coradoc/html/converters/source_code.rb +16 -0
- data/lib/coradoc/html/converters/span.rb +61 -0
- data/lib/coradoc/html/converters/strikethrough.rb +34 -0
- data/lib/coradoc/html/converters/subscript.rb +34 -0
- data/lib/coradoc/html/converters/superscript.rb +34 -0
- data/lib/coradoc/html/converters/table.rb +85 -0
- data/lib/coradoc/html/converters/table_cell.rb +203 -0
- data/lib/coradoc/html/converters/table_row.rb +45 -0
- data/lib/coradoc/html/converters/template_html_converter.rb +105 -0
- data/lib/coradoc/html/converters/term.rb +58 -0
- data/lib/coradoc/html/converters/text_element.rb +44 -0
- data/lib/coradoc/html/converters/underline.rb +34 -0
- data/lib/coradoc/html/converters/unordered.rb +47 -0
- data/lib/coradoc/html/converters/verse.rb +105 -0
- data/lib/coradoc/html/converters/video.rb +179 -0
- data/lib/coradoc/html/element_mapping.rb +210 -0
- data/lib/coradoc/html/entity.rb +137 -0
- data/lib/coradoc/html/input/cleaner.rb +163 -0
- data/lib/coradoc/html/input/config.rb +79 -0
- data/lib/coradoc/html/input/converters/a.rb +90 -0
- data/lib/coradoc/html/input/converters/aside.rb +23 -0
- data/lib/coradoc/html/input/converters/audio.rb +50 -0
- data/lib/coradoc/html/input/converters/base.rb +116 -0
- data/lib/coradoc/html/input/converters/blockquote.rb +25 -0
- data/lib/coradoc/html/input/converters/br.rb +19 -0
- data/lib/coradoc/html/input/converters/bypass.rb +83 -0
- data/lib/coradoc/html/input/converters/code.rb +25 -0
- data/lib/coradoc/html/input/converters/div.rb +25 -0
- data/lib/coradoc/html/input/converters/dl.rb +106 -0
- data/lib/coradoc/html/input/converters/drop.rb +28 -0
- data/lib/coradoc/html/input/converters/em.rb +23 -0
- data/lib/coradoc/html/input/converters/figure.rb +58 -0
- data/lib/coradoc/html/input/converters/h.rb +76 -0
- data/lib/coradoc/html/input/converters/head.rb +30 -0
- data/lib/coradoc/html/input/converters/hr.rb +20 -0
- data/lib/coradoc/html/input/converters/ignore.rb +22 -0
- data/lib/coradoc/html/input/converters/img.rb +110 -0
- data/lib/coradoc/html/input/converters/li.rb +35 -0
- data/lib/coradoc/html/input/converters/mark.rb +21 -0
- data/lib/coradoc/html/input/converters/markup.rb +107 -0
- data/lib/coradoc/html/input/converters/math.rb +46 -0
- data/lib/coradoc/html/input/converters/ol.rb +46 -0
- data/lib/coradoc/html/input/converters/p.rb +81 -0
- data/lib/coradoc/html/input/converters/pass_through.rb +19 -0
- data/lib/coradoc/html/input/converters/pre.rb +59 -0
- data/lib/coradoc/html/input/converters/q.rb +24 -0
- data/lib/coradoc/html/input/converters/strong.rb +22 -0
- data/lib/coradoc/html/input/converters/sub.rb +40 -0
- data/lib/coradoc/html/input/converters/sup.rb +40 -0
- data/lib/coradoc/html/input/converters/table.rb +64 -0
- data/lib/coradoc/html/input/converters/td.rb +70 -0
- data/lib/coradoc/html/input/converters/text.rb +67 -0
- data/lib/coradoc/html/input/converters/th.rb +20 -0
- data/lib/coradoc/html/input/converters/tr.rb +28 -0
- data/lib/coradoc/html/input/converters/video.rb +53 -0
- data/lib/coradoc/html/input/converters.rb +122 -0
- data/lib/coradoc/html/input/errors.rb +22 -0
- data/lib/coradoc/html/input/html_converter.rb +170 -0
- data/lib/coradoc/html/input/plugin.rb +169 -0
- data/lib/coradoc/html/input/plugins/plateau.rb +229 -0
- data/lib/coradoc/html/input/postprocessor.rb +31 -0
- data/lib/coradoc/html/input.rb +68 -0
- data/lib/coradoc/html/output.rb +95 -0
- data/lib/coradoc/html/renderer.rb +409 -0
- data/lib/coradoc/html/spa.rb +309 -0
- data/lib/coradoc/html/static.rb +293 -0
- data/lib/coradoc/html/template_config.rb +151 -0
- data/lib/coradoc/html/template_helpers.rb +58 -0
- data/lib/coradoc/html/template_locator.rb +114 -0
- data/lib/coradoc/html/theme/base.rb +231 -0
- data/lib/coradoc/html/theme/classic_renderer.rb +390 -0
- data/lib/coradoc/html/theme/modern/components/ui_components.rb +344 -0
- data/lib/coradoc/html/theme/modern/css_generator.rb +311 -0
- data/lib/coradoc/html/theme/modern/javascript_generator.rb +314 -0
- data/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +382 -0
- data/lib/coradoc/html/theme/modern/tailwind_config_builder.rb +164 -0
- data/lib/coradoc/html/theme/modern/vue_template_generator.rb +374 -0
- data/lib/coradoc/html/theme/modern_renderer.rb +250 -0
- data/lib/coradoc/html/theme/registry.rb +153 -0
- data/lib/coradoc/html/theme.rb +13 -0
- data/lib/coradoc/html/transform/from_core_model.rb +32 -0
- data/lib/coradoc/html/transform/to_core_model.rb +39 -0
- data/lib/coradoc/html/version.rb +7 -0
- data/lib/coradoc/html.rb +255 -0
- metadata +264 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ef32c1399675f7e55b4ab77c56dd992b246c0f5fc0d42f027a4792c35b67ed05
|
|
4
|
+
data.tar.gz: be81a3f8b9baa8dd31515bfeb32b134761271e4066d53cbeb57856a6735a4c77
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7e2696c2a2e80fb3059881b2ef7a4dbd124f8ca252b1b2fed68590324ae7d875c0d178349647be091547cf7d9143b64b75286abd55a5059dd8d91e15aff55715
|
|
7
|
+
data.tar.gz: 5ccdc1ff38c8c851f0923deebddbe933488820738a6fb2faa1ed02d3262ca559e41d0c0d9b85e4fb5954a7809cf18ff7b1158dc960f5bfa973404c3749edbdda
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Abu Nashir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Html
|
|
5
|
+
# Base module for HTML processing utilities
|
|
6
|
+
module Base
|
|
7
|
+
class << self
|
|
8
|
+
# Convert content to HTML, handling various input types
|
|
9
|
+
def convert_content(content, state = {})
|
|
10
|
+
return '' if content.nil?
|
|
11
|
+
|
|
12
|
+
case content
|
|
13
|
+
when String
|
|
14
|
+
return '' if content.empty?
|
|
15
|
+
|
|
16
|
+
escape_html(content)
|
|
17
|
+
when Array
|
|
18
|
+
return '' if content.empty?
|
|
19
|
+
|
|
20
|
+
content.map { |item| convert_content(item, state) }.join
|
|
21
|
+
else
|
|
22
|
+
# If content responds to a converter, use it
|
|
23
|
+
converter = find_converter(content.class)
|
|
24
|
+
if converter
|
|
25
|
+
converter.to_html(content, state)
|
|
26
|
+
else
|
|
27
|
+
content.to_s
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Find the appropriate converter for a model class
|
|
33
|
+
def find_converter(model_class)
|
|
34
|
+
return nil unless defined?(Coradoc::Html::Converters)
|
|
35
|
+
|
|
36
|
+
converter_name = model_class.name.split('::').last
|
|
37
|
+
|
|
38
|
+
# Use const_get on the Converters module to trigger autoload
|
|
39
|
+
begin
|
|
40
|
+
klass = Coradoc::Html::Converters.const_get(converter_name, false)
|
|
41
|
+
# Return nil if this is the Base class itself (not a real converter)
|
|
42
|
+
# or if it doesn't inherit from Converters::Base
|
|
43
|
+
return nil if klass == Coradoc::Html::Converters::Base
|
|
44
|
+
return nil unless klass <= Coradoc::Html::Converters::Base
|
|
45
|
+
|
|
46
|
+
klass
|
|
47
|
+
rescue NameError
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Escape HTML special characters
|
|
53
|
+
def escape_html(text)
|
|
54
|
+
return '' if text.nil?
|
|
55
|
+
return text unless text.is_a?(String)
|
|
56
|
+
|
|
57
|
+
text
|
|
58
|
+
.gsub('&', '&')
|
|
59
|
+
.gsub('<', '<')
|
|
60
|
+
.gsub('>', '>')
|
|
61
|
+
.gsub('"', '"')
|
|
62
|
+
.gsub("'", ''')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Unescape HTML entities
|
|
66
|
+
def unescape_html(text)
|
|
67
|
+
return '' if text.nil?
|
|
68
|
+
return text unless text.is_a?(String)
|
|
69
|
+
|
|
70
|
+
text
|
|
71
|
+
.gsub('&', '&')
|
|
72
|
+
.gsub('<', '<')
|
|
73
|
+
.gsub('>', '>')
|
|
74
|
+
.gsub('"', '"')
|
|
75
|
+
.gsub(''', "'")
|
|
76
|
+
.gsub(''', "'")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Build HTML element with attributes
|
|
80
|
+
def build_element(tag, content = nil, attributes = {})
|
|
81
|
+
attrs = build_attributes(attributes)
|
|
82
|
+
attr_string = attrs.empty? ? '' : " #{attrs}"
|
|
83
|
+
|
|
84
|
+
# Handle empty content (String, Array, or nil)
|
|
85
|
+
content_empty = case content
|
|
86
|
+
when nil, String, Array
|
|
87
|
+
content.nil? || content.empty?
|
|
88
|
+
else
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if content_empty
|
|
93
|
+
# Self-closing for void elements
|
|
94
|
+
if void_element?(tag)
|
|
95
|
+
"<#{tag}#{attr_string}>"
|
|
96
|
+
else
|
|
97
|
+
"<#{tag}#{attr_string}></#{tag}>"
|
|
98
|
+
end
|
|
99
|
+
else
|
|
100
|
+
"<#{tag}#{attr_string}>#{content}</#{tag}>"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Build HTML attributes string
|
|
105
|
+
def build_attributes(attributes)
|
|
106
|
+
return '' if attributes.nil? || attributes.empty?
|
|
107
|
+
|
|
108
|
+
attributes.map do |key, value|
|
|
109
|
+
next if value.nil?
|
|
110
|
+
|
|
111
|
+
escaped_value = escape_html(value.to_s)
|
|
112
|
+
%(#{key}="#{escaped_value}")
|
|
113
|
+
end.compact.join(' ')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Check if element is a void element (self-closing)
|
|
117
|
+
def void_element?(tag)
|
|
118
|
+
%w[area base br col embed hr img input link meta param source track wbr].include?(tag.to_s)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Extract attributes from a CoreModel
|
|
122
|
+
#
|
|
123
|
+
# @param model [Coradoc::CoreModel::Base] Model to extract attributes from
|
|
124
|
+
# @return [Hash] Attributes hash
|
|
125
|
+
def extract_attributes(model)
|
|
126
|
+
attrs = {}
|
|
127
|
+
|
|
128
|
+
attrs[:id] = model.id if model.id
|
|
129
|
+
attrs[:title] = model.title if model.title
|
|
130
|
+
|
|
131
|
+
if model.is_a?(Coradoc::CoreModel::StructuralElement) && model.metadata
|
|
132
|
+
attrs[:class] = model.metadata[:class] || model.metadata[:role]
|
|
133
|
+
attrs.merge!(model.metadata.except(:class, :role))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
attrs
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Wrap content with line breaks if needed
|
|
140
|
+
def wrap_lines(content)
|
|
141
|
+
return content unless content.is_a?(String)
|
|
142
|
+
|
|
143
|
+
content.split("\n").join("<br>\n")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Process children of a node (common operation)
|
|
147
|
+
def treat_children(children, state = {})
|
|
148
|
+
return [] if children.nil? || children.empty?
|
|
149
|
+
|
|
150
|
+
Array(children).map do |child|
|
|
151
|
+
convert_content(child, state)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Html
|
|
5
|
+
# HTML configuration and options
|
|
6
|
+
module Config
|
|
7
|
+
# Default HTML output options
|
|
8
|
+
DEFAULT_OPTIONS = {
|
|
9
|
+
# Theme system options
|
|
10
|
+
theme: :classic,
|
|
11
|
+
modern: {
|
|
12
|
+
# Appearance
|
|
13
|
+
color_scheme: :glass,
|
|
14
|
+
primary_color: '#6366f1',
|
|
15
|
+
accent_color: '#8b5cf6',
|
|
16
|
+
|
|
17
|
+
# Layout
|
|
18
|
+
max_width: '1200px',
|
|
19
|
+
content_width: '65ch',
|
|
20
|
+
sidebar_width: '280px',
|
|
21
|
+
|
|
22
|
+
# Features
|
|
23
|
+
theme_toggle: true,
|
|
24
|
+
reading_progress: true,
|
|
25
|
+
back_to_top: true,
|
|
26
|
+
toc_sticky: true,
|
|
27
|
+
copy_code_buttons: true,
|
|
28
|
+
|
|
29
|
+
# Animation
|
|
30
|
+
enable_animations: true,
|
|
31
|
+
animation_duration: '300ms',
|
|
32
|
+
|
|
33
|
+
# Performance
|
|
34
|
+
lazy_load_images: true
|
|
35
|
+
}.freeze,
|
|
36
|
+
|
|
37
|
+
# HTML version
|
|
38
|
+
html_version: :html5,
|
|
39
|
+
|
|
40
|
+
# Formatting options
|
|
41
|
+
pretty_print: false,
|
|
42
|
+
indent: ' ',
|
|
43
|
+
line_wrap: 0,
|
|
44
|
+
|
|
45
|
+
# Content options
|
|
46
|
+
escape_content: true,
|
|
47
|
+
preserve_whitespace: false,
|
|
48
|
+
convert_line_breaks: true,
|
|
49
|
+
preserve_comments: false,
|
|
50
|
+
|
|
51
|
+
# Element options
|
|
52
|
+
use_semantic_elements: true,
|
|
53
|
+
add_css_classes: true,
|
|
54
|
+
add_data_attributes: false,
|
|
55
|
+
|
|
56
|
+
# Link options
|
|
57
|
+
external_link_target: nil,
|
|
58
|
+
link_rel: nil,
|
|
59
|
+
|
|
60
|
+
# Image options
|
|
61
|
+
image_loading: nil,
|
|
62
|
+
image_decoding: nil,
|
|
63
|
+
|
|
64
|
+
# Code block options
|
|
65
|
+
syntax_highlighter: nil,
|
|
66
|
+
syntax_highlighter_opts: {},
|
|
67
|
+
|
|
68
|
+
# Table options
|
|
69
|
+
table_border: false,
|
|
70
|
+
table_stripes: false,
|
|
71
|
+
|
|
72
|
+
# Attribute options
|
|
73
|
+
preserve_custom_attributes: true,
|
|
74
|
+
attribute_prefix: 'data-',
|
|
75
|
+
|
|
76
|
+
# CSS & Styling options
|
|
77
|
+
stylesheet: 'coradoc.css',
|
|
78
|
+
stylesdir: './css',
|
|
79
|
+
linkcss: false,
|
|
80
|
+
copycss: true,
|
|
81
|
+
css_theme: 'professional',
|
|
82
|
+
custom_css: nil,
|
|
83
|
+
|
|
84
|
+
# JavaScript options
|
|
85
|
+
javascript: 'coradoc.js',
|
|
86
|
+
jsdir: './js',
|
|
87
|
+
linkjs: false,
|
|
88
|
+
theme_toggle: true,
|
|
89
|
+
toc_interactive: nil,
|
|
90
|
+
|
|
91
|
+
# Document metadata options
|
|
92
|
+
author: nil,
|
|
93
|
+
description: nil,
|
|
94
|
+
keywords: nil,
|
|
95
|
+
lang: 'en',
|
|
96
|
+
embedded: false,
|
|
97
|
+
meta_tags: {},
|
|
98
|
+
|
|
99
|
+
# Table of contents options
|
|
100
|
+
toc: false,
|
|
101
|
+
toclevels: 2,
|
|
102
|
+
toc_title: 'Table of Contents',
|
|
103
|
+
toc_placement: :auto,
|
|
104
|
+
|
|
105
|
+
# Section numbering options
|
|
106
|
+
sectnums: false,
|
|
107
|
+
sectnumlevels: 3,
|
|
108
|
+
|
|
109
|
+
# Syntax highlighting options
|
|
110
|
+
source_highlighter: nil,
|
|
111
|
+
highlightjs_theme: 'github',
|
|
112
|
+
pygments_style: 'default',
|
|
113
|
+
rouge_style: 'github'
|
|
114
|
+
}.freeze
|
|
115
|
+
|
|
116
|
+
class << self
|
|
117
|
+
# Get default options
|
|
118
|
+
def default_options
|
|
119
|
+
DEFAULT_OPTIONS.dup
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Merge user options with defaults
|
|
123
|
+
def merge_options(user_options = {})
|
|
124
|
+
default_options.merge(user_options)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Validate options
|
|
128
|
+
def validate_options(options)
|
|
129
|
+
valid_keys = DEFAULT_OPTIONS.keys
|
|
130
|
+
invalid_keys = options.keys - valid_keys
|
|
131
|
+
|
|
132
|
+
raise ArgumentError, "Invalid options: #{invalid_keys.join(', ')}" unless invalid_keys.empty?
|
|
133
|
+
|
|
134
|
+
options
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Get CSS class for element type
|
|
138
|
+
def css_class_for(element_type, role = nil)
|
|
139
|
+
classes = [element_type.to_s.tr('_', '-')]
|
|
140
|
+
classes << role if role
|
|
141
|
+
classes.join(' ')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Get data attribute name
|
|
145
|
+
def data_attribute_name(name, prefix: 'data-')
|
|
146
|
+
"#{prefix}#{name.to_s.tr('_', '-')}"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Build element configuration
|
|
150
|
+
def element_config(element_type, options = {})
|
|
151
|
+
{
|
|
152
|
+
tag: html_tag_for(element_type),
|
|
153
|
+
css_class: css_class_for(element_type, options[:role]),
|
|
154
|
+
attributes: options[:attributes] || {}
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Map element type to HTML tag
|
|
159
|
+
def html_tag_for(element_type)
|
|
160
|
+
TAG_MAPPING[element_type] || 'div'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Get stylesheet path
|
|
164
|
+
def stylesheet_path(options = {})
|
|
165
|
+
# When linking, use css_theme-based filename, not the stylesheet option
|
|
166
|
+
css_theme = options[:css_theme] || DEFAULT_OPTIONS[:css_theme]
|
|
167
|
+
stylesheet = "#{css_theme}.css"
|
|
168
|
+
stylesdir = options[:stylesdir] || DEFAULT_OPTIONS[:stylesdir]
|
|
169
|
+
|
|
170
|
+
if stylesdir && stylesdir != '.'
|
|
171
|
+
File.join(stylesdir, stylesheet)
|
|
172
|
+
else
|
|
173
|
+
stylesheet
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Get embedded stylesheet content
|
|
178
|
+
def embedded_stylesheet(options = {})
|
|
179
|
+
css_theme = options[:css_theme] || DEFAULT_OPTIONS[:css_theme]
|
|
180
|
+
stylesheet_name = "#{css_theme}.css"
|
|
181
|
+
|
|
182
|
+
# Try themes directory first
|
|
183
|
+
themes_path = File.join(__dir__, 'assets', 'themes', stylesheet_name)
|
|
184
|
+
asset_path = if File.exist?(themes_path)
|
|
185
|
+
themes_path
|
|
186
|
+
else
|
|
187
|
+
# Fall back to assets directory for backward compatibility
|
|
188
|
+
File.join(__dir__, 'assets', stylesheet_name)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
css_content = if File.exist?(asset_path)
|
|
192
|
+
File.read(asset_path)
|
|
193
|
+
else
|
|
194
|
+
# Fallback to default coradoc.css
|
|
195
|
+
default_path = File.join(__dir__, 'assets', 'coradoc.css')
|
|
196
|
+
File.exist?(default_path) ? File.read(default_path) : ''
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Resolve @import statements for embedded CSS
|
|
200
|
+
# @import doesn't work in inline <style> tags
|
|
201
|
+
resolve_css_imports(css_content, File.dirname(asset_path))
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Resolve @import statements in CSS content
|
|
205
|
+
# @param css_content [String] CSS content with potential @import statements
|
|
206
|
+
# @param base_dir [String] Base directory for resolving relative imports
|
|
207
|
+
# @return [String] CSS content with imports resolved
|
|
208
|
+
def resolve_css_imports(css_content, base_dir)
|
|
209
|
+
# Match @import url('...') or @import url("...") or @import '...' or @import "..."
|
|
210
|
+
css_content.gsub(/@import\s+(?:url\()?['"]([^'"]+)['"]\)?;?/) do
|
|
211
|
+
import_path = ::Regexp.last_match(1)
|
|
212
|
+
full_path = File.join(base_dir, import_path)
|
|
213
|
+
|
|
214
|
+
if File.exist?(full_path)
|
|
215
|
+
# Read the imported file and recursively resolve its imports
|
|
216
|
+
imported_content = File.read(full_path)
|
|
217
|
+
resolve_css_imports(imported_content, File.dirname(full_path))
|
|
218
|
+
else
|
|
219
|
+
# Keep the original import if file not found
|
|
220
|
+
::Regexp.last_match(0)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Build CSS link tag
|
|
226
|
+
def css_link_tag(options = {})
|
|
227
|
+
href = stylesheet_path(options)
|
|
228
|
+
%(<link rel="stylesheet" href="#{href}">)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Build CSS style tag with embedded content
|
|
232
|
+
def css_style_tag(options = {})
|
|
233
|
+
css_content = embedded_stylesheet(options)
|
|
234
|
+
custom_css = options[:custom_css]
|
|
235
|
+
|
|
236
|
+
content = css_content
|
|
237
|
+
content += "\n\n#{custom_css}" if custom_css && !custom_css.empty?
|
|
238
|
+
|
|
239
|
+
%(<style>\n#{content}\n</style>)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Build custom CSS style tag
|
|
243
|
+
def custom_css_tag(custom_css)
|
|
244
|
+
return '' unless custom_css && !custom_css.empty?
|
|
245
|
+
|
|
246
|
+
%(<style>\n#{custom_css}\n</style>)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Determine whether to embed or link CSS
|
|
250
|
+
def embed_css?(options = {})
|
|
251
|
+
# Embed if linkcss is false or embedded mode is true
|
|
252
|
+
!options.fetch(:linkcss, DEFAULT_OPTIONS[:linkcss]) ||
|
|
253
|
+
options.fetch(:embedded, DEFAULT_OPTIONS[:embedded])
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Build complete CSS tags (link or embedded, plus custom)
|
|
257
|
+
def css_tags(options = {})
|
|
258
|
+
tags = []
|
|
259
|
+
|
|
260
|
+
if embed_css?(options)
|
|
261
|
+
# Embedded mode: include full stylesheet in style tag
|
|
262
|
+
tags << css_style_tag(options)
|
|
263
|
+
else
|
|
264
|
+
# Linked mode: link to external stylesheet
|
|
265
|
+
tags << css_link_tag(options)
|
|
266
|
+
# Add custom CSS separately if provided
|
|
267
|
+
tags << custom_css_tag(options[:custom_css]) if options[:custom_css]
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
tags.join("\n")
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Determine whether to embed or link JavaScript
|
|
274
|
+
def embed_js?(options = {})
|
|
275
|
+
# Embed if linkjs is false, embedded mode is true, or linkcss is false (to match CSS behavior)
|
|
276
|
+
!options.fetch(:linkjs, DEFAULT_OPTIONS[:linkjs]) ||
|
|
277
|
+
options.fetch(:embedded, DEFAULT_OPTIONS[:embedded]) ||
|
|
278
|
+
!options.fetch(:linkcss, DEFAULT_OPTIONS[:linkcss])
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Get JavaScript file path
|
|
282
|
+
def javascript_path(options = {})
|
|
283
|
+
javascript = options[:javascript] || DEFAULT_OPTIONS[:javascript]
|
|
284
|
+
jsdir = options[:jsdir] || DEFAULT_OPTIONS[:jsdir]
|
|
285
|
+
|
|
286
|
+
if jsdir && jsdir != '.'
|
|
287
|
+
File.join(jsdir, javascript)
|
|
288
|
+
else
|
|
289
|
+
javascript
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Get embedded JavaScript content
|
|
294
|
+
def embedded_javascript(options = {})
|
|
295
|
+
javascript_name = options[:javascript] || DEFAULT_OPTIONS[:javascript]
|
|
296
|
+
asset_path = File.join(__dir__, 'assets', 'js', javascript_name)
|
|
297
|
+
|
|
298
|
+
if File.exist?(asset_path)
|
|
299
|
+
File.read(asset_path)
|
|
300
|
+
else
|
|
301
|
+
''
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Build JavaScript link tag
|
|
306
|
+
def js_link_tag(options = {})
|
|
307
|
+
src = javascript_path(options)
|
|
308
|
+
%(<script src="#{src}" defer></script>)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Build JavaScript script tag with embedded content
|
|
312
|
+
def js_script_tag(options = {})
|
|
313
|
+
js_content = embedded_javascript(options)
|
|
314
|
+
return '' if js_content.empty?
|
|
315
|
+
|
|
316
|
+
%(<script>\n#{js_content}\n</script>)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Build complete JavaScript tags (link or embedded)
|
|
320
|
+
def js_tags(options = {})
|
|
321
|
+
return '' if options[:javascript] == false
|
|
322
|
+
|
|
323
|
+
tags = []
|
|
324
|
+
|
|
325
|
+
tags << if embed_js?(options)
|
|
326
|
+
# Embedded mode: include full JavaScript in script tag
|
|
327
|
+
js_script_tag(options)
|
|
328
|
+
else
|
|
329
|
+
# Linked mode: link to external JavaScript file
|
|
330
|
+
js_link_tag(options)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
tags.join("\n")
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Check if theme toggle should be enabled
|
|
337
|
+
def theme_toggle?(options = {})
|
|
338
|
+
options.fetch(:theme_toggle, DEFAULT_OPTIONS[:theme_toggle])
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Check if interactive TOC should be enabled
|
|
342
|
+
def toc_interactive?(options = {})
|
|
343
|
+
# Default to true if TOC is enabled and toc_interactive is not explicitly set to false
|
|
344
|
+
toc_enabled = options.fetch(:toc, DEFAULT_OPTIONS[:toc])
|
|
345
|
+
toc_interactive = options[:toc_interactive]
|
|
346
|
+
|
|
347
|
+
# If toc_interactive is nil, default to true when TOC is enabled
|
|
348
|
+
if toc_interactive.nil?
|
|
349
|
+
toc_enabled
|
|
350
|
+
else
|
|
351
|
+
toc_interactive
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Build syntax highlighter tags (CSS and JS)
|
|
356
|
+
# @param options [Hash] Configuration options
|
|
357
|
+
# @return [String] HTML tags for syntax highlighting
|
|
358
|
+
def syntax_highlighter_tags(options = {})
|
|
359
|
+
highlighter = options[:source_highlighter]
|
|
360
|
+
return '' unless highlighter
|
|
361
|
+
|
|
362
|
+
case highlighter.to_sym
|
|
363
|
+
when :highlightjs, :highlight_js, :'highlight.js'
|
|
364
|
+
highlightjs_tags(options)
|
|
365
|
+
when :pygments
|
|
366
|
+
# Pygments requires server-side processing, not implemented for client-side HTML
|
|
367
|
+
''
|
|
368
|
+
when :rouge
|
|
369
|
+
# Rouge requires server-side processing, not implemented for client-side HTML
|
|
370
|
+
''
|
|
371
|
+
else
|
|
372
|
+
''
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Build Highlight.js tags
|
|
377
|
+
# @param options [Hash] Configuration options
|
|
378
|
+
# @return [String] HTML tags for Highlight.js
|
|
379
|
+
def highlightjs_tags(options = {})
|
|
380
|
+
theme = options[:highlightjs_theme] || DEFAULT_OPTIONS[:highlightjs_theme]
|
|
381
|
+
tags = []
|
|
382
|
+
|
|
383
|
+
# Add Highlight.js library
|
|
384
|
+
tags << %(<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/#{theme}.min.css">)
|
|
385
|
+
tags << %(<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>)
|
|
386
|
+
tags << %(<script>hljs.highlightAll();</script>)
|
|
387
|
+
|
|
388
|
+
tags.join("\n")
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Get data attributes for code block
|
|
392
|
+
# @param language [String] Programming language
|
|
393
|
+
# @param options [Hash] Code block options
|
|
394
|
+
# @return [Hash] Data attributes
|
|
395
|
+
def code_block_attributes(language, options = {})
|
|
396
|
+
attrs = {}
|
|
397
|
+
|
|
398
|
+
attrs[:class] = "language-#{language}" if language && !language.empty?
|
|
399
|
+
|
|
400
|
+
if options[:linenums] || options[:line_numbers]
|
|
401
|
+
attrs[:class] =
|
|
402
|
+
[attrs[:class], 'line-numbers'].compact.join(' ')
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
attrs
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Mapping of Coradoc elements to HTML tags
|
|
409
|
+
TAG_MAPPING = {
|
|
410
|
+
# Sections
|
|
411
|
+
section: 'section',
|
|
412
|
+
header: 'header',
|
|
413
|
+
|
|
414
|
+
# Blocks
|
|
415
|
+
paragraph: 'p',
|
|
416
|
+
example: 'div',
|
|
417
|
+
sidebar: 'aside',
|
|
418
|
+
quote: 'blockquote',
|
|
419
|
+
verse: 'div',
|
|
420
|
+
listing: 'pre',
|
|
421
|
+
literal: 'pre',
|
|
422
|
+
source: 'pre',
|
|
423
|
+
open: 'div',
|
|
424
|
+
|
|
425
|
+
# Lists
|
|
426
|
+
ordered_list: 'ol',
|
|
427
|
+
unordered_list: 'ul',
|
|
428
|
+
list_item: 'li',
|
|
429
|
+
description_list: 'dl',
|
|
430
|
+
description_term: 'dt',
|
|
431
|
+
description_detail: 'dd',
|
|
432
|
+
|
|
433
|
+
# Tables
|
|
434
|
+
table: 'table',
|
|
435
|
+
table_row: 'tr',
|
|
436
|
+
table_cell: 'td',
|
|
437
|
+
table_header: 'th',
|
|
438
|
+
|
|
439
|
+
# Inline
|
|
440
|
+
bold: 'strong',
|
|
441
|
+
italic: 'em',
|
|
442
|
+
monospace: 'code',
|
|
443
|
+
highlight: 'mark',
|
|
444
|
+
superscript: 'sup',
|
|
445
|
+
subscript: 'sub',
|
|
446
|
+
underline: 'u',
|
|
447
|
+
strikethrough: 'del',
|
|
448
|
+
small_caps: 'span',
|
|
449
|
+
|
|
450
|
+
# Links
|
|
451
|
+
anchor: 'a',
|
|
452
|
+
cross_reference: 'a',
|
|
453
|
+
|
|
454
|
+
# Media
|
|
455
|
+
image: 'img',
|
|
456
|
+
video: 'video',
|
|
457
|
+
audio: 'audio',
|
|
458
|
+
|
|
459
|
+
# Other
|
|
460
|
+
break: 'hr',
|
|
461
|
+
line_break: 'br',
|
|
462
|
+
admonition: 'div'
|
|
463
|
+
}.freeze
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
end
|