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
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Joint < Base
|
6
|
+
parents :Caption, :Quote
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
@raw1 = nil
|
12
|
+
@base = nil
|
13
|
+
@raw2 = nil
|
14
|
+
@notes = []
|
15
|
+
@raw3 = nil
|
16
|
+
|
17
|
+
@context = nil
|
18
|
+
@context_note = false
|
19
|
+
end
|
20
|
+
|
21
|
+
#################
|
22
|
+
# Basic methods #
|
23
|
+
#################
|
24
|
+
|
25
|
+
def word_count = components.sum(&:word_count)
|
26
|
+
|
27
|
+
def to_summary_plain = components.map(&:to_summary_plain).join.freeze
|
28
|
+
|
29
|
+
def to_html = components.map(&:to_html).join.freeze
|
30
|
+
|
31
|
+
def to_gemtext = components.map(&:to_gemtext).join.freeze
|
32
|
+
|
33
|
+
########################
|
34
|
+
# Builder methods: Raw #
|
35
|
+
########################
|
36
|
+
|
37
|
+
def raw(str)
|
38
|
+
text = Text.new self, str
|
39
|
+
if @notes.any?
|
40
|
+
raise 'Joint raw text already exists' if @raw3
|
41
|
+
|
42
|
+
@raw3 = text
|
43
|
+
elsif @base
|
44
|
+
raise 'Joint raw text already exists' if @raw2
|
45
|
+
|
46
|
+
@raw2 = text
|
47
|
+
else
|
48
|
+
raise 'Joint raw text already exists' if @raw1
|
49
|
+
|
50
|
+
@raw1 = text
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#########################
|
55
|
+
# Builder methods: Base #
|
56
|
+
#########################
|
57
|
+
|
58
|
+
def abbrev(abbrev, transcript) = base Abbrev.new self, abbrev, transcript
|
59
|
+
|
60
|
+
def bold(str) = base Text.new self, str, bold: true
|
61
|
+
|
62
|
+
def code_inline(str) = base CodeInline.new self, str
|
63
|
+
|
64
|
+
def ellipsis = base Special.new self, :ellipsis
|
65
|
+
|
66
|
+
def fraction(top, bottom) = base Fraction.new self, top, bottom
|
67
|
+
|
68
|
+
def italic(str) = base Text.new self, str, italic: true
|
69
|
+
|
70
|
+
def link(text, uri) = base Link.new self, text, uri
|
71
|
+
|
72
|
+
def link_italic(text, uri) = base Link.new self, text, uri, italic: true
|
73
|
+
|
74
|
+
def mdash = base Special.new self, :mdash
|
75
|
+
|
76
|
+
def power(base, exponent) = base Power.new self, base, exponent
|
77
|
+
|
78
|
+
def quote(str = nil)
|
79
|
+
quote = Quote.new self
|
80
|
+
case [!!str, block_given?]
|
81
|
+
when [true, false] then quote.text str
|
82
|
+
when [false, true] then yield quote
|
83
|
+
else
|
84
|
+
raise 'Invalid args'
|
85
|
+
end
|
86
|
+
base quote
|
87
|
+
end
|
88
|
+
|
89
|
+
def quote_italic(str)
|
90
|
+
quote = Quote.new self
|
91
|
+
quote.italic str
|
92
|
+
base quote
|
93
|
+
end
|
94
|
+
|
95
|
+
def section(*args) = base Section.new self, *args
|
96
|
+
|
97
|
+
def text(str) = base Text.new self, str
|
98
|
+
|
99
|
+
##########################
|
100
|
+
# Builder methods: Notes #
|
101
|
+
##########################
|
102
|
+
|
103
|
+
def context(index, category, slug)
|
104
|
+
raise 'Context already given' if @context
|
105
|
+
|
106
|
+
@context = [index, category, slug]
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def context_note
|
111
|
+
raise 'No context given' if @context.nil?
|
112
|
+
raise 'Context already noted' if @context_note
|
113
|
+
|
114
|
+
@context_note = true
|
115
|
+
ref_note(*@context)
|
116
|
+
end
|
117
|
+
|
118
|
+
def ref_note(index, category, slug)
|
119
|
+
note Note.new self, index, category, slug
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def components
|
125
|
+
raise 'No context note' if @context && !@context_note
|
126
|
+
|
127
|
+
[@raw1, @base, @raw2, *@notes, @raw3].compact
|
128
|
+
end
|
129
|
+
|
130
|
+
def base(elem)
|
131
|
+
raise 'Joint base already exists' if @base
|
132
|
+
raise 'Joint notes already exist' if @notes.any?
|
133
|
+
|
134
|
+
@base = elem
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def note(elem)
|
139
|
+
raise 'Joint base does not exists' if @base.nil?
|
140
|
+
|
141
|
+
@notes << elem
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
|
145
|
+
module ForwardingBuilders
|
146
|
+
def joint = raise NotImplementedError, "#{self.class}#joint"
|
147
|
+
|
148
|
+
def abbrev(*args) = joint { |joint| joint.abbrev(*args) }
|
149
|
+
|
150
|
+
def bold(*args) = joint { |joint| joint.bold(*args) }
|
151
|
+
|
152
|
+
def code_inline(*args) = joint { |joint| joint.code_inline(*args) }
|
153
|
+
|
154
|
+
def ellipsis(*args) = joint { |joint| joint.ellipsis(*args) }
|
155
|
+
|
156
|
+
def fraction(*args) = joint { |joint| joint.fraction(*args) }
|
157
|
+
|
158
|
+
def italic(*args) = joint { |joint| joint.italic(*args) }
|
159
|
+
|
160
|
+
def link(*args) = joint { |joint| joint.link(*args) }
|
161
|
+
|
162
|
+
def link_italic(*args) = joint { |joint| joint.link_italic(*args) }
|
163
|
+
|
164
|
+
def mdash(*args) = joint { |joint| joint.mdash(*args) }
|
165
|
+
|
166
|
+
def power(*args) = joint { |joint| joint.power(*args) }
|
167
|
+
|
168
|
+
def quote_italic(*args) = joint { |joint| joint.quote_italic(*args) }
|
169
|
+
|
170
|
+
def section(*args) = joint { |joint| joint.section(*args) }
|
171
|
+
|
172
|
+
def text(*args) = joint { |joint| joint.text(*args) }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Link < Text
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
SCHEMES = %w[http https].freeze
|
9
|
+
|
10
|
+
def initialize(parent, str, uri, **kwargs)
|
11
|
+
super parent, str, **kwargs
|
12
|
+
|
13
|
+
self.uri = uri
|
14
|
+
validate_uri!
|
15
|
+
end
|
16
|
+
|
17
|
+
#################
|
18
|
+
# Basic methods #
|
19
|
+
#################
|
20
|
+
|
21
|
+
def to_html
|
22
|
+
"<a href=\"#{build_uri('.html')}\">#{str_to_html}</a>".freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :uri
|
28
|
+
|
29
|
+
def uri=(uri)
|
30
|
+
@uri = URI.parse(uri).freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_uri!
|
34
|
+
raise 'Expected normalized URI' unless uri.normalize == uri
|
35
|
+
raise 'Expected no userinfo' unless uri.userinfo.nil?
|
36
|
+
|
37
|
+
validate_uri_http_absolute!
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_uri_http_absolute!
|
41
|
+
return unless uri.is_a?(URI::HTTP) && uri.absolute?
|
42
|
+
|
43
|
+
raise 'Invalid scheme' unless SCHEMES.include? uri.scheme
|
44
|
+
raise 'Expected hostname' unless uri.hostname
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_uri(ext)
|
48
|
+
if uri.is_a?(URI::MailTo) || uri.absolute?
|
49
|
+
uri
|
50
|
+
else
|
51
|
+
own_url "#{uri}#{ext}"
|
52
|
+
end.to_s.freeze
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class List < Base
|
6
|
+
parents :Canvas, :ListItem
|
7
|
+
|
8
|
+
def initialize(parent, links:, ordered:)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
@links = !!links
|
12
|
+
@ordered = !!ordered
|
13
|
+
|
14
|
+
@items = []
|
15
|
+
end
|
16
|
+
|
17
|
+
#################
|
18
|
+
# Basic methods #
|
19
|
+
#################
|
20
|
+
|
21
|
+
def word_count = @items.sum(&:word_count)
|
22
|
+
|
23
|
+
def to_summary_plain = @items.map(&:to_summary_plain).join(' ').freeze
|
24
|
+
|
25
|
+
def to_html
|
26
|
+
[
|
27
|
+
"<#{tag_name}>\n",
|
28
|
+
*@items.map(&:to_html),
|
29
|
+
"</#{tag_name}>\n",
|
30
|
+
].join.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_gemtext = @items.map(&:to_gemtext).join.freeze
|
34
|
+
|
35
|
+
##################
|
36
|
+
# Helper methods #
|
37
|
+
##################
|
38
|
+
|
39
|
+
def level = parent.instance_of?(ListItem) ? parent.level + 1 : 0
|
40
|
+
|
41
|
+
def items_count = @items.count
|
42
|
+
|
43
|
+
def unicode_decor
|
44
|
+
return if level <= 1
|
45
|
+
|
46
|
+
"#{parent.unicode_decor_parent}#{parent.last? ? '⠀ ' : '│ '}"
|
47
|
+
end
|
48
|
+
|
49
|
+
###################
|
50
|
+
# Builder methods #
|
51
|
+
###################
|
52
|
+
|
53
|
+
def item(...) = @links ? item_links(...) : item_nolinks(...)
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def parent=(parent)
|
58
|
+
unless parent.instance_of?(Canvas) || parent.instance_of?(ListItem)
|
59
|
+
raise TypeError,
|
60
|
+
"Expected #{Canvas} or #{ListItem}, got #{parent.class}"
|
61
|
+
end
|
62
|
+
|
63
|
+
@parent = parent
|
64
|
+
end
|
65
|
+
|
66
|
+
def tag_name = @ordered ? 'ol' : 'ul'
|
67
|
+
|
68
|
+
def item_nolinks
|
69
|
+
list_item = ListItem.new self, @items.count
|
70
|
+
@items << list_item
|
71
|
+
yield list_item
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def item_links(ref_url, ref_title = nil)
|
76
|
+
titled_ref = TitledRef.new ref_url, ref_title
|
77
|
+
list_item = ListItem.new self, @items.count, titled_ref
|
78
|
+
@items << list_item
|
79
|
+
yield list_item if block_given?
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class ListItem < Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
parents :List
|
9
|
+
|
10
|
+
attr_reader :index, :titled_ref
|
11
|
+
|
12
|
+
def initialize(parent, index, titled_ref = nil)
|
13
|
+
super parent
|
14
|
+
|
15
|
+
self.index = index
|
16
|
+
self.titled_ref = titled_ref
|
17
|
+
|
18
|
+
@caption = Caption.new self
|
19
|
+
@sublist = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
#################
|
23
|
+
# Basic methods #
|
24
|
+
#################
|
25
|
+
|
26
|
+
def word_count = @caption.word_count + (@sublist&.word_count || 0)
|
27
|
+
|
28
|
+
def to_summary_plain
|
29
|
+
[
|
30
|
+
@caption.to_summary_plain,
|
31
|
+
@sublist&.to_summary_plain,
|
32
|
+
].compact.join("\n").freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_html
|
36
|
+
[
|
37
|
+
"<li>\n",
|
38
|
+
build_ref(:html),
|
39
|
+
@caption.to_html,
|
40
|
+
@sublist&.to_html,
|
41
|
+
"</li>\n",
|
42
|
+
].join.freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_gemtext
|
46
|
+
[
|
47
|
+
if titled_ref
|
48
|
+
"#{build_ref(:gemtext)}#{@caption.to_gemtext}".strip
|
49
|
+
else
|
50
|
+
"* #{unicode_decor_own}#{@caption.to_gemtext}".strip
|
51
|
+
end,
|
52
|
+
@sublist&.to_gemtext,
|
53
|
+
].join("\n").freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
##################
|
57
|
+
# Helper methods #
|
58
|
+
##################
|
59
|
+
|
60
|
+
def level = parent.level
|
61
|
+
|
62
|
+
def last? = index >= parent.items_count - 1
|
63
|
+
|
64
|
+
def unicode_decor_own
|
65
|
+
"#{parent.unicode_decor}#{last? ? '└' : '├'} " unless level.zero?
|
66
|
+
end
|
67
|
+
|
68
|
+
def unicode_decor_parent = parent.unicode_decor
|
69
|
+
|
70
|
+
###################
|
71
|
+
# Builder methods #
|
72
|
+
###################
|
73
|
+
|
74
|
+
def subolist
|
75
|
+
raise 'No nested link lists' if titled_ref
|
76
|
+
raise 'Sublist already exists' if @sublist
|
77
|
+
|
78
|
+
@sublist = List.new self, links: false, ordered: true
|
79
|
+
yield @sublist
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def subulist
|
84
|
+
raise 'No nested link lists' if titled_ref
|
85
|
+
raise 'Sublist already exists' if @sublist
|
86
|
+
|
87
|
+
@sublist = List.new self, links: false, ordered: false
|
88
|
+
yield @sublist
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def respond_to_missing?(method_name, _include_private)
|
93
|
+
@caption.respond_to?(method_name) || super
|
94
|
+
end
|
95
|
+
|
96
|
+
def method_missing(method_name, ...)
|
97
|
+
if @caption.respond_to? method_name
|
98
|
+
raise 'Caption after sublist' if @sublist
|
99
|
+
|
100
|
+
@caption.public_send(method_name, ...)
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def parent=(parent)
|
109
|
+
unless parent.instance_of? List
|
110
|
+
raise TypeError, "Expected #{List}, got #{parent.class}"
|
111
|
+
end
|
112
|
+
|
113
|
+
@parent = parent
|
114
|
+
end
|
115
|
+
|
116
|
+
def index=(index)
|
117
|
+
index = Integer index
|
118
|
+
raise 'Invalid index' if index.negative?
|
119
|
+
|
120
|
+
@index = index
|
121
|
+
end
|
122
|
+
|
123
|
+
def titled_ref=(titled_ref)
|
124
|
+
return @titled_ref = nil if titled_ref.nil?
|
125
|
+
|
126
|
+
unless titled_ref.instance_of? TitledRef
|
127
|
+
raise TypeError, "Expected #{TitledRef}, got #{titled_ref.class}"
|
128
|
+
end
|
129
|
+
|
130
|
+
@titled_ref = titled_ref
|
131
|
+
end
|
132
|
+
|
133
|
+
def build_ref(format)
|
134
|
+
return if titled_ref.nil?
|
135
|
+
|
136
|
+
case format
|
137
|
+
when :html
|
138
|
+
"<a href=\"#{titled_ref.url}\">#{titled_ref.title}</a>\n".freeze
|
139
|
+
when :gemtext
|
140
|
+
"=> #{titled_ref.url} #{unicode_decor_own}#{titled_ref.title} ".freeze
|
141
|
+
else
|
142
|
+
raise 'Invalid format'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Note < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
def initialize(parent, index, category, slug)
|
9
|
+
super parent
|
10
|
+
@index = index
|
11
|
+
@category = category
|
12
|
+
@slug = slug
|
13
|
+
end
|
14
|
+
|
15
|
+
#################
|
16
|
+
# Basic methods #
|
17
|
+
#################
|
18
|
+
|
19
|
+
def to_html
|
20
|
+
[
|
21
|
+
'<sup>',
|
22
|
+
%(<a href="##{CGI.escape_html(anchor)}">),
|
23
|
+
%([#{CGI.escape_html(@index.to_s)}]),
|
24
|
+
'</a>',
|
25
|
+
'</sup>',
|
26
|
+
].join.freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_gemtext = "⁽#{index_unicode_sup}⁾".freeze
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def anchor = "#@category-#@slug"
|
34
|
+
|
35
|
+
def index_unicode_sup
|
36
|
+
@index_unicode_sup ||= Repubmark.unicode_sup @index
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Paragraph < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
@caption = Caption.new self
|
11
|
+
end
|
12
|
+
|
13
|
+
#################
|
14
|
+
# Basic methods #
|
15
|
+
#################
|
16
|
+
|
17
|
+
def word_count = @caption.word_count
|
18
|
+
|
19
|
+
def to_summary_plain = @caption.to_summary_plain
|
20
|
+
|
21
|
+
def to_html = "<p>\n#{@caption.to_html}</p>\n".freeze
|
22
|
+
|
23
|
+
def to_gemtext = "#{@caption.to_gemtext}\n".freeze
|
24
|
+
|
25
|
+
###################
|
26
|
+
# Builder methods #
|
27
|
+
###################
|
28
|
+
|
29
|
+
def respond_to_missing?(method_name, _include_private)
|
30
|
+
@caption.respond_to?(method_name) || super
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method_name, ...)
|
34
|
+
if @caption.respond_to?(method_name)
|
35
|
+
@caption.public_send(method_name, ...)
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Power < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
def initialize(parent, base, exponent)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
@base = Integer base
|
12
|
+
@exponent = Integer exponent
|
13
|
+
end
|
14
|
+
|
15
|
+
#################
|
16
|
+
# Basic methods #
|
17
|
+
#################
|
18
|
+
|
19
|
+
def word_count = 1
|
20
|
+
|
21
|
+
def to_summary_plain = "#@base#{unicode_exponent}".freeze
|
22
|
+
|
23
|
+
def to_html = "#@base<sup>#@exponent</sup>".freeze
|
24
|
+
|
25
|
+
def to_gemtext = "#@base#{unicode_exponent}".freeze
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def unicode_exponent
|
30
|
+
@unicode_exponent ||= Repubmark.unicode_sup @exponent
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Quote < Base
|
6
|
+
include Joint::ForwardingBuilders
|
7
|
+
|
8
|
+
parents :Caption, :Joint, :Quote
|
9
|
+
|
10
|
+
def initialize(...)
|
11
|
+
super
|
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
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_html = "«#{@items.map(&:to_html).join("\n")}»".freeze
|
26
|
+
|
27
|
+
def to_gemtext = "«#{@items.map(&:to_gemtext).join(' ')}»".freeze
|
28
|
+
|
29
|
+
###################
|
30
|
+
# Builder methods #
|
31
|
+
###################
|
32
|
+
|
33
|
+
def joint
|
34
|
+
joint = Joint.new self
|
35
|
+
yield joint
|
36
|
+
@items << joint
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def quote(str = nil)
|
41
|
+
quote = Quote.new self
|
42
|
+
case [!!str, block_given?]
|
43
|
+
when [true, false] then quote.text str
|
44
|
+
when [false, true] then yield quote
|
45
|
+
else
|
46
|
+
raise 'Invalid args'
|
47
|
+
end
|
48
|
+
@items << quote
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Section < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
SECT_HTML = '§'
|
9
|
+
SECT_UNICODE = '§'
|
10
|
+
|
11
|
+
attr_reader :count, :text
|
12
|
+
|
13
|
+
def initialize(parent, *args)
|
14
|
+
super parent
|
15
|
+
|
16
|
+
case args.length
|
17
|
+
when 1
|
18
|
+
self.count = 1
|
19
|
+
self.text = args[0]
|
20
|
+
when 2
|
21
|
+
self.count, self.text = args
|
22
|
+
else
|
23
|
+
raise ArgumentError, 'Expected 1 or 2 arguments'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#################
|
28
|
+
# Basic methods #
|
29
|
+
#################
|
30
|
+
|
31
|
+
def word_count = count_words @text
|
32
|
+
|
33
|
+
def to_summary_plain = "#{SECT_UNICODE * count}#{text}".freeze
|
34
|
+
|
35
|
+
def to_html = "#{SECT_HTML * count}#{text}".freeze
|
36
|
+
|
37
|
+
def to_gemtext = "#{SECT_UNICODE * count}#{text}".freeze
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def count=(count)
|
42
|
+
unless count.instance_of? Integer
|
43
|
+
raise TypeError, "Expected #{Integer}, got #{count.class}"
|
44
|
+
end
|
45
|
+
raise 'Expected positive count' unless count.positive?
|
46
|
+
|
47
|
+
@count = count
|
48
|
+
end
|
49
|
+
|
50
|
+
def text=(text)
|
51
|
+
text = String(text).strip.freeze
|
52
|
+
raise 'Expected non-empty text' if text.empty?
|
53
|
+
|
54
|
+
@text = text
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Separator < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
#################
|
9
|
+
# Basic methods #
|
10
|
+
#################
|
11
|
+
|
12
|
+
def to_html = "<hr/>\n"
|
13
|
+
|
14
|
+
def to_gemtext = "---\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|