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
@@ -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
|