markita 4.1.230214 → 5.0.241001

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,8 @@ class Markdown
5
5
 
6
6
  def initialize(title)
7
7
  @title = title
8
- @line=@html=@file=@opt=nil
9
- @metadata = {}
8
+ @line=@html=@file=nil
9
+ @metadata,@attributes = {},[]
10
10
  end
11
11
 
12
12
  def start
@@ -22,13 +22,8 @@ class Markdown
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,@opt = Preprocess.new(fh),'',{}
26
+ @file,@html = Preprocess.new(fh),''
32
27
  end
33
28
 
34
29
  def parse(fh)
@@ -38,6 +33,21 @@ class Markdown
38
33
  finish
39
34
  end
40
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
+
41
51
  def markdown(string)
42
52
  parse StringIO.new string
43
53
  @html
@@ -85,6 +95,14 @@ class Markdown
85
95
  end
86
96
  end
87
97
 
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
+
88
106
  def self.tag(entry, regx, m2string, &block)
89
107
  if (m=regx.match entry)
90
108
  string = ''
@@ -101,6 +119,7 @@ class Markdown
101
119
  end
102
120
 
103
121
  def inline(entry)
122
+ entry = Markdown.tag(entry, ENTITYx, ENTITY)
104
123
  string = Markdown.tag(entry, CODEx, CODE) do |entry|
105
124
  Markdown.tag(entry, Ax, method(:anchor)) do |entry|
106
125
  Markdown.tag(entry, URLx, URL) do |entry|
@@ -109,11 +128,13 @@ class Markdown
109
128
  entry = Markdown.tag(entry, Ix, I)
110
129
  entry = Markdown.tag(entry, Sx, S)
111
130
  entry = Markdown.tag(entry, Ux, U)
112
- Markdown.tag(entry, FOOTNOTEx, FOOTNOTE)
131
+ entry = Markdown.tag(entry, FOOTNOTEx, FOOTNOTE)
132
+ entry = Markdown.tag(entry, SUPERSCRIPTx, SUPERSCRIPT)
133
+ Markdown.tag(entry, SUBSCRIPTx, SUBSCRIPT)
113
134
  end
114
135
  end
115
136
  end
116
- string.sub(/ $/,'<br>')
137
+ string.sub(/ ?[ \\]$/,'<br>')
117
138
  end
118
139
 
119
140
  # Empty
@@ -125,76 +146,33 @@ class Markdown
125
146
  true
126
147
  end
127
148
 
