markita 1.0.210828 → 3.0.210912
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 +4 -4
- data/README.md +245 -60
- data/data/emojis.tsv +1874 -0
- data/lib/markita/base.rb +1 -91
- data/lib/markita/config.rb +2 -0
- data/lib/markita/html.rb +21 -0
- data/lib/markita/markdown.rb +435 -0
- data/lib/markita/plug/about.rb +4 -2
- data/lib/markita/preprocess.rb +30 -0
- data/lib/markita.rb +5 -3
- metadata +19 -16
data/lib/markita/base.rb
CHANGED
@@ -18,100 +18,10 @@ class Base < Sinatra::Base
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def Base.header(key)
|
22
|
-
<<-HEADER
|
23
|
-
<!DOCTYPE html>
|
24
|
-
<html>
|
25
|
-
<head>
|
26
|
-
<title>#{key}</title>
|
27
|
-
#{HEADER_LINKS}</head>
|
28
|
-
<body>
|
29
|
-
|
30
|
-
HEADER
|
31
|
-
end
|
32
|
-
|
33
|
-
def Base.footer
|
34
|
-
<<-FOOTER
|
35
|
-
|
36
|
-
</body>
|
37
|
-
</html>
|
38
|
-
FOOTER
|
39
|
-
end
|
40
|
-
|
41
|
-
def Base.pre_process(text)
|
42
|
-
val,string,_ = {},'',nil
|
43
|
-
text.each_line do |line|
|
44
|
-
line.chomp!
|
45
|
-
case line
|
46
|
-
when ''
|
47
|
-
val.clear
|
48
|
-
when val[:regx]
|
49
|
-
# Template/Substitutions
|
50
|
-
line=_ if _=val[:template]
|
51
|
-
$~.named_captures.each do |name, value|
|
52
|
-
line = line.gsub("&#{name};", value)
|
53
|
-
line = line.gsub("&#{name.upcase};", CGI.escape(value))
|
54
|
-
end
|
55
|
-
when %r(^<!-- (.*) -->$)
|
56
|
-
directive = $1
|
57
|
-
case directive
|
58
|
-
when %r(^(\w+): "(.*)"$)
|
59
|
-
val[$1.to_sym] = $2
|
60
|
-
when %r(^(\w+): /(.*)/)
|
61
|
-
val[$1.to_sym] = Regexp.new $2
|
62
|
-
else
|
63
|
-
$stderr.puts "Unrecognized directive: "+directive
|
64
|
-
end
|
65
|
-
next
|
66
|
-
end
|
67
|
-
string << line << "\n"
|
68
|
-
end
|
69
|
-
return string
|
70
|
-
end
|
71
|
-
|
72
|
-
def Base.post_process(text)
|
73
|
-
string,_ = '',nil
|
74
|
-
text.each_line do |line|
|
75
|
-
line.chomp!
|
76
|
-
case line
|
77
|
-
when %r(^(\s*)<li>\[(x| )\] (.*)</li>$)
|
78
|
-
# Task Lists
|
79
|
-
s,x,item = $1,$2,$3
|
80
|
-
li = (x=='x')?
|
81
|
-
%q{<li style="list-style-type: '☑ '">} :
|
82
|
-
%q{<li style="list-style-type: '☐ '">}
|
83
|
-
line = s+li+item+"</li>"
|
84
|
-
when %r(^<p>(\w+:\[\*?\w+\] )+\((\S+)\)</p>$)
|
85
|
-
# One Line Forms
|
86
|
-
action,method,form = $2,'get',''
|
87
|
-
line.scan(/(\w+):\[(\*)?(\w+)\] /).each do |field, pwd, name|
|
88
|
-
type = (pwd)? 'password' : 'text'
|
89
|
-
method = 'post' if pwd
|
90
|
-
form << %Q{ #{field}:<input type="#{type}" name="#{name}">\n}
|
91
|
-
end
|
92
|
-
line = %Q(<form action="#{action}" method="#{method}">\n) +
|
93
|
-
form + %Q( <input type="submit">\n</form>)
|
94
|
-
when %r(^<p><img (src="[^"]*" alt=" [^"]* ") /></p>$)
|
95
|
-
line = %Q(<img style="display: block; margin-left: auto; margin-right: auto;" #{$1} />)
|
96
|
-
when %r(^<p><img (src="[^"]*" alt=" [^"]*") />$)
|
97
|
-
line = %Q(<p><img style="float: left;" #{$1} />)
|
98
|
-
when %r(^<p><img (src="[^"]*" alt="[^"]* ") />$)
|
99
|
-
line = %Q(<p><img style="float: right;" #{$1} />)
|
100
|
-
end
|
101
|
-
string << line << "\n"
|
102
|
-
end
|
103
|
-
return string
|
104
|
-
end
|
105
|
-
|
106
|
-
def Base.page(key)
|
107
|
-
Base.header(key) + yield + Base.footer
|
108
|
-
end
|
109
|
-
|
110
21
|
get PAGE_KEY do |key|
|
111
22
|
filepath = File.join ROOT, key+'.md'
|
112
23
|
raise Sinatra::NotFound unless File.exist? filepath
|
113
|
-
|
114
|
-
Base.page(key){ Base.post_process markdown Base.pre_process text}
|
24
|
+
Markdown.new(key).filepath filepath
|
115
25
|
end
|
116
26
|
|
117
27
|
get IMAGE_PATH do |path, *_|
|
data/lib/markita/config.rb
CHANGED
data/lib/markita/html.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Markita
|
2
|
+
module HTML
|
3
|
+
def HTML.header(key)
|
4
|
+
<<~HEADER
|
5
|
+
<!DOCTYPE html>
|
6
|
+
<html>
|
7
|
+
<head>
|
8
|
+
<title>#{key}</title>
|
9
|
+
#{HEADER_LINKS}</head>
|
10
|
+
<body>
|
11
|
+
HEADER
|
12
|
+
end
|
13
|
+
|
14
|
+
def HTML.footer
|
15
|
+
<<~FOOTER
|
16
|
+
</body>
|
17
|
+
</html>
|
18
|
+
FOOTER
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,435 @@
|
|
1
|
+
module Markita
|
2
|
+
class Markdown
|
3
|
+
ROUGE = Rouge::Formatters::HTML.new
|
4
|
+
PARSERS = []
|
5
|
+
|
6
|
+
def initialize(title)
|
7
|
+
@title = title
|
8
|
+
@line=@html=@file=@opt=nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
@line,@html,@opt = HTML.header(@title),'',{}
|
13
|
+
end
|
14
|
+
|
15
|
+
def finish
|
16
|
+
@html << HTML.footer
|
17
|
+
end
|
18
|
+
|
19
|
+
def default
|
20
|
+
@html << @line
|
21
|
+
@line = @file.gets
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(fh)
|
25
|
+
@file = Preprocess.new(fh)
|
26
|
+
start
|
27
|
+
while @line
|
28
|
+
PARSERS.detect{method(_1).call} or default
|
29
|
+
end
|
30
|
+
finish
|
31
|
+
end
|
32
|
+
|
33
|
+
def markdown(string)
|
34
|
+
parse StringIO.new string
|
35
|
+
@html
|
36
|
+
end
|
37
|
+
|
38
|
+
def filepath(filepath)
|
39
|
+
File.open(filepath, 'r'){|fh| parse fh}
|
40
|
+
@html
|
41
|
+
end
|
42
|
+
|
43
|
+
Ux = /_([^_]+)_/
|
44
|
+
U = lambda {|m| "<u>#{m[1]}</u>"}
|
45
|
+
|
46
|
+
Sx = /~([^~]+)~/
|
47
|
+
S = lambda {|m| "<s>#{m[1]}</s>"}
|
48
|
+
|
49
|
+
Ix = /"([^"]+)"/
|
50
|
+
I = lambda {|m| "<i>#{m[1]}</i>"}
|
51
|
+
|
52
|
+
Bx = /\*([^\*]+)\*/
|
53
|
+
B = lambda {|m| "<b>#{m[1]}</b>"}
|
54
|
+
|
55
|
+
CODEx = /`([^`]+)`/
|
56
|
+
CODE = lambda {|m| "<code>#{m[1]}</code>"}
|
57
|
+
|
58
|
+
Ax = /\[([^\[\]]+)\]\(([^()]+)\)/
|
59
|
+
A = lambda {|m| %Q(<a href="#{m[2]}">#{m[1]}</a>)}
|
60
|
+
|
61
|
+
URLx = %r(\[(https?://[\w\.\-\/\&\+\?\%]+)\])
|
62
|
+
URL = lambda {|m| %Q(<a href="#{m[1]}">#{m[1]}</a>)}
|
63
|
+
|
64
|
+
EMOJIx = /:(\w+):/
|
65
|
+
EMOJI = lambda {|m| (_=EMOJIS[m[1]])? "&\#x#{_};" : m[0]}
|
66
|
+
|
67
|
+
FOOTNOTEx = /\[\^(\d+)\](:)?/
|
68
|
+
FOOTNOTE = lambda do |m|
|
69
|
+
if m[2]
|
70
|
+
%Q(<a id="fn:#{m[1]}" href="\#fnref:#{m[1]}">#{m[1]}:</a>)
|
71
|
+
else
|
72
|
+
%Q(<a id="fnref:#{m[1]}" href="\#fn:#{m[1]}"><sup>#{m[1]}</sup></a>)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def Markdown.tag(entry, regx, m2string, &block)
|
77
|
+
if m = regx.match(entry)
|
78
|
+
pre_match = (block ? block.call(m.pre_match) : m.pre_match)
|
79
|
+
string = pre_match + m2string[m]
|
80
|
+
post_match = m.post_match
|
81
|
+
while m = regx.match(post_match)
|
82
|
+
pre_match = (block ? block.call(m.pre_match) : m.pre_match)
|
83
|
+
string << pre_match + m2string[m]
|
84
|
+
post_match = m.post_match
|
85
|
+
end
|
86
|
+
string << (block ? block.call(post_match) : post_match)
|
87
|
+
return string
|
88
|
+
end
|
89
|
+
return (block ? block.call(entry) : entry)
|
90
|
+
end
|
91
|
+
|
92
|
+
INLINE = lambda do |entry|
|
93
|
+
string = Markdown.tag(entry, CODEx, CODE) do |entry|
|
94
|
+
Markdown.tag(entry, Ax, A) do |entry|
|
95
|
+
Markdown.tag(entry, URLx, URL) do |entry|
|
96
|
+
entry = Markdown.tag(entry, EMOJIx, EMOJI)
|
97
|
+
entry = Markdown.tag(entry, Bx, B)
|
98
|
+
entry = Markdown.tag(entry, Ix, I)
|
99
|
+
entry = Markdown.tag(entry, Sx, S)
|
100
|
+
entry = Markdown.tag(entry, Ux, U)
|
101
|
+
entry = Markdown.tag(entry, FOOTNOTEx, FOOTNOTE)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
string.sub(/ $/,'<br>')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Empty
|
109
|
+
EMPTY = /^$/
|
110
|
+
PARSERS << :empty
|
111
|
+
def empty
|
112
|
+
EMPTY.match?(@line) or return false
|
113
|
+
@line = @file.gets
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
# Ordered list
|
118
|
+
ORDERED = /^\d+. (.*)$/
|
119
|
+
PARSERS << :ordered
|
120
|
+
def ordered
|
121
|
+
md = ORDERED.match(@line) or return false
|
122
|
+
@html << "<ol#{@opt[:attributes]}>\n"
|
123
|
+
@opt.delete(:attributes)
|
124
|
+
while md
|
125
|
+
@html << " <li>#{INLINE[md[1]]}</li>\n"
|
126
|
+
md = (@line=@file.gets)&.match ORDERED
|
127
|
+
end
|
128
|
+
@html << "</ol>\n"
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
# Paragraph
|
133
|
+
PARAGRAPHS = /^[\[`*"~_]?\w/
|
134
|
+
PARSERS << :paragraphs
|
135
|
+
def paragraphs
|
136
|
+
md = PARAGRAPHS.match(@line) or return false
|
137
|
+
@html << "<p#{@opt[:attributes]}>\n"
|
138
|
+
@opt.delete(:attributes)
|
139
|
+
while md
|
140
|
+
@html << INLINE[@line]
|
141
|
+
md = (@line=@file.gets)&.match PARAGRAPHS
|
142
|
+
end
|
143
|
+
@html << "</p>\n"
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
# Unordered list
|
148
|
+
UNORDERED = /^[*] (.*)$/
|
149
|
+
PARSERS << :unordered
|
150
|
+
def unordered
|
151
|
+
md = UNORDERED.match(@line) or return false
|
152
|
+
@html << "<ul#{@opt[:attributes]}>\n"
|
153
|
+
@opt.delete(:attributes)
|
154
|
+
while md
|
155
|
+
@html << " <li>#{INLINE[md[1]]}</li>\n"
|
156
|
+
md = (@line=@file.gets)&.match UNORDERED
|
157
|
+
end
|
158
|
+
@html << "</ul>\n"
|
159
|
+
true
|
160
|
+
end
|
161
|
+
|
162
|
+
# Ballot box
|
163
|
+
BALLOTS = /^- \[(x| )\] (.*)$/
|
164
|
+
PARSERS << :ballots
|
165
|
+
def ballots
|
166
|
+
md = BALLOTS.match(@line) or return false
|
167
|
+
@html << "<ul#{@opt[:attributes]}>\n"
|
168
|
+
@opt.delete(:attributes)
|
169
|
+
while md
|
170
|
+
x,t = md[1],md[2]
|
171
|
+
li = (x=='x')?
|
172
|
+
%q{<li style="list-style-type: '☑ '">} :
|
173
|
+
%q{<li style="list-style-type: '☐ '">}
|
174
|
+
@html << " #{li}#{INLINE[t]}</li>\n"
|
175
|
+
md = (@line=@file.gets)&.match BALLOTS
|
176
|
+
end
|
177
|
+
@html << "</ul>\n"
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
# Definition list
|
182
|
+
DEFINITIONS = /^: (.*)$/
|
183
|
+
PARSERS << :definitions
|
184
|
+
def definitions
|
185
|
+
md = DEFINITIONS.match(@line) or return false
|
186
|
+
@html << "<dl#{@opt[:attributes]}>\n"
|
187
|
+
@opt.delete(:attributes)
|
188
|
+
while md
|
189
|
+
item = md[1]
|
190
|
+
@html << ((item[-1]==':')? "<dt>#{INLINE[item[0..-2]]}</dt>\n" :
|
191
|
+
"<dd>#{INLINE[item]}</dd>\n")
|
192
|
+
md = (@line=@file.gets)&.match DEFINITIONS
|
193
|
+
end
|
194
|
+
@html << "</dl>\n"
|
195
|
+
true
|
196
|
+
end
|
197
|
+
|
198
|
+
# Headers
|
199
|
+
HEADERS = /^([#]{1,6}) (.*)$/
|
200
|
+
PARSERS << :headers
|
201
|
+
def headers
|
202
|
+
md = HEADERS.match(@line) or return false
|
203
|
+
i,header = md[1].length,md[2]
|
204
|
+
id = header.strip.gsub(/\s+/,'+')
|
205
|
+
@html << %Q(<a id="#{id}">\n)
|
206
|
+
@html << " <h#{i}#{@opt[:attributes]}>#{INLINE[header]}</h#{i}>\n"
|
207
|
+
@html << "</a>\n"
|
208
|
+
@opt.delete(:attributes)
|
209
|
+
@line = @file.gets
|
210
|
+
true
|
211
|
+
end
|
212
|
+
|
213
|
+
# Block-quote
|
214
|
+
BLOCKQS = /^> (.*)$/
|
215
|
+
PARSERS << :blockqs
|
216
|
+
def blockqs
|
217
|
+
md = BLOCKQS.match(@line) or return false
|
218
|
+
@html << "<blockquote#{@opt[:attributes]}>\n"
|
219
|
+
@opt.delete(:attributes)
|
220
|
+
while md
|
221
|
+
@html << INLINE[md[1]]
|
222
|
+
@html << "\n"
|
223
|
+
md = (@line=@file.gets)&.match BLOCKQS
|
224
|
+
end
|
225
|
+
@html << "</blockquote>\n"
|
226
|
+
true
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
# Code
|
231
|
+
CODES = /^[`~]{3}\s*(\w+)?$/
|
232
|
+
PARSERS << :codes
|
233
|
+
def codes
|
234
|
+
md = CODES.match(@line) or return false
|
235
|
+
lang = Rouge::Lexer.find md[1]
|
236
|
+
klass = lang ? ' class="highlight"' : nil
|
237
|
+
@html << "<pre#{klass}#{@opt[:attributes]}><code>\n"
|
238
|
+
@opt.delete(:attributes)
|
239
|
+
code = ''
|
240
|
+
while @line=@file.gets and not CODES.match(@line)
|
241
|
+
code << @line
|
242
|
+
end
|
243
|
+
@html << (lang ? ROUGE.format(lang.new.lex(code)) : code)
|
244
|
+
@html << "</code></pre>\n"
|
245
|
+
@line = @file.gets if @line # then it's code close and thus need next @line.
|
246
|
+
true
|
247
|
+
end
|
248
|
+
|
249
|
+
# Preform
|
250
|
+
PREFORMS = /^ {4}(.*)$/
|
251
|
+
PARSERS << :preforms
|
252
|
+
def preforms
|
253
|
+
md = PREFORMS.match(@line) or return false
|
254
|
+
@html << "<pre#{@opt[:attributes]}>\n"
|
255
|
+
@opt.delete(:attributes)
|
256
|
+
while md
|
257
|
+
@html << md[1]
|
258
|
+
@html << "\n"
|
259
|
+
md = (@line=@file.gets)&.match PREFORMS
|
260
|
+
end
|
261
|
+
@html << "</pre>\n"
|
262
|
+
true
|
263
|
+
end
|
264
|
+
|
265
|
+
# Horizontal rule
|
266
|
+
HRS = /^---+$/
|
267
|
+
PARSERS << :hrs
|
268
|
+
def hrs
|
269
|
+
HRS.match? @line or return false
|
270
|
+
@html << "<hr#{@opt[:attributes]}>\n"
|
271
|
+
@opt.delete(:attributes)
|
272
|
+
@line = @file.gets
|
273
|
+
true
|
274
|
+
end
|
275
|
+
|
276
|
+
# Table
|
277
|
+
TABLES = /^\|.+\|$/
|
278
|
+
PARSERS << :tables
|
279
|
+
def tables
|
280
|
+
TABLES.match? @line or return false
|
281
|
+
@html << "<table#{@opt[:attributes]}>\n"
|
282
|
+
@opt.delete(:attributes)
|
283
|
+
@html << '<thead><tr><th>'
|
284
|
+
@html << @line[1...-1].split('|').map{INLINE[_1.strip]}.join('</th><th>')
|
285
|
+
@html << "</th></tr></thead>\n"
|
286
|
+
align = []
|
287
|
+
while (@line=@file.gets)&.match TABLES
|
288
|
+
@html << '<tr>'
|
289
|
+
@line[1...-1].split('|').each_with_index do |cell, i|
|
290
|
+
case cell
|
291
|
+
when /^\s*:-+:\s*$/
|
292
|
+
align[i] = ' align="center"'
|
293
|
+
@html << '<td><hr></td>'
|
294
|
+
when /^\s*-+:\s*$/
|
295
|
+
align[i] = ' align="right"'
|
296
|
+
@html << '<td><hr></td>'
|
297
|
+
when /^\s*:-+\s*$/
|
298
|
+
align[i] = ' align="left"'
|
299
|
+
@html << '<td><hr></td>'
|
300
|
+
else
|
301
|
+
@html << "<td#{align[i]}>#{INLINE[cell.strip]}</td>"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
@html << "</tr>\n"
|
305
|
+
end
|
306
|
+
@html << "</table>\n"
|
307
|
+
true
|
308
|
+
end
|
309
|
+
|
310
|
+
# Splits
|
311
|
+
SPLITS = /^:?\|:?$/
|
312
|
+
PARSERS << :splits
|
313
|
+
def splits
|
314
|
+
SPLITS.match? @line or return false
|
315
|
+
case @line.chomp
|
316
|
+
when '|:'
|
317
|
+
@html << %Q(<table><tr><td#{@opt[:attributes]}>\n)
|
318
|
+
when '|'
|
319
|
+
@html << %Q(</td><td#{@opt[:attributes]}>\n)
|
320
|
+
when ':|:'
|
321
|
+
@html << %Q(</td></tr><tr><td#{@opt[:attributes]}>\n)
|
322
|
+
when ':|'
|
323
|
+
@html << %Q(</td></tr></table>\n)
|
324
|
+
end
|
325
|
+
@opt.delete(:attributes)
|
326
|
+
@line = @file.gets
|
327
|
+
true
|
328
|
+
end
|
329
|
+
|
330
|
+
# Image
|
331
|
+
IMAGES = /^!\[([^\[\]]+)\]\(([^\(\)]+)\)$/
|
332
|
+
PARSERS << :images
|
333
|
+
def images
|
334
|
+
md = IMAGES.match(@line) or return false
|
335
|
+
alt,src=md[1],md[2]
|
336
|
+
style = ' '
|
337
|
+
case alt
|
338
|
+
when /^ .* $/
|
339
|
+
style = %Q( style="display: block; margin-left: auto; margin-right: auto;" )
|
340
|
+
when / $/
|
341
|
+
style = %Q( style="float:left;" )
|
342
|
+
when /^ /
|
343
|
+
style = %Q( style="float:right;" )
|
344
|
+
end
|
345
|
+
@html << %Q(<img src="#{src}"#{style}alt="#{alt.strip}"#{@opt[:attributes]}>\n)
|
346
|
+
@opt.delete(:attributes)
|
347
|
+
@line = @file.gets
|
348
|
+
true
|
349
|
+
end
|
350
|
+
|
351
|
+
# Forms
|
352
|
+
FORMS = /^!( (\w+:)?\[\*?\w+(="[^"]*")?\])+/
|
353
|
+
PARSERS << :forms
|
354
|
+
def forms
|
355
|
+
md = FORMS.match(@line) or return false
|
356
|
+
form = []
|
357
|
+
n,fields,submit,method = 0,0,nil,nil
|
358
|
+
action = (_=/\(([^\(\)]*)\)$/.match(@line))? _[1] : nil
|
359
|
+
while md
|
360
|
+
n += 1
|
361
|
+
form << ' <br>' if n > 1
|
362
|
+
@line.scan(/(\w+:)?\[(\*)?(\w+)(="[^"]*")?\]/).each do |field, pwd, name, value|
|
363
|
+
method ||= ' method="post"' if pwd
|
364
|
+
field &&= field[0...-1]
|
365
|
+
value &&= value[2...-1]
|
366
|
+
if field
|
367
|
+
fields += 1
|
368
|
+
type = (pwd)? 'password' : 'text'
|
369
|
+
if value
|
370
|
+
form << %Q{ #{field}:<input type="#{type}" name="#{name}" value="#{value}">}
|
371
|
+
else
|
372
|
+
form << %Q{ #{field}:<input type="#{type}" name="#{name}">}
|
373
|
+
end
|
374
|
+
elsif name=='submit'
|
375
|
+
submit = value
|
376
|
+
else
|
377
|
+
form << %Q{ <input type="hidden" name="#{name}" value="#{value}">}
|
378
|
+
end
|
379
|
+
end
|
380
|
+
md = (@line=@file.gets)&.match FORMS
|
381
|
+
end
|
382
|
+
if submit or not fields==1
|
383
|
+
submit ||= 'Submit'
|
384
|
+
form << ' <br>' if n > 1
|
385
|
+
form << %Q( <input type="submit" value="#{submit}">)
|
386
|
+
end
|
387
|
+
form.unshift %Q(<form action="#{action}"#{method}#{@opt[:attributes]}>)
|
388
|
+
form << %Q(</form>)
|
389
|
+
@html << form.join("\n")
|
390
|
+
@html << "\n"
|
391
|
+
@opt.delete(:attributes)
|
392
|
+
true
|
393
|
+
end
|
394
|
+
|
395
|
+
# Embed text
|
396
|
+
EMBED_TEXTS = /^!> (#{PAGE_KEY}\.txt)$/
|
397
|
+
PARSERS << :embed_texts
|
398
|
+
def embed_texts
|
399
|
+
md = EMBED_TEXTS.match(@line) or return false
|
400
|
+
if File.exist?(filename=File.join(ROOT, md[1]))
|
401
|
+
@html << "<pre>\n"
|
402
|
+
@html << File.read(filename)
|
403
|
+
@html << "</pre>\n"
|
404
|
+
else
|
405
|
+
@html << @line
|
406
|
+
end
|
407
|
+
@line = @file.gets
|
408
|
+
true
|
409
|
+
end
|
410
|
+
|
411
|
+
# Footnotes
|
412
|
+
FOOTNOTES = /^\[\^\d+\]:/
|
413
|
+
PARSERS << :footnotes
|
414
|
+
def footnotes
|
415
|
+
md = FOOTNOTES.match(@line) or return false
|
416
|
+
@html << "<small>\n"
|
417
|
+
while md
|
418
|
+
@html << INLINE[@line.chomp]+"<br>\n"
|
419
|
+
md = (@line=@file.gets)&.match FOOTNOTES
|
420
|
+
end
|
421
|
+
@html << "</small>\n"
|
422
|
+
true
|
423
|
+
end
|
424
|
+
|
425
|
+
# Attributes
|
426
|
+
ATTRIBUTES = /^\{:( .*)\}/
|
427
|
+
PARSERS << :attributes
|
428
|
+
def attributes
|
429
|
+
md = ATTRIBUTES.match(@line) or return false
|
430
|
+
@opt[:attributes] = md[1]
|
431
|
+
@line = md.post_match
|
432
|
+
true
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|