repubmark 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|