128
- # Ordered list
129
- ORDERED = /^( {0,3})\d+\. (\S.*)$/
130
- PARSERS << :ordered
131
- def ordered(md=nil)
132
- md ||= ORDERED.match(@line) or return false
133
- level = md[1].length
134
- @html << "<ol#{@opt[:attributes]}>\n"
135
- @opt.delete(:attributes)
136
- while md && level==md[1].length
137
- @html << " <li>#{inline(md[2])}</li>\n"
138
- next unless (md=(@line=@file.gets)&.match ORDERED) && level<md[1].length
139
- ordered(md)
140
- md = @line&.match(ORDERED)
141
- end
142
- @html << "</ol>\n"
143
- true
144
- end
145
-
146
- # Paragraph
147
- PARAGRAPHS = /^[\[(*`'"~_]?:?\w/
148
- PARSERS << :paragraphs
149
- def paragraphs
150
- md = PARAGRAPHS.match(@line) or return false
151
- @html << "<p#{@opt[:attributes]}>\n"
152
- @opt.delete(:attributes)
153
- while md
154
- @html << inline(@line)
155
- while (@line=@file.gets)&.start_with?('<')
156
- @html << @line # Exceptional HTML injection into the paragraph
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: '&#9744; '")
161
+ when 'x'
162
+ %q( style="list-style-type: '&#9745; '")
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)
157
170
  end
158
- md = @line&.match PARAGRAPHS
171
+ break unless md &&
172
+ (level == md[:spaces].length) &&
173
+ (list == (md[:bullet][0]=~/\d/ ? 'ol' : 'ul'))
159
174
  end
160
- @html << "</p>\n"
161
- true
162
- end
163
-
164
- # Unordered list
165
- UNORDERED = /^( {0,3})[*] (\S.*)$/
166
- PARSERS << :unordered
167
- def unordered(md=nil)
168
- md ||= UNORDERED.match(@line) or return false
169
- level = md[1].length
170
- @html << "<ul#{@opt[:attributes]}>\n"
171
- @opt.delete(:attributes)
172
- while md && level==md[1].length
173
- @html << " <li>#{inline(md[2])}</li>\n"
174
- next unless (md=(@line=@file.gets)&.match UNORDERED) && level<md[1].length
175
- unordered(md)
176
- md = @line&.match(UNORDERED)
177
- end
178
- @html << "</ul>\n"
179
- true
180
- end
181
-
182
- # Ballot box
183
- BALLOTS = /^- \[(x| )\] (.*)$/
184
- PARSERS << :ballots
185
- def ballots
186
- md = BALLOTS.match(@line) or return false
187
- @html << "<ul#{@opt[:attributes]}>\n"
188
- @opt.delete(:attributes)
189
- while md
190
- x,t = md[1],md[2]
191
- li = x=='x' ?
192
- %q(<li style="list-style-type: '&#9745; '">) :
193
- %q(<li style="list-style-type: '&#9744; '">)
194
- @html << " #{li}#{inline(t)}</li>\n"
195
- md = (@line=@file.gets)&.match BALLOTS
196
- end
197
- @html << "</ul>\n"
175
+ @html << "</#{list}>\n"
198
176
  true
199
177
  end
200
178
 
@@ -203,8 +181,7 @@ class Markdown
203
181
  PARSERS << :definitions
204
182
  def definitions
205
183
  md = DEFINITIONS.match(@line) or return false
206
- @html << "<dl#{@opt[:attributes]}>\n"
207
- @opt.delete(:attributes)
184
+ @html << "<dl#{@attributes.shift}>\n"
208
185
  while md
209
186
  case md[1]
210
187
  when /(.*): (.*)$/
@@ -229,9 +206,8 @@ class Markdown
229
206
  i,header = md[1].length,md[2]
230
207
  id = header.gsub(/\([^()]*\)/,'').scan(/\w+/).join('+')
231
208
  @html << %(<a id="#{id}">\n)
232
- @html << " <h#{i}#{@opt[:attributes]}>#{inline(header)}</h#{i}>\n"
209
+ @html << " <h#{i}#{@attributes.shift}>#{inline(header)}</h#{i}>\n"
233
210
  @html << "</a>\n"
234
- @opt.delete(:attributes)
235
211
  @line = @file.gets
236
212
  true
237
213
  end
@@ -242,8 +218,7 @@ class Markdown
242
218
  def blockqs(md=nil)
243
219
  md ||= BLOCKQS.match(@line) or return false
244
220
  level = md[1].length
245
- @html << "<blockquote#{@opt[:attributes]}>\n"
246
- @opt.delete(:attributes)
221
+ @html << "<blockquote#{@attributes.shift}>\n"
247
222
  while md && level==md[1].length
248
223
  @html << inline(md[2])
249
224
  @html << "\n"
@@ -256,14 +231,13 @@ class Markdown
256
231
  end
257
232
 
258
233
  # Code
259
- CODES = /^[`~]{3}\s*(\w+)?$/
234
+ CODES = /^[`]{3}\s*(\w+)?$/
260
235
  PARSERS << :codes
261
236
  def codes
262
237
  md = CODES.match(@line) or return false
263
238
  lang = Rouge::Lexer.find md[1]
264
239
  klass = lang ? ' class="highlight"' : nil
265
- @html << "<pre#{klass}#{@opt[:attributes]}><code>\n"
266
- @opt.delete(:attributes)
240
+ @html << "<pre#{klass}#{@attributes.shift}><code>\n"
267
241
  code = ''
268
242
  code << @line while (@line=@file.gets) && !CODES.match?(@line)
269
243
  @html << (lang ? ROUGE.format(lang.new.lex(code)) : code)
@@ -272,27 +246,12 @@ class Markdown
272
246
  true
273
247
  end
274
248
 
275
- # Script
276
- SCRIPT = /^<script/
277
- PARSERS << :script
278
- def script
279
- SCRIPT.match(@line) or return false
280
- @html << @line
281
- while (@line=@file.gets)
282
- @html << @line
283
- break if %r{^</script>}.match?(@line)
284
- end
285
- @line = @file.gets if @line
286
- true
287
- end
288
-
289
249
  # Preform
290
250
  PREFORMS = /^ {4}(.*)$/
291
251
  PARSERS << :preforms
292
252
  def preforms
293
253
  md = PREFORMS.match(@line) or return false
294
- @html << "<pre#{@opt[:attributes]}>\n"
295
- @opt.delete(:attributes)
254
+ @html << "<pre#{@attributes.shift}>\n"
296
255
  while md
297
256
  @html << md[1]
298
257
  @html << "\n"
@@ -324,8 +283,7 @@ class Markdown
324
283
  @line = @file.gets if @line&.match? HRS
325
284
  else
326
285
  # Display HR
327
- @html << "<hr#{@opt[:attributes]}>\n"
328
- @opt.delete(:attributes)
286
+ @html << "<hr#{@attributes.shift}>\n"
329
287
  end
330
288
  true
331
289
  end
@@ -335,9 +293,8 @@ class Markdown
335
293
  PARSERS << :tables
336
294
  def tables
337
295
  TABLES.match? @line or return false
338
- @html << "<table#{@opt[:attributes]}>\n"
339
- @opt.delete(:attributes)
340
- @html << '<thead><tr><th>'
296
+ @html << "<table#{@attributes.shift}>\n"
297
+ @html << "<thead#{@attributes.shift}><tr><th>"
341
298
  @html << @line[1...-1].split('|').map{inline(_1.strip)}.join('</th><th>')
342
299
  @html << "</th></tr></thead>\n"
343
300
  align = []
@@ -371,15 +328,14 @@ class Markdown
371
328
  SPLITS.match? @line or return false
372
329
  case @line.chomp
373
330
  when '|:'
374
- @html << %(<table><tr><td#{@opt[:attributes]}>\n)
331
+ @html << %(<table><tr><td#{@attributes.shift}>\n)
375
332
  when '|'
376
- @html << %(</td><td#{@opt[:attributes]}>\n)
333
+ @html << %(</td><td#{@attributes.shift}>\n)
377
334
  when ':|:'
378
- @html << %(</td></tr><tr><td#{@opt[:attributes]}>\n)
335
+ @html << %(</td></tr><tr><td#{@attributes.shift}>\n)
379
336
  when ':|'
380
337
  @html << %(</td></tr></table>\n)
381
338
  end
382
- @opt.delete(:attributes)
383
339
  @line = @file.gets
384
340
  true
385
341
  end
@@ -405,9 +361,84 @@ class Markdown
405
361
  end
406
362
  @html << %(<a href="#{href}">\n) if href
407
363
  @html <<
408
- %(<img src="#{src}"#{style}alt="#{alt.strip}"#{@opt[:attributes]}>\n)
364
+ %(<img src="#{src}"#{style}alt="#{alt.strip}"#{@attributes.shift}>\n)
409
365
  @html << %(</a>\n) if href
410
- @opt.delete(:attributes)
366
+ @line = @file.gets
367
+ true
368
+ end
369
+
370
+ # Embed text
371
+ EMBED_TEXTS = /^!> (#{PAGE_KEY}\.\w+)$/
372
+ PARSERS << :embed_texts
373
+ def embed_texts
374
+ md = EMBED_TEXTS.match(@line) or return false
375
+ if File.exist?(filename=File.join(ROOT, md[1]))
376
+ extension,lang = filename.split('.').last,nil
377
+ unless extension=='html'
378
+ lang = Rouge::Lexer.find(extension) unless extension=='txt'
379
+ klass = lang ? ' class="highlight"' : nil
380
+ @html << "<pre#{klass}#{@attributes.shift}>"
381
+ @html << '<code>' if lang
382
+ @html << "\n"
383
+ end
384
+ code = File.read(filename)
385
+ @html << (lang ? ROUGE.format(lang.new.lex(code)) : code)
386
+ unless extension=='html'
387
+ @html << '</code>' if lang
388
+ @html << '</pre>'
389
+ @html << "\n"
390
+ end
391
+ else
392
+ @html << @line
393
+ end
394
+ @line = @file.gets
395
+ true
396
+ end
397
+
398
+ # Footnotes
399
+ FOOTNOTES = /^\[\^\d+\]:/
400
+ PARSERS << :footnotes
401
+ def footnotes
402
+ md = FOOTNOTES.match(@line) or return false
403
+ @html << "<small>\n"
404
+ while md
405
+ @html << inline(@line.chomp)+"<br>\n"
406
+ md = (@line=@file.gets)&.match FOOTNOTES
407
+ end
408
+ @html << "</small>\n"
409
+ true
410
+ end
411
+
412
+ # Attributes
413
+ ATTRIBUTES = /^\{:( [^\{\}]+)\}/
414
+ PARSERS << :attributes
415
+ def attributes
416
+ md = ATTRIBUTES.match(@line) or return false
417
+ @attributes.push md[1]
418
+ @line = md.post_match
419
+ true
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
411
442
  @line = @file.gets
412
443
  true
413
444
  end
@@ -422,8 +453,7 @@ class Markdown
422
453
  fields,nl,submit = 0,false,nil
423
454
  action = (_=/\(([^()]*)\)!?$/.match(@line))? %( action="#{_[1]}") : nil
424
455
  method = @line.match?(/!$/) ? ' method="post"' : nil
425
- @html << %(<form#{action}#{method}#{@opt[:attributes]}>\n)
426
- @opt.delete(:attributes)
456
+ @html << %(<form#{action}#{method}#{@attributes.shift}>\n)
427
457
  while md
428
458
  @html << " <br>\n" if nl
429
459
  @line.scan(FIELDS).each do |field, pwd, name, value|
@@ -464,58 +494,5 @@ class Markdown
464
494
  @html << %(</form>\n)
465
495
  true
466
496
  end
467
-
468
- # Embed text
469
- EMBED_TEXTS = /^!> (#{PAGE_KEY}\.\w+)$/
470
- PARSERS << :embed_texts
471
- def embed_texts
472
- md = EMBED_TEXTS.match(@line) or return false
473
- if File.exist?(filename=File.join(ROOT, md[1]))
474
- extension,lang = filename.split('.').last,nil
475
- unless extension=='html'
476
- lang = Rouge::Lexer.find(extension) unless extension=='txt'
477
- klass = lang ? ' class="highlight"' : nil
478
- @html << "<pre#{klass}#{@opt[:attributes]}>"
479
- @opt.delete(:attributes)
480
- @html << '<code>' if lang
481
- @html << "\n"
482
- end
483
- code = File.read(filename)
484
- @html << (lang ? ROUGE.format(lang.new.lex(code)) : code)
485
- unless extension=='html'
486
- @html << '</code>' if lang
487
- @html << '</pre>'
488
- @html << "\n"
489
- end
490
- else
491
- @html << @line
492
- end
493
- @line = @file.gets
494
- true
495
- end
496
-
497
- # Footnotes
498
- FOOTNOTES = /^\[\^\d+\]:/
499
- PARSERS << :footnotes
500
- def footnotes
501
- md = FOOTNOTES.match(@line) or return false
502
- @html << "<small>\n"
503
- while md
504
- @html << inline(@line.chomp)+"<br>\n"
505
- md = (@line=@file.gets)&.match FOOTNOTES
506
- end
507
- @html << "</small>\n"
508
- true
509
- end
510
-
511
- # Attributes
512
- ATTRIBUTES = /^\{:( .*)\}/
513
- PARSERS << :attributes
514
- def attributes
515
- md = ATTRIBUTES.match(@line) or return false
516
- @opt[:attributes] = md[1]
517
- @line = md.post_match
518
- true
519
- end
520
497
  end
521
498
  end
@@ -1,7 +1,7 @@
1
1
  module Markita
2
2
  class Base
3
3
  HEADER_LINKS <<
4
- %( <link rel="icon" type="image/x-icon" href="/favicon.ico">\n)
4
+ %(\n<link rel="icon" type="image/x-icon" href="/favicon.ico">)
5
5
 
6
6
  module Favicon
7
7
  ICO = File.read PATH['favicon.ico']
@@ -1,9 +1,10 @@
1
1
  module Markita
2
2
  class Base
3
3
  HEADER_LINKS <<
4
- %( <link rel="stylesheet" href="/highlight.css" type="text/css">\n)
4
+ %(\n<link rel="stylesheet" href="/highlight.css" type="text/css">)
5
+
5
6
  module Highlight
6
- theme = OPTIONS&.theme || 'base16.light'
7
+ theme = OPTIONS.theme || 'base16.light'
7
8
  CSS = Rouge::Theme.find(theme)&.render(scope: '.highlight')
8
9
  raise "Can't find Rouge Theme "+theme unless CSS
9
10
  end
@@ -4,8 +4,8 @@ class Base
4
4
  ID = File.exist?(_=File.join(ROOT, '.valid-id')) ?
5
5
  File.read(_).strip :
6
6
  nil
7
- IPS = (_=OPTIONS&.allowed)? _.split(',') : nil
8
- if IPS && !ID
7
+ IPS = (_=OPTIONS.allowed)? _.split(',') : []
8
+ if !IPS.empty? && !ID
9
9
  raise 'Allowed ips without site password does not make sense.'
10
10
  end
11
11
  FORM = File.read PATH['login_form.html']
@@ -13,7 +13,7 @@ class Base
13
13
  end
14
14
 
15
15
  before do
16
- unless Login::ID.nil? || Login::IPS&.include?(request.ip)
16
+ unless Login::ID.nil? || Login::IPS.include?(request.ip)
17
17
  if (id=params[:id])
18
18
  session[:id] = Digest::SHA256.hexdigest id
19
19
  end
data/lib/markita.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  module Markita
2
- VERSION = '4.1.230214'
2
+ VERSION = '5.0.241001'
3
3
 
4
4
  def self.run!
5
5
  # Standard libraries
6
6
  require 'digest'
7
7
  require 'cgi'
8
+ require 'openssl'
8
9
  # Gems
9
10
  require 'rouge'
11
+ require 'webrick/https'
10
12
  require 'sinatra/base'
11
- require 'thin'
12
13
  # Local
13
14
  require_relative 'markita/config'
14
15
  require_relative 'markita/html'
@@ -16,13 +17,13 @@ module Markita
16
17
  require_relative 'markita/markdown'
17
18
  require_relative 'markita/base'
18
19
  # Plugs
19
- require_relative 'markita/plug/about' unless OPTIONS&.no_about
20
- require_relative 'markita/plug/favicon' unless OPTIONS&.no_favicon
21
- require_relative 'markita/plug/highlight' unless OPTIONS&.no_highlight
22
- require_relative 'markita/plug/login' unless OPTIONS&.no_login
23
- require_relative 'markita/plug/navigation' unless OPTIONS&.no_navigation
24
- require_relative 'markita/plug/plugs' unless OPTIONS&.no_plugs
25
- require_relative 'markita/plug/readme' unless OPTIONS&.no_readme
20
+ require_relative 'markita/plug/about' unless OPTIONS.no_about
21
+ require_relative 'markita/plug/favicon' unless OPTIONS.no_favicon
22
+ require_relative 'markita/plug/highlight' unless OPTIONS.no_highlight
23
+ require_relative 'markita/plug/login' unless OPTIONS.no_login
24
+ require_relative 'markita/plug/navigation' unless OPTIONS.no_navigation
25
+ require_relative 'markita/plug/plugs' unless OPTIONS.no_plugs
26
+ require_relative 'markita/plug/readme' unless OPTIONS.no_readme
26
27
  Base.run!
27
28
  end
28
29
  end