markita 4.1.230116 → 5.0.241001
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 +174 -230
- data/bin/markita +32 -32
- data/lib/markita/base.rb +13 -21
- data/lib/markita/config.rb +30 -8
- data/lib/markita/html.rb +5 -5
- data/lib/markita/markdown.rb +183 -216
- data/lib/markita/plug/about.rb +2 -2
- data/lib/markita/plug/favicon.rb +3 -1
- data/lib/markita/plug/highlight.rb +5 -3
- data/lib/markita/plug/login.rb +5 -5
- data/lib/markita/plug/navigation.rb +1 -1
- data/lib/markita/preprocess.rb +5 -5
- data/lib/markita.rb +15 -14
- metadata +141 -20
data/lib/markita/markdown.rb
CHANGED
@@ -5,8 +5,8 @@ class Markdown
|
|
5
5
|
|
6
6
|
def initialize(title)
|
7
7
|
@title = title
|
8
|
-
@line=@html=@file
|
9
|
-
@metadata = {}
|
8
|
+
@line=@html=@file=nil
|
9
|
+
@metadata,@attributes = {},[]
|
10
10
|
end
|
11
11
|
|
12
12
|
def start
|
@@ -15,31 +15,39 @@ class Markdown
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def finish
|
18
|
-
if title
|
19
|
-
@html << %
|
18
|
+
if (title=@metadata['Title'])
|
19
|
+
@html << %(<script> document.title = "#{title}" </script>\n)
|
20
20
|
end
|
21
21
|
@html << HTML.footer
|
22
22
|
@line = nil
|
23
23
|
end
|
24
24
|
|
25
|
-
def default
|
26
|
-
@html << @line
|
27
|
-
@line = @file.gets
|
28
|
-
end
|
29
|
-
|
30
25
|
def init(fh)
|
31
|
-
@file,@html
|
26
|
+
@file,@html = Preprocess.new(fh),''
|
32
27
|
end
|
33
28
|
|
34
29
|
def parse(fh)
|
35
30
|
init(fh)
|
36
31
|
start
|
37
|
-
while @line
|
38
|
-
PARSERS.detect{method(_1).call} or default
|
39
|
-
end
|
32
|
+
PARSERS.detect{method(_1).call} or default while @line
|
40
33
|
finish
|
41
34
|
end
|
42
35
|
|
36
|
+
def default
|
37
|
+
# Defaults to paragraph
|
38
|
+
# Let html take the original @html object and set @html to ''
|
39
|
+
html,@html = @html,''
|
40
|
+
html << "<p#{@attributes.shift}>\n"
|
41
|
+
loop do
|
42
|
+
html << inline(@line)
|
43
|
+
break if (@line=@file.gets).nil? || PARSERS.detect{method(_1).call}
|
44
|
+
end
|
45
|
+
html << "</p>\n"
|
46
|
+
html << @html
|
47
|
+
# Give back the original object to @html
|
48
|
+
@html = html
|
49
|
+
end
|
50
|
+
|
43
51
|
def markdown(string)
|
44
52
|
parse StringIO.new string
|
45
53
|
@html
|
@@ -51,44 +59,52 @@ class Markdown
|
|
51
59
|
end
|
52
60
|
|
53
61
|
Ux = /_([^_]+)_/
|
54
|
-
U =
|
62
|
+
U = ->(m){"<u>#{m[1]}</u>"}
|
55
63
|
|
56
64
|
Sx = /~([^~]+)~/
|
57
|
-
S =
|
65
|
+
S = ->(m){"<s>#{m[1]}</s>"}
|
58
66
|
|
59
67
|
Ix = /"([^"]+)"/
|
60
|
-
I =
|
68
|
+
I = ->(m){"<i>#{m[1]}</i>"}
|
61
69
|
|
62
|
-
Bx = /\*([
|
63
|
-
B =
|
70
|
+
Bx = /\*([^*]+)\*/
|
71
|
+
B = ->(m){"<b>#{m[1]}</b>"}
|
64
72
|
|
65
73
|
CODEx = /`([^`]+)`/
|
66
|
-
CODE =
|
74
|
+
CODE = ->(m){"<code>#{m[1].gsub('<','<')}</code>"}
|
67
75
|
|
68
76
|
Ax = /\[([^\[\]]+)\]\(([^()]+)\)/
|
69
77
|
def anchor(m)
|
70
78
|
href = ((_=m[2]).match?(/^\d+$/) and @metadata[_] or _)
|
71
79
|
text = Markdown.tag(m[1], EMOJIx, EMOJI)
|
72
|
-
%
|
80
|
+
%(<a href="#{href}">#{text}</a>)
|
73
81
|
end
|
74
82
|
|
75
|
-
URLx = %r(
|
76
|
-
URL =
|
83
|
+
URLx = %r{(https?://[\w./&+?%-]+)}
|
84
|
+
URL = ->(m){%(<a href="#{m[1]}">#{m[1]}</a>)}
|
77
85
|
|
78
86
|
EMOJIx = /:(\w+):/
|
79
|
-
EMOJI =
|
87
|
+
EMOJI = ->(m){(_=EMOJIS[m[1]])? "&#x#{_};" : m[0]}
|
80
88
|
|
81
89
|
FOOTNOTEx = /\[\^(\d+)\](:)?/
|
82
90
|
FOOTNOTE = lambda do |m|
|
83
91
|
if m[2]
|
84
|
-
%
|
92
|
+
%(<a id="fn:#{m[1]}" href="#fnref:#{m[1]}">#{m[1]}:</a>)
|
85
93
|
else
|
86
|
-
%
|
94
|
+
%(<a id="fnref:#{m[1]}" href="#fn:#{m[1]}"><sup>#{m[1]}</sup></a>)
|
87
95
|
end
|
88
96
|
end
|
89
97
|
|
90
|
-
|
91
|
-
|
98
|
+
SUPERSCRIPTx = /\\\^\(([^()]+)\)/
|
99
|
+
SUPERSCRIPT = ->(m){"<sup>#{m[1]}</sup>"}
|
100
|
+
SUBSCRIPTx = /\\\(([^()]+)\)/
|
101
|
+
SUBSCRIPT = ->(m){"<sub>#{m[1]}</sub>"}
|
102
|
+
|
103
|
+
ENTITYx = /\\([<>*"~`_&;:\\])/
|
104
|
+
ENTITY = ->(m){"&##{m[1].ord};"}
|
105
|
+
|
106
|
+
def self.tag(entry, regx, m2string, &block)
|
107
|
+
if (m=regx.match entry)
|
92
108
|
string = ''
|
93
109
|
while m
|
94
110
|
pre_match = (block ? block.call(m.pre_match) : m.pre_match)
|
@@ -99,10 +115,11 @@ class Markdown
|
|
99
115
|
string << (block ? block.call(post_match) : post_match)
|
100
116
|
return string
|
101
117
|
end
|
102
|
-
|
118
|
+
block ? block.call(entry) : entry
|
103
119
|
end
|
104
120
|
|
105
121
|
def inline(entry)
|
122
|
+
entry = Markdown.tag(entry, ENTITYx, ENTITY)
|
106
123
|
string = Markdown.tag(entry, CODEx, CODE) do |entry|
|
107
124
|
Markdown.tag(entry, Ax, method(:anchor)) do |entry|
|
108
125
|
Markdown.tag(entry, URLx, URL) do |entry|
|
@@ -112,10 +129,12 @@ class Markdown
|
|
112
129
|
entry = Markdown.tag(entry, Sx, S)
|
113
130
|
entry = Markdown.tag(entry, Ux, U)
|
114
131
|
entry = Markdown.tag(entry, FOOTNOTEx, FOOTNOTE)
|
132
|
+
entry = Markdown.tag(entry, SUPERSCRIPTx, SUPERSCRIPT)
|
133
|
+
Markdown.tag(entry, SUBSCRIPTx, SUBSCRIPT)
|
115
134
|
end
|
116
135
|
end
|
117
136
|
end
|
118
|
-
string.sub(/
|
137
|
+
string.sub(/ ?[ \\]$/,'<br>')
|
119
138
|
end
|
120
139
|
|
121
140
|
# Empty
|
@@ -127,82 +146,33 @@ class Markdown
|
|
127
146
|
true
|
128
147
|
end
|
129
148
|
|
130
|
-
#
|
131
|
-
|
132
|
-
PARSERS << :
|
133
|
-
def
|
134
|
-
md ||=
|
135
|
-
level = md[
|
136
|
-
|
137
|
-
@
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
# Paragraph
|
152
|
-
PARAGRAPHS = /^[\[\(*`'"~_]?:?\w/
|
153
|
-
PARSERS << :paragraphs
|
154
|
-
def paragraphs
|
155
|
-
md = PARAGRAPHS.match(@line) or return false
|
156
|
-
@html << "<p#{@opt[:attributes]}>\n"
|
157
|
-
@opt.delete(:attributes)
|
158
|
-
while md
|
159
|
-
@html << inline(@line)
|
160
|
-
while (@line=@file.gets)&.start_with?('<')
|
161
|
-
@html << @line # Exceptional HTML injection into the paragraph
|
162
|
-
end
|
163
|
-
md = @line&.match PARAGRAPHS
|
164
|
-
end
|
165
|
-
@html << "</p>\n"
|
166
|
-
true
|
167
|
-
end
|
168
|
-
|
169
|
-
# Unordered list
|
170
|
-
UNORDERED = /^( {0,3})[*] (\S.*)$/
|
171
|
-
PARSERS << :unordered
|
172
|
-
def unordered(md=nil)
|
173
|
-
md ||= UNORDERED.match(@line) or return false
|
174
|
-
level = md[1].length
|
175
|
-
@html << "<ul#{@opt[:attributes]}>\n"
|
176
|
-
@opt.delete(:attributes)
|
177
|
-
while md and level==md[1].length
|
178
|
-
@html << " <li>#{inline(md[2])}</li>\n"
|
179
|
-
if md = (@line=@file.gets)&.match(UNORDERED)
|
180
|
-
if level < md[1].length
|
181
|
-
unordered(md)
|
182
|
-
md = @line&.match(UNORDERED)
|
183
|
-
end
|
149
|
+
# List
|
150
|
+
LIST = /^(?<spaces> {0,3})(?<bullet>[*]|(\d+\.)|(- \[( |x)\])) (?<text>\S.*)$/
|
151
|
+
PARSERS << :list
|
152
|
+
def list(md=nil)
|
153
|
+
md ||= LIST.match(@line) or return false
|
154
|
+
level = md[:spaces].length
|
155
|
+
list = md[:bullet][0]=~/\d/ ? 'ol' : 'ul'
|
156
|
+
@html << "<#{list}#{@attributes.shift}>\n"
|
157
|
+
loop do
|
158
|
+
style = case md[:bullet][3]
|
159
|
+
when ' '
|
160
|
+
%q( style="list-style-type: '☐ '")
|
161
|
+
when 'x'
|
162
|
+
%q( style="list-style-type: '☑ '")
|
163
|
+
else
|
164
|
+
''
|
165
|
+
end
|
166
|
+
@html << " <li#{style}>#{inline(md[:text])}</li>\n"
|
167
|
+
if (md=(@line=@file.gets)&.match LIST) && level<md[:spaces].length
|
168
|
+
list(md)
|
169
|
+
md = @line&.match(LIST)
|
184
170
|
end
|
171
|
+
break unless md &&
|
172
|
+
(level == md[:spaces].length) &&
|
173
|
+
(list == (md[:bullet][0]=~/\d/ ? 'ol' : 'ul'))
|
185
174
|
end
|
186
|
-
@html << "
|
187
|
-
true
|
188
|
-
end
|
189
|
-
|
190
|
-
# Ballot box
|
191
|
-
BALLOTS = /^- \[(x| )\] (.*)$/
|
192
|
-
PARSERS << :ballots
|
193
|
-
def ballots
|
194
|
-
md = BALLOTS.match(@line) or return false
|
195
|
-
@html << "<ul#{@opt[:attributes]}>\n"
|
196
|
-
@opt.delete(:attributes)
|
197
|
-
while md
|
198
|
-
x,t = md[1],md[2]
|
199
|
-
li = (x=='x')?
|
200
|
-
%q{<li style="list-style-type: '☑ '">} :
|
201
|
-
%q{<li style="list-style-type: '☐ '">}
|
202
|
-
@html << " #{li}#{inline(t)}</li>\n"
|
203
|
-
md = (@line=@file.gets)&.match BALLOTS
|
204
|
-
end
|
205
|
-
@html << "</ul>\n"
|
175
|
+
@html << "</#{list}>\n"
|
206
176
|
true
|
207
177
|
end
|
208
178
|
|
@@ -211,8 +181,7 @@ class Markdown
|
|
211
181
|
PARSERS << :definitions
|
212
182
|
def definitions
|
213
183
|
md = DEFINITIONS.match(@line) or return false
|
214
|
-
@html << "<dl#{@
|
215
|
-
@opt.delete(:attributes)
|
184
|
+
@html << "<dl#{@attributes.shift}>\n"
|
216
185
|
while md
|
217
186
|
case md[1]
|
218
187
|
when /(.*): (.*)$/
|
@@ -230,16 +199,15 @@ class Markdown
|
|
230
199
|
end
|
231
200
|
|
232
201
|
# Headers
|
233
|
-
HEADERS = /^(
|
202
|
+
HEADERS = /^(\#{1,6}) (.*)$/
|
234
203
|
PARSERS << :headers
|
235
204
|
def headers
|
236
205
|
md = HEADERS.match(@line) or return false
|
237
206
|
i,header = md[1].length,md[2]
|
238
|
-
id = header.gsub(/\([
|
239
|
-
@html << %
|
240
|
-
@html << " <h#{i}#{@
|
207
|
+
id = header.gsub(/\([^()]*\)/,'').scan(/\w+/).join('+')
|
208
|
+
@html << %(<a id="#{id}">\n)
|
209
|
+
@html << " <h#{i}#{@attributes.shift}>#{inline(header)}</h#{i}>\n"
|
241
210
|
@html << "</a>\n"
|
242
|
-
@opt.delete(:attributes)
|
243
211
|
@line = @file.gets
|
244
212
|
true
|
245
213
|
end
|
@@ -250,62 +218,40 @@ class Markdown
|
|
250
218
|
def blockqs(md=nil)
|
251
219
|
md ||= BLOCKQS.match(@line) or return false
|
252
220
|
level = md[1].length
|
253
|
-
@html << "<blockquote#{@
|
254
|
-
|
255
|
-
while md and level==md[1].length
|
221
|
+
@html << "<blockquote#{@attributes.shift}>\n"
|
222
|
+
while md && level==md[1].length
|
256
223
|
@html << inline(md[2])
|
257
224
|
@html << "\n"
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
md = @line&.match(BLOCKQS)
|
262
|
-
end
|
263
|
-
end
|
225
|
+
next unless (md=(@line=@file.gets)&.match BLOCKQS) && level<md[1].length
|
226
|
+
blockqs(md)
|
227
|
+
md = @line&.match(BLOCKQS)
|
264
228
|
end
|
265
229
|
@html << "</blockquote>\n"
|
266
230
|
true
|
267
231
|
end
|
268
232
|
|
269
233
|
# Code
|
270
|
-
CODES = /^[
|
234
|
+
CODES = /^[`]{3}\s*(\w+)?$/
|
271
235
|
PARSERS << :codes
|
272
236
|
def codes
|
273
237
|
md = CODES.match(@line) or return false
|
274
238
|
lang = Rouge::Lexer.find md[1]
|
275
239
|
klass = lang ? ' class="highlight"' : nil
|
276
|
-
@html << "<pre#{klass}#{@
|
277
|
-
@opt.delete(:attributes)
|
240
|
+
@html << "<pre#{klass}#{@attributes.shift}><code>\n"
|
278
241
|
code = ''
|
279
|
-
while @line=@file.gets
|
280
|
-
code << @line
|
281
|
-
end
|
242
|
+
code << @line while (@line=@file.gets) && !CODES.match?(@line)
|
282
243
|
@html << (lang ? ROUGE.format(lang.new.lex(code)) : code)
|
283
244
|
@html << "</code></pre>\n"
|
284
245
|
@line = @file.gets if @line # then it's code close and thus need next @line.
|
285
246
|
true
|
286
247
|
end
|
287
248
|
|
288
|
-
# Script
|
289
|
-
SCRIPT = /^<script/
|
290
|
-
PARSERS << :script
|
291
|
-
def script
|
292
|
-
md = SCRIPT.match(@line) or return false
|
293
|
-
@html << @line
|
294
|
-
while @line=@file.gets
|
295
|
-
@html << @line
|
296
|
-
break if %r(^</script>).match?(@line)
|
297
|
-
end
|
298
|
-
@line = @file.gets if @line
|
299
|
-
true
|
300
|
-
end
|
301
|
-
|
302
249
|
# Preform
|
303
250
|
PREFORMS = /^ {4}(.*)$/
|
304
251
|
PARSERS << :preforms
|
305
252
|
def preforms
|
306
253
|
md = PREFORMS.match(@line) or return false
|
307
|
-
@html << "<pre#{@
|
308
|
-
@opt.delete(:attributes)
|
254
|
+
@html << "<pre#{@attributes.shift}>\n"
|
309
255
|
while md
|
310
256
|
@html << md[1]
|
311
257
|
@html << "\n"
|
@@ -337,8 +283,7 @@ class Markdown
|
|
337
283
|
@line = @file.gets if @line&.match? HRS
|
338
284
|
else
|
339
285
|
# Display HR
|
340
|
-
@html << "<hr#{@
|
341
|
-
@opt.delete(:attributes)
|
286
|
+
@html << "<hr#{@attributes.shift}>\n"
|
342
287
|
end
|
343
288
|
true
|
344
289
|
end
|
@@ -348,9 +293,8 @@ class Markdown
|
|
348
293
|
PARSERS << :tables
|
349
294
|
def tables
|
350
295
|
TABLES.match? @line or return false
|
351
|
-
@html << "<table#{@
|
352
|
-
@
|
353
|
-
@html << '<thead><tr><th>'
|
296
|
+
@html << "<table#{@attributes.shift}>\n"
|
297
|
+
@html << "<thead#{@attributes.shift}><tr><th>"
|
354
298
|
@html << @line[1...-1].split('|').map{inline(_1.strip)}.join('</th><th>')
|
355
299
|
@html << "</th></tr></thead>\n"
|
356
300
|
align = []
|
@@ -384,21 +328,20 @@ class Markdown
|
|
384
328
|
SPLITS.match? @line or return false
|
385
329
|
case @line.chomp
|
386
330
|
when '|:'
|
387
|
-
@html << %
|
331
|
+
@html << %(<table><tr><td#{@attributes.shift}>\n)
|
388
332
|
when '|'
|
389
|
-
@html << %
|
333
|
+
@html << %(</td><td#{@attributes.shift}>\n)
|
390
334
|
when ':|:'
|
391
|
-
@html << %
|
335
|
+
@html << %(</td></tr><tr><td#{@attributes.shift}>\n)
|
392
336
|
when ':|'
|
393
|
-
@html << %
|
337
|
+
@html << %(</td></tr></table>\n)
|
394
338
|
end
|
395
|
-
@opt.delete(:attributes)
|
396
339
|
@line = @file.gets
|
397
340
|
true
|
398
341
|
end
|
399
342
|
|
400
343
|
# Image
|
401
|
-
IMAGES = /^!\[([^\[\]]+)\]\(([
|
344
|
+
IMAGES = /^!\[([^\[\]]+)\]\(([^()]+)\)$/
|
402
345
|
PARSERS << :images
|
403
346
|
def images
|
404
347
|
md = IMAGES.match(@line) or return false
|
@@ -406,75 +349,24 @@ class Markdown
|
|
406
349
|
style = ' '
|
407
350
|
case alt
|
408
351
|
when /^:.*:$/
|
409
|
-
style =
|
352
|
+
style =
|
353
|
+
%( style="display: block; margin-left: auto; margin-right: auto;" )
|
410
354
|
when /:$/
|
411
|
-
style = %
|
355
|
+
style = %( style="float:left;" )
|
412
356
|
when /^:/
|
413
|
-
style = %
|
357
|
+
style = %( style="float:right;" )
|
414
358
|
end
|
415
359
|
if /(\d+)x(\d+)/.match alt
|
416
|
-
style << %
|
360
|
+
style << %(width="#{$1}" height="#{$2}" )
|
417
361
|
end
|
418
|
-
@html << %
|
419
|
-
@html <<
|
420
|
-
|
421
|
-
@
|
362
|
+
@html << %(<a href="#{href}">\n) if href
|
363
|
+
@html <<
|
364
|
+
%(<img src="#{src}"#{style}alt="#{alt.strip}"#{@attributes.shift}>\n)
|
365
|
+
@html << %(</a>\n) if href
|
422
366
|
@line = @file.gets
|
423
367
|
true
|
424
368
|
end
|
425
369
|
|
426
|
-
# Forms
|
427
|
-
FIELD = '(\w+:)?\[(\*)?(\w+)(=("[^"]+")(,"[^"]+")*)?\]'
|
428
|
-
FIELDS = Regexp.new FIELD
|
429
|
-
FORMS = Regexp.new "^!( #{FIELD})+"
|
430
|
-
PARSERS << :forms
|
431
|
-
def forms
|
432
|
-
md = FORMS.match(@line) or return false
|
433
|
-
fields,nl,submit = 0,false,nil
|
434
|
-
action = (_=/\(([^\(\)]*)\)!?$/.match(@line))? %Q( action="#{_[1]}") : nil
|
435
|
-
method = @line.match?(/!$/) ? ' method="post"' : nil
|
436
|
-
@html << %Q(<form#{action}#{method}#{@opt[:attributes]}>\n)
|
437
|
-
@opt.delete(:attributes)
|
438
|
-
while md
|
439
|
-
@html << " <br>\n" if nl
|
440
|
-
@line.scan(FIELDS).each do |field, pwd, name, value|
|
441
|
-
field &&= field[0...-1]
|
442
|
-
value &&= value[2...-1]
|
443
|
-
if field
|
444
|
-
type = (pwd)? 'password' : 'text'
|
445
|
-
if value
|
446
|
-
if (values = value.split('","')).length > 1
|
447
|
-
@html << %Q(#{field}:<select name="#{name}">\n)
|
448
|
-
values.each do |value|
|
449
|
-
fields += 1
|
450
|
-
@html << %Q( <option value="#{value}">#{value}</option>\n)
|
451
|
-
end
|
452
|
-
@html << "</select>\n"
|
453
|
-
else
|
454
|
-
fields += 1
|
455
|
-
@html << %Q{ #{field}:<input type="#{type}" name="#{name}" value="#{value}">\n}
|
456
|
-
end
|
457
|
-
else
|
458
|
-
fields += 1
|
459
|
-
@html << %Q{ #{field}:<input type="#{type}" name="#{name}">\n}
|
460
|
-
end
|
461
|
-
elsif name=='submit'
|
462
|
-
submit = value
|
463
|
-
else
|
464
|
-
@html << %Q{ <input type="hidden" name="#{name}" value="#{value}">\n}
|
465
|
-
end
|
466
|
-
end
|
467
|
-
md=(@line=@file.gets)&.match(FORMS) and nl=true
|
468
|
-
end
|
469
|
-
if submit or not fields==1
|
470
|
-
submit ||= 'Submit'
|
471
|
-
@html << " <br>\n" if nl
|
472
|
-
@html << %Q( <input type="submit" value="#{submit}">\n)
|
473
|
-
end
|
474
|
-
@html << %Q(</form>\n)
|
475
|
-
true
|
476
|
-
end
|
477
|
-
|
478
370
|
# Embed text
|
479
371
|
EMBED_TEXTS = /^!> (#{PAGE_KEY}\.\w+)$/
|
480
372
|
PARSERS << :embed_texts
|
@@ -485,8 +377,7 @@ class Markdown
|
|
485
377
|
unless extension=='html'
|
486
378
|
lang = Rouge::Lexer.find(extension) unless extension=='txt'
|
487
379
|
klass = lang ? ' class="highlight"' : nil
|
488
|
-
@html << "<pre#{klass}#{@
|
489
|
-
@opt.delete(:attributes)
|
380
|
+
@html << "<pre#{klass}#{@attributes.shift}>"
|
490
381
|
@html << '<code>' if lang
|
491
382
|
@html << "\n"
|
492
383
|
end
|
@@ -519,13 +410,89 @@ class Markdown
|
|
519
410
|
end
|
520
411
|
|
521
412
|
# Attributes
|
522
|
-
ATTRIBUTES = /^\{:(
|
413
|
+
ATTRIBUTES = /^\{:( [^\{\}]+)\}/
|
523
414
|
PARSERS << :attributes
|
524
415
|
def attributes
|
525
416
|
md = ATTRIBUTES.match(@line) or return false
|
526
|
-
@
|
417
|
+
@attributes.push md[1]
|
527
418
|
@line = md.post_match
|
528
419
|
true
|
529
420
|
end
|
421
|
+
|
422
|
+
# Script
|
423
|
+
SCRIPT = /^<script/
|
424
|
+
PARSERS << :script
|
425
|
+
def script
|
426
|
+
SCRIPT.match(@line) or return false
|
427
|
+
@html << @line
|
428
|
+
while (@line=@file.gets)
|
429
|
+
@html << @line
|
430
|
+
break if %r{^</script>}.match?(@line)
|
431
|
+
end
|
432
|
+
@line = @file.gets if @line
|
433
|
+
true
|
434
|
+
end
|
435
|
+
|
436
|
+
# Html
|
437
|
+
HTML_MARKUP = /^ {0,3}<.*>$/
|
438
|
+
PARSERS << :html_markup
|
439
|
+
def html_markup
|
440
|
+
HTML_MARKUP.match(@line) or return false
|
441
|
+
@html << @line
|
442
|
+
@line = @file.gets
|
443
|
+
true
|
444
|
+
end
|
445
|
+
|
446
|
+
# Forms
|
447
|
+
FIELD = '(\w+:)?\[(\*)?(\w+)(=("[^"]+")(,"[^"]+")*)?\]'
|
448
|
+
FIELDS = Regexp.new FIELD
|
449
|
+
FORMS = Regexp.new "^!( #{FIELD})+"
|
450
|
+
PARSERS << :forms
|
451
|
+
def forms
|
452
|
+
md = FORMS.match(@line) or return false
|
453
|
+
fields,nl,submit = 0,false,nil
|
454
|
+
action = (_=/\(([^()]*)\)!?$/.match(@line))? %( action="#{_[1]}") : nil
|
455
|
+
method = @line.match?(/!$/) ? ' method="post"' : nil
|
456
|
+
@html << %(<form#{action}#{method}#{@attributes.shift}>\n)
|
457
|
+
while md
|
458
|
+
@html << " <br>\n" if nl
|
459
|
+
@line.scan(FIELDS).each do |field, pwd, name, value|
|
460
|
+
field &&= field[0...-1]
|
461
|
+
value &&= value[2...-1]
|
462
|
+
if field
|
463
|
+
type = pwd ? 'password' : 'text'
|
464
|
+
if value
|
465
|
+
if (values = value.split('","')).length > 1
|
466
|
+
@html << %(#{field}:<select name="#{name}">\n)
|
467
|
+
values.each do |value|
|
468
|
+
fields += 1
|
469
|
+
@html << %( <option value="#{value}">#{value}</option>\n)
|
470
|
+
end
|
471
|
+
@html << "</select>\n"
|
472
|
+
else
|
473
|
+
fields += 1
|
474
|
+
@html << %( #{field}:<input type="#{type}" name="#{name}")
|
475
|
+
@html << %( value="#{value}">\n)
|
476
|
+
end
|
477
|
+
else
|
478
|
+
fields += 1
|
479
|
+
@html << %( #{field}:<input type="#{type}" name="#{name}">\n)
|
480
|
+
end
|
481
|
+
elsif name=='submit'
|
482
|
+
submit = value
|
483
|
+
else
|
484
|
+
@html << %( <input type="hidden" name="#{name}" value="#{value}">\n)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
md=(@line=@file.gets)&.match(FORMS) and nl=true
|
488
|
+
end
|
489
|
+
if submit || fields!=1
|
490
|
+
submit ||= 'Submit'
|
491
|
+
@html << " <br>\n" if nl
|
492
|
+
@html << %( <input type="submit" value="#{submit}">\n)
|
493
|
+
end
|
494
|
+
@html << %(</form>\n)
|
495
|
+
true
|
496
|
+
end
|
530
497
|
end
|
531
498
|
end
|
data/lib/markita/plug/about.rb
CHANGED
@@ -15,11 +15,11 @@ class Base
|
|
15
15
|
TEXT
|
16
16
|
Base.routes['GET'].each do |route|
|
17
17
|
path = route[0].to_s
|
18
|
-
next
|
18
|
+
next unless %r{^/\w+\.html$}.match? path
|
19
19
|
basename = File.basename(path, '.*')
|
20
20
|
text << "* [#{basename}](#{path})\n"
|
21
21
|
end
|
22
|
-
if defined? Favicon
|
22
|
+
if defined? Favicon && Favicon::ICO
|
23
23
|
text << "\n\n"
|
24
24
|
end
|
25
25
|
text
|
data/lib/markita/plug/favicon.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Markita
|
2
2
|
class Base
|
3
|
-
HEADER_LINKS <<
|
3
|
+
HEADER_LINKS <<
|
4
|
+
%(\n<link rel="stylesheet" href="/highlight.css" type="text/css">)
|
5
|
+
|
4
6
|
module Highlight
|
5
|
-
theme = OPTIONS
|
7
|
+
theme = OPTIONS.theme || 'base16.light'
|
6
8
|
CSS = Rouge::Theme.find(theme)&.render(scope: '.highlight')
|
7
|
-
raise "Can't find Rouge Theme "+theme
|
9
|
+
raise "Can't find Rouge Theme "+theme unless CSS
|
8
10
|
end
|
9
11
|
|
10
12
|
get '/highlight.css' do
|
data/lib/markita/plug/login.rb
CHANGED
@@ -4,17 +4,17 @@ class Base
|
|
4
4
|
ID = File.exist?(_=File.join(ROOT, '.valid-id')) ?
|
5
5
|
File.read(_).strip :
|
6
6
|
nil
|
7
|
-
IPS = (_=OPTIONS
|
8
|
-
if IPS
|
9
|
-
raise
|
7
|
+
IPS = (_=OPTIONS.allowed)? _.split(',') : []
|
8
|
+
if !IPS.empty? && !ID
|
9
|
+
raise 'Allowed ips without site password does not make sense.'
|
10
10
|
end
|
11
11
|
FORM = File.read PATH['login_form.html']
|
12
12
|
FAILED = File.read PATH['login_failed.html']
|
13
13
|
end
|
14
14
|
|
15
15
|
before do
|
16
|
-
unless Login::ID.nil?
|
17
|
-
if id
|
16
|
+
unless Login::ID.nil? || Login::IPS.include?(request.ip)
|
17
|
+
if (id=params[:id])
|
18
18
|
session[:id] = Digest::SHA256.hexdigest id
|
19
19
|
end
|
20
20
|
if session[:id] == Login::ID
|