repubmark 0.0.0
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/.rubocop.yml +46 -0
- data/.rubocop_todo.yml +129 -0
- data/.yardopts +4 -0
- data/LICENSE +21 -0
- data/README.md +21 -0
- data/Rakefile +60 -0
- data/examples/config.yml +71 -0
- data/examples/full.chapters.json +22 -0
- data/examples/full.gmi +44 -0
- data/examples/full.html +114 -0
- data/examples/full.repub +91 -0
- data/examples/full.summary.txt +2 -0
- data/examples/hello.chapters.json +3 -0
- data/examples/hello.gmi +1 -0
- data/examples/hello.html +5 -0
- data/examples/hello.repub +3 -0
- data/examples/hello.summary.txt +1 -0
- data/exe/repubmark +46 -0
- data/lib/repubmark/config.rb +53 -0
- data/lib/repubmark/elems/abbrev.rb +52 -0
- data/lib/repubmark/elems/annotation.rb +48 -0
- data/lib/repubmark/elems/article.rb +96 -0
- data/lib/repubmark/elems/base.rb +104 -0
- data/lib/repubmark/elems/blockquote.rb +80 -0
- data/lib/repubmark/elems/canvas.rb +104 -0
- data/lib/repubmark/elems/caption.rb +69 -0
- data/lib/repubmark/elems/chapter.rb +148 -0
- data/lib/repubmark/elems/code_block.rb +70 -0
- data/lib/repubmark/elems/code_inline.rb +35 -0
- data/lib/repubmark/elems/figure.rb +73 -0
- data/lib/repubmark/elems/figures.rb +47 -0
- data/lib/repubmark/elems/footnote.rb +87 -0
- data/lib/repubmark/elems/footnotes_category.rb +48 -0
- data/lib/repubmark/elems/fraction.rb +31 -0
- data/lib/repubmark/elems/iframe.rb +62 -0
- data/lib/repubmark/elems/joint.rb +176 -0
- data/lib/repubmark/elems/link.rb +56 -0
- data/lib/repubmark/elems/list.rb +84 -0
- data/lib/repubmark/elems/list_item.rb +147 -0
- data/lib/repubmark/elems/note.rb +40 -0
- data/lib/repubmark/elems/paragraph.rb +42 -0
- data/lib/repubmark/elems/power.rb +34 -0
- data/lib/repubmark/elems/quote.rb +53 -0
- data/lib/repubmark/elems/section.rb +58 -0
- data/lib/repubmark/elems/separator.rb +17 -0
- data/lib/repubmark/elems/special.rb +37 -0
- data/lib/repubmark/elems/text.rb +43 -0
- data/lib/repubmark/highlight.rb +85 -0
- data/lib/repubmark/titled_ref.rb +16 -0
- data/lib/repubmark/version.rb +6 -0
- data/lib/repubmark.rb +125 -0
- data/tests/examples.rb +51 -0
- metadata +129 -0
data/exe/repubmark
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path('../lib', __dir__).freeze
|
5
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
|
9
|
+
require 'i18n'
|
10
|
+
require 'repubmark'
|
11
|
+
|
12
|
+
I18n.load_path = Dir[
|
13
|
+
Pathname.new(__dir__).join('../vendor/rails-i18n/rails/locale/**/*.yml')
|
14
|
+
].each(&:freeze).freeze
|
15
|
+
|
16
|
+
config_filename = String(ARGV[0]).freeze
|
17
|
+
profile = String(ARGV[1]).freeze
|
18
|
+
|
19
|
+
config = Repubmark::Config.load_file config_filename, profile
|
20
|
+
$article = Repubmark::Elems::Article.new config
|
21
|
+
|
22
|
+
template = $stdin.read.freeze
|
23
|
+
context = BasicObject.new
|
24
|
+
def context.article = $article
|
25
|
+
context.instance_eval template, __FILE__, __LINE__
|
26
|
+
|
27
|
+
def wrap(str)
|
28
|
+
words = str.strip.split
|
29
|
+
lines = [[]]
|
30
|
+
words.each do |word|
|
31
|
+
lines << [] if [*lines.last, word].join(' ').length > 70
|
32
|
+
lines.last << word
|
33
|
+
end
|
34
|
+
lines.map { |line| "#{line.join(' ')}\n" }.join.freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
output =
|
38
|
+
case config.format
|
39
|
+
when :chapters then JSON.pretty_generate $article.chapters
|
40
|
+
when :gemtext then $article.to_gemtext
|
41
|
+
when :html then $article.to_html
|
42
|
+
when :summary_plain then wrap $article.to_summary_plain
|
43
|
+
when :word_count then $article.word_count
|
44
|
+
end.to_s.strip
|
45
|
+
|
46
|
+
puts output unless output.empty?
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
class Config
|
5
|
+
OPTIONAL_KEYS = %i[
|
6
|
+
base_url
|
7
|
+
chapter_anchor_prefix
|
8
|
+
css_class_annotation
|
9
|
+
css_class_blockquote_figure
|
10
|
+
css_class_blockquote_blockquote
|
11
|
+
css_class_blockquote_figcaption
|
12
|
+
css_class_figure_self
|
13
|
+
css_class_figure_wrap
|
14
|
+
css_class_figures_left
|
15
|
+
css_class_figures_right
|
16
|
+
css_class_figures_wrap
|
17
|
+
css_class_iframe_wrap
|
18
|
+
current_path
|
19
|
+
images_prefix
|
20
|
+
relative_urls
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
attr_reader :format
|
24
|
+
|
25
|
+
def self.load_file(filename, profile)
|
26
|
+
config_data = YAML.safe_load_file filename, aliases: true
|
27
|
+
profile_data = config_data[profile]
|
28
|
+
raise 'Unknown profile' if profile_data.nil?
|
29
|
+
|
30
|
+
new(**profile_data.transform_keys(&:to_sym))
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(format:, **kwargs)
|
34
|
+
self.format = format
|
35
|
+
raise unless (kwargs.keys - OPTIONAL_KEYS).empty?
|
36
|
+
|
37
|
+
@kwargs = kwargs.freeze
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](key)
|
41
|
+
OPTIONAL_KEYS.include?(key) ? @kwargs[key] : @kwargs.fetch(key)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def format=(format)
|
47
|
+
format = format.to_sym if format.instance_of? String
|
48
|
+
raise 'Invalid format' unless FORMATS.include? format
|
49
|
+
|
50
|
+
@format = format
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Abbrev < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
attr_reader :abbrev, :transcript
|
9
|
+
|
10
|
+
def initialize(parent, abbrev, transcript)
|
11
|
+
super parent
|
12
|
+
|
13
|
+
self.abbrev = abbrev
|
14
|
+
self.transcript = transcript
|
15
|
+
end
|
16
|
+
|
17
|
+
#################
|
18
|
+
# Basic methods #
|
19
|
+
#################
|
20
|
+
|
21
|
+
def word_count = 1
|
22
|
+
|
23
|
+
def to_summary_plain = abbrev
|
24
|
+
|
25
|
+
def to_html
|
26
|
+
%(<abbr title="#{escape_transcript}">#{escape_abbrev}</abbr>).freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_gemtext = abbrev
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def abbrev=(abbrev)
|
34
|
+
@abbrev =
|
35
|
+
String(abbrev).split.join(' ').freeze.tap do |new_abbrev|
|
36
|
+
raise 'Empty string' if new_abbrev.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def transcript=(transcript)
|
41
|
+
@transcript =
|
42
|
+
String(transcript).split.join(' ').freeze.tap do |new_transcript|
|
43
|
+
raise 'Empty string' if new_transcript.empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def escape_abbrev = CGI.escape_html(abbrev).freeze
|
48
|
+
|
49
|
+
def escape_transcript = CGI.escape_html(transcript).freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Annotation < Base
|
6
|
+
parents :Article
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
@canvas = Canvas.new self
|
11
|
+
end
|
12
|
+
|
13
|
+
#################
|
14
|
+
# Basic methods #
|
15
|
+
#################
|
16
|
+
|
17
|
+
def word_count = @canvas.word_count
|
18
|
+
|
19
|
+
def to_summary_plain = @canvas.to_summary_plain
|
20
|
+
|
21
|
+
def to_html
|
22
|
+
[
|
23
|
+
%(<div#{html_class(:annotation)}>\n),
|
24
|
+
@canvas.to_html,
|
25
|
+
"</div>\n",
|
26
|
+
].join.freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_gemtext = @canvas.to_gemtext
|
30
|
+
|
31
|
+
###################
|
32
|
+
# Builder methods #
|
33
|
+
###################
|
34
|
+
|
35
|
+
def respond_to_missing?(method_name, _include_private)
|
36
|
+
@canvas.respond_to?(method_name) || super
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(method_name, ...)
|
40
|
+
if @canvas.respond_to? method_name
|
41
|
+
@canvas.public_send(method_name, ...)
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Article < Base
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config) # rubocop:disable Lint/MissingSuper
|
9
|
+
@parent = nil
|
10
|
+
self.config = config
|
11
|
+
@footnotes_categories = []
|
12
|
+
end
|
13
|
+
|
14
|
+
#################
|
15
|
+
# Basic methods #
|
16
|
+
#################
|
17
|
+
|
18
|
+
def word_count
|
19
|
+
(@annotation&.word_count || 0) +
|
20
|
+
(@chapter&.word_count || 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_summary_plain
|
24
|
+
[
|
25
|
+
@annotation&.to_summary_plain,
|
26
|
+
@chapter&.to_summary_plain,
|
27
|
+
].compact.join(' ').freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_html
|
31
|
+
[
|
32
|
+
@annotation&.to_html,
|
33
|
+
@chapter&.to_html,
|
34
|
+
@footnotes_categories.any? ? "<div>\n<hr/>\n" : nil,
|
35
|
+
*@footnotes_categories.map(&:to_html),
|
36
|
+
@footnotes_categories.any? ? "</div>\n" : nil,
|
37
|
+
].compact.join.freeze
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_gemtext
|
41
|
+
[
|
42
|
+
@annotation&.to_gemtext,
|
43
|
+
@chapter&.to_gemtext,
|
44
|
+
*@footnotes_categories.map(&:to_gemtext),
|
45
|
+
].compact.join("\n\n\n").freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
def chapters = @chapter&.chapters[:chapters] || [].freeze
|
49
|
+
|
50
|
+
###################
|
51
|
+
# Builder methods #
|
52
|
+
###################
|
53
|
+
|
54
|
+
def annotation
|
55
|
+
raise 'Annotation already exists' if @annotation
|
56
|
+
raise 'Annotation after chapters' if @chapter
|
57
|
+
|
58
|
+
@annotation = Annotation.new self
|
59
|
+
yield @annotation
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def respond_to_missing?(method_name, _include_private)
|
64
|
+
chapter = @chapter || Chapter.new(self)
|
65
|
+
chapter.respond_to?(method_name) || super
|
66
|
+
end
|
67
|
+
|
68
|
+
def method_missing(method_name, ...)
|
69
|
+
chapter = @chapter || Chapter.new(self)
|
70
|
+
if chapter.respond_to? method_name
|
71
|
+
@chapter = chapter
|
72
|
+
@chapter.public_send(method_name, ...)
|
73
|
+
else
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def footnotes_category(name)
|
79
|
+
footnotes_category = FootnotesCategory.new self, name
|
80
|
+
@footnotes_categories << footnotes_category
|
81
|
+
yield footnotes_category
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def config=(config)
|
88
|
+
unless config.instance_of? Config
|
89
|
+
raise TypeError, "Expected #{Config}, got #{config.class}"
|
90
|
+
end
|
91
|
+
|
92
|
+
@config = config
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Base
|
6
|
+
attr_reader :parent
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
self.parent = parent
|
10
|
+
end
|
11
|
+
|
12
|
+
#################
|
13
|
+
# Basic methods #
|
14
|
+
#################
|
15
|
+
|
16
|
+
def word_count = 0
|
17
|
+
|
18
|
+
def to_summary_plain = nil
|
19
|
+
|
20
|
+
def to_html
|
21
|
+
raise NotImplementedError, "#{self.class}#to_html"
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_gemtext
|
25
|
+
raise NotImplementedError, "#{self.class}#to_gemtext"
|
26
|
+
end
|
27
|
+
|
28
|
+
##################
|
29
|
+
# Helper methods #
|
30
|
+
##################
|
31
|
+
|
32
|
+
def config = parent.config
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def parents(*args)
|
36
|
+
if @parents
|
37
|
+
raise ArgumentError, 'Invalid args' unless args.empty?
|
38
|
+
|
39
|
+
return @parents
|
40
|
+
end
|
41
|
+
|
42
|
+
@parents = args.map { |arg| "#{Elems}::#{arg}".freeze }.to_a.freeze
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def parent?(klass)
|
47
|
+
unless klass.instance_of? Class
|
48
|
+
raise TypeError, "Expected #{Class}, got #{klass.class}"
|
49
|
+
end
|
50
|
+
|
51
|
+
parents.include? klass.name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def parent=(parent)
|
58
|
+
unless parent.is_a? Base
|
59
|
+
raise TypeError, "Expected #{Base}, got #{parent.class}"
|
60
|
+
end
|
61
|
+
|
62
|
+
unless self.class.parent? parent.class
|
63
|
+
raise TypeError,
|
64
|
+
"Expected #{self.class.parents.join(', ')}, got #{parent.class}"
|
65
|
+
end
|
66
|
+
|
67
|
+
@parent = parent
|
68
|
+
end
|
69
|
+
|
70
|
+
def own_url(path)
|
71
|
+
config[:relative_urls] ? relative_url(path) : absolute_url(path)
|
72
|
+
end
|
73
|
+
|
74
|
+
def absolute_url(path)
|
75
|
+
base_url = String(config[:base_url]).strip.freeze
|
76
|
+
raise 'Invalid base URL' if base_url.empty?
|
77
|
+
|
78
|
+
path = "/#{path}" unless path.start_with? '/'
|
79
|
+
"#{base_url}#{path}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def relative_url(path)
|
83
|
+
current_path = String(config[:current_path]).strip.freeze
|
84
|
+
raise 'Invalid current path URL' if current_path.empty?
|
85
|
+
|
86
|
+
Pathname
|
87
|
+
.new(path)
|
88
|
+
.relative_path_from("/#{current_path}/..")
|
89
|
+
.to_s
|
90
|
+
.freeze
|
91
|
+
end
|
92
|
+
|
93
|
+
def html_class(key)
|
94
|
+
if (value = config[:"css_class_#{key}"])
|
95
|
+
%( class="#{value}").freeze
|
96
|
+
else
|
97
|
+
''
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def count_words(str) = str.split.count
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Blockquote < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
@canvas = Canvas.new self
|
12
|
+
@caption = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
#################
|
16
|
+
# Basic methods #
|
17
|
+
#################
|
18
|
+
|
19
|
+
def word_count = @canvas.word_count + (@caption&.word_count || 0)
|
20
|
+
|
21
|
+
def to_html
|
22
|
+
[
|
23
|
+
"<figure#{html_class(:blockquote_figure)}>\n",
|
24
|
+
"<blockquote#{html_class(:blockquote_blockquote)}>\n",
|
25
|
+
@canvas.to_html,
|
26
|
+
"</blockquote>\n",
|
27
|
+
caption_html,
|
28
|
+
"</figure>\n",
|
29
|
+
].join.freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_gemtext
|
33
|
+
[
|
34
|
+
@canvas.to_gemtext.split("\n").map { |s| "> #{s}\n" }.join,
|
35
|
+
caption_gemtext,
|
36
|
+
].join.freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
###################
|
40
|
+
# Builder methods #
|
41
|
+
###################
|
42
|
+
|
43
|
+
def caption
|
44
|
+
@caption = Caption.new self
|
45
|
+
yield @caption
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(method_name, _include_private)
|
50
|
+
@canvas.respond_to?(method_name) || super
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing(method_name, ...)
|
54
|
+
if @canvas.respond_to? method_name
|
55
|
+
raise 'Paragraph after caption' unless @caption.nil?
|
56
|
+
|
57
|
+
@canvas.public_send(method_name, ...)
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def caption_html
|
66
|
+
return if @caption.nil?
|
67
|
+
|
68
|
+
[
|
69
|
+
"<figcaption#{html_class(:blockquote_figcaption)}>\n",
|
70
|
+
@caption.to_html,
|
71
|
+
"</figcaption>\n",
|
72
|
+
].join.freeze
|
73
|
+
end
|
74
|
+
|
75
|
+
def caption_gemtext
|
76
|
+
"#{@caption.to_gemtext}\n".freeze unless @caption.nil?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Canvas < Base
|
6
|
+
parents :Annotation, :Blockquote, :Chapter
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
@items = []
|
11
|
+
end
|
12
|
+
|
13
|
+
#################
|
14
|
+
# Basic methods #
|
15
|
+
#################
|
16
|
+
|
17
|
+
def word_count = @items.sum(&:word_count)
|
18
|
+
|
19
|
+
def to_summary_plain
|
20
|
+
return if @items.empty?
|
21
|
+
|
22
|
+
@items.filter_map(&:to_summary_plain).join(' ').freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_html
|
26
|
+
@items.map(&:to_html).join.freeze unless @items.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_gemtext
|
30
|
+
@items.map(&:to_gemtext).join("\n").freeze unless @items.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
###################
|
34
|
+
# Builder methods #
|
35
|
+
###################
|
36
|
+
|
37
|
+
def blockquote
|
38
|
+
blockquote = Blockquote.new self
|
39
|
+
@items << blockquote
|
40
|
+
yield blockquote
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def code_block(*args, **kwargs)
|
45
|
+
code_block = CodeBlock.new self, *args, **kwargs
|
46
|
+
@items << code_block
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def iframe(*args, **kwargs)
|
51
|
+
iframe = Iframe.new self, *args, **kwargs
|
52
|
+
@items << iframe
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def links_list
|
57
|
+
list = List.new self, links: true, ordered: false
|
58
|
+
@items << list
|
59
|
+
yield list
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def nice_figure(name, alt)
|
64
|
+
figure = Figure.new self, name, alt
|
65
|
+
@items << figure
|
66
|
+
yield figure if block_given?
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def nice_figures
|
71
|
+
figures = Figures.new self
|
72
|
+
@items << figures
|
73
|
+
yield figures
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def olist
|
78
|
+
list = List.new self, links: false, ordered: true
|
79
|
+
@items << list
|
80
|
+
yield list
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def paragraph
|
85
|
+
paragraph = Paragraph.new self
|
86
|
+
@items << paragraph
|
87
|
+
yield paragraph
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def separator
|
92
|
+
@items << Separator.new(self)
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def ulist
|
97
|
+
list = List.new self, links: false, ordered: false
|
98
|
+
@items << list
|
99
|
+
yield list
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Caption < Base
|
6
|
+
include Joint::ForwardingBuilders
|
7
|
+
|
8
|
+
parents :Blockquote, :Figure, :Footnote, :ListItem, :Paragraph
|
9
|
+
|
10
|
+
def initialize(parent)
|
11
|
+
super parent
|
12
|
+
@items = []
|
13
|
+
end
|
14
|
+
|
15
|
+
#################
|
16
|
+
# Basic methods #
|
17
|
+
#################
|
18
|
+
|
19
|
+
def word_count = @items.sum(&:word_count)
|
20
|
+
|
21
|
+
def to_summary_plain
|
22
|
+
@items.map(&:to_summary_plain).join(' ').freeze unless @items.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_html
|
26
|
+
return if @items.empty?
|
27
|
+
|
28
|
+
[
|
29
|
+
'<span>',
|
30
|
+
*@items.map(&:to_html),
|
31
|
+
'</span>',
|
32
|
+
].map { |s| "#{s}\n" }.join.freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_gemtext
|
36
|
+
@items.map(&:to_gemtext).join(' ').strip.freeze unless @items.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
##################
|
40
|
+
# Helper methods #
|
41
|
+
##################
|
42
|
+
|
43
|
+
def empty? = @items.empty?
|
44
|
+
|
45
|
+
###################
|
46
|
+
# Builder methods #
|
47
|
+
###################
|
48
|
+
|
49
|
+
def joint
|
50
|
+
joint = Joint.new self
|
51
|
+
yield joint
|
52
|
+
@items << joint
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def quote(str = nil)
|
57
|
+
quote = Quote.new self
|
58
|
+
case [!!str, block_given?]
|
59
|
+
when [true, false] then quote.text str
|
60
|
+
when [false, true] then yield quote
|
61
|
+
else
|
62
|
+
raise 'Invalid args'
|
63
|
+
end
|
64
|
+
@items << quote
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|