runeblog 0.1.76 → 0.1.78

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9820213ad610c3b8f8372fc6ba3e4b276a171e0fcf45392713c862d968aedafe
4
- data.tar.gz: e55b2c905d49352cac5d38b663e02857082277c21dd32644f8cb010c6aa7b054
3
+ metadata.gz: 7e483b3ec658dfbb130d8b2b090107155af6c5371066949ae9d78bf8b4851525
4
+ data.tar.gz: 91ae3aebfefc68e69ddea4aa9cb700a28a572a422f41b9c010b570271069c22e
5
5
  SHA512:
6
- metadata.gz: 6c277ed7f2e698c0750b60cb914f4ad161b1e189d322f1bf28275f59df38196bd219cc5ad2d165a7f2f50242107385a27aa17bfd8cf7b9db4018a64b6b0022c4
7
- data.tar.gz: e888d0b904dbd275a4fd5befe62fb7af5723aed24c7b4a3883e5e6515939883a4f2e8f51640353be60615a618cdc3fdf3421a8bef1d938840a7e32ac49f59ea1
6
+ metadata.gz: bd0a51b5eef53f86f738e51dca6428bf2eefa6373912e023c61819aadef977b30f4e7735d0a3a183944f4071c136d797ba6f32f06e327e663bd75ba84db4efe2
7
+ data.tar.gz: a531739bb62e383d4e660a7f9e630a786934d739badfd179359d63392f23f5486c62b6f53922169b0ee12c319da71edad2fb7ec7839497fa2c3121be4d2bcf6c
Binary file
@@ -3,7 +3,7 @@ class RuneBlog::Default
3
3
  # This will all become much more generic later.
4
4
 
5
5
 
6
- def RuneBlog.post_template(title: "No title", date: nil, view: "test_view",
6
+ def RuneBlog.post_template(num: 0, title: "No title", date: nil, view: "test_view",
7
7
  teaser: "No teaser", body: "No body", tags: ["untagged"],
8
8
  views: [], back: "javascript:history.go(-1)", home: "no url")
9
9
  viewlist = (views + [view.to_s]).join(" ")
@@ -11,6 +11,8 @@ def RuneBlog.post_template(title: "No title", date: nil, view: "test_view",
11
11
  <<-TEXT
12
12
  .mixin liveblog
13
13
  . ^ get rid of this later
14
+
15
+ .post #{num}
14
16
 
15
17
  .title #{title}
16
18
  .pubdate #{date}
@@ -21,10 +23,6 @@ def RuneBlog.post_template(title: "No title", date: nil, view: "test_view",
21
23
  #{teaser}
22
24
  .end
23
25
  #{body}
24
- <hr>
25
- <a href='#{back}' style='text-decoration: none'>Back</a>
26
- <br>
27
- <a href='#{home}' style='text-decoration: none'>Home</a>
28
26
  TEXT
29
27
 
30
28
  end
@@ -1,15 +1,31 @@
1
1
  require 'runeblog_version'
2
2
 
3
+ # Home = Dir.pwd # unless Home
4
+
3
5
  module RuneBlog::Helpers
4
6
 
7
+ def get_root
8
+ if $_blog
9
+ if $_blog.root
10
+ puts "0. Returned: #{$_blog.root}/"
11
+ return $_blog.root + "/"
12
+ else
13
+ puts "1. Returned: ./"
14
+ return "./"
15
+ end
16
+ else
17
+ puts "2. Returned: ./"
18
+ return "./"
19
+ end
20
+ end
21
+
5
22
  def read_config(file, *syms)
6
- # verify(File.exist?(file) => "File #{file} doesn't exist")
23
+ # puts "dir = #{Dir.pwd}"
7
24
  lines = File.readlines(file).map(&:chomp)
8
25
  obj = ::OpenStruct.new
9
26
  lines.each do |line|
10
27
  next if line == "\n" || line[0] == "#"
11
28
  key, val = line.split(/: +/, 2)
12
- # STDERR.puts [key, val].inspect
13
29
  obj.send(key+"=", val)
14
30
  end
15
31
  return obj if syms.empty?
@@ -37,6 +53,8 @@ module RuneBlog::Helpers
37
53
 
38
54
  def write_config(obj, file)
39
55
  hash = obj.to_h
56
+ # Dir.chdir(::Home)
57
+ # puts "--- wc: pwd = #{Dir.pwd}"
40
58
  File.open(file, "w") do |f|
41
59
  hash.each_pair do |key, val|
42
60
  f.puts "#{key}: #{val}"
@@ -45,11 +63,13 @@ module RuneBlog::Helpers
45
63
  end
46
64
 
47
65
  def get_views # read from filesystem
48
- verify(@root => "#@root is nil",
49
- Dir.exist?(@root) => "#@root doesn't exist",
50
- Dir.exist?("#@root/views") => "#@root/views doesn't exist")
51
- dirs = subdirs("#@root/views/").sort
52
- dirs.map {|name| RuneBlog::View.new(name) }
66
+ # Dir.chdir(::Home) do
67
+ verify(@root => "#@root is nil",
68
+ Dir.exist?(@root) => "#@root doesn't exist",
69
+ Dir.exist?("#@root/views") => "#@root/views doesn't exist")
70
+ dirs = subdirs("#@root/views/").sort
71
+ dirs.map {|name| RuneBlog::View.new(name) }
72
+ # end
53
73
  end
54
74
 
55
75
  def new_dotfile(root: "./.blogs/data", current_view: "no_default", editor: "vi")
@@ -57,7 +77,7 @@ module RuneBlog::Helpers
57
77
  Dir.mkdir(".blogs")
58
78
  x = OpenStruct.new
59
79
  x.root, x.current_view, x.editor = root, current_view, editor
60
- write_config(x, RuneBlog::ConfigFile)
80
+ write_config(x, ".blogs/" + RuneBlog::ConfigFile)
61
81
  end
62
82
 
63
83
  def new_sequence
@@ -2,7 +2,34 @@ require 'ostruct'
2
2
  require 'pp'
3
3
  require 'date'
4
4
 
5
- # require 'runeblog' # Now depends explicitly
5
+ require 'livetext'
6
+ require 'runeblog'
7
+
8
+ # ::Home = Dir.pwd unless defined?(::Home)
9
+
10
+ =begin
11
+ 123:def title # side-effect
12
+ 133:def pubdate # side-effect
13
+ 153:def tags # side-effect
14
+ 160:def views # side-effect
15
+ 167:def pin # side-effect
16
+ 218:def write_post # side-effect
17
+ 393:def main # side-effect
18
+ 491:def _post_lookup(postid) # side-effect
19
+ =end
20
+
21
+ def post
22
+ @meta = OpenStruct.new
23
+ @meta.num = _args[0]
24
+ # @live = ::Livetext.new
25
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
26
+ end
27
+
28
+ def _view_from_cwd
29
+ md = Dir.pwd.match(%r[.*/views/(.*?)/])
30
+ return md[1] if md
31
+ nil
32
+ end
6
33
 
7
34
  def quote
8
35
  _passthru "<blockquote>"
@@ -67,28 +94,27 @@ end
67
94
 
68
95
  ### copy_asset
69
96
 
70
- def copy_asset(asset)
71
- vdir = @blog.view.dir
72
- return if File.exist?(vdir + "/assets/" + asset)
73
- top = vdir + "/../../assets/"
74
- if File.exist?(top + asset)
75
- system("cp #{top}/#{asset} #{vdir}/assets/#{asset}")
76
- return
77
- end
78
- raise "Can't find #{asset.inspect}"
79
- end
97
+ # def copy_asset(asset)
98
+ # vdir = @blog.view.dir
99
+ # return if File.exist?(vdir + "/assets/" + asset)
100
+ # top = vdir + "/../../assets/"
101
+ # if File.exist?(top + asset)
102
+ # system("cp #{top}/#{asset} #{vdir}/assets/#{asset}")
103
+ # return
104
+ # end
105
+ # raise "Can't find #{asset.inspect}"
106
+ # end
80
107
 
81
108
  #############
82
109
 
83
110
  def init_liveblog # FIXME - a lot of this logic sucks
84
- @blog, num, @live = Livetext.parameters # live is Livetext instance
85
- @live ||= Livetext.new(nil)
86
- @meta = OpenStruct.new
87
- @meta.num = num
88
- @root = @blog.root rescue nil
89
- @view = @blog.view.name rescue nil
90
- @vdir = @blog.view.dir rescue nil
91
- # @body = ""
111
+ @blog = $_blog = RuneBlog.new(false)
112
+ @root = @blog.root
113
+ @view = @blog.view
114
+ @view_name = @blog.view.name
115
+ @vdir = @blog.view.dir
116
+ @version = RuneBlog::VERSION
117
+ @theme = @vdir + "/themes/standard/"
92
118
  end
93
119
 
94
120
  def _errout(*args)
@@ -109,14 +135,19 @@ def _passthru_noline(line)
109
135
  _out "<p>" if line.empty? && ! @_nopara
110
136
  end
111
137
 
112
- def title
138
+ def title # side-effect
139
+ raise "'post' was not called" unless @meta
113
140
  title = @_data.chomp
114
141
  @meta.title = title
115
- _out "<h1>#{title}</h1>"
142
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
143
+ # @live.setvar :title, title
144
+ setvar :title, title
145
+ _out %[<h1 class="post-title">#{title}</h1><br>]
116
146
  _optional_blank_line
117
147
  end
118
148
 
119
- def pubdate
149
+ def pubdate # side-effect
150
+ raise "'post' was not called" unless @meta
120
151
  _debug "data = #@_data"
121
152
  # Check for discrepancy?
122
153
  match = /(\d{4}).(\d{2}).(\d{2})/.match @_data
@@ -124,6 +155,7 @@ def pubdate
124
155
  y, m, d = y.to_i, m.to_i, d.to_i
125
156
  @meta.date = ::Date.new(y, m, d)
126
157
  @meta.pubdate = "%04d-%02d-%02d" % [y, m, d]
158
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
127
159
  _optional_blank_line
128
160
  end
129
161
 
@@ -135,22 +167,28 @@ def image # primitive so far
135
167
  _optional_blank_line
136
168
  end
137
169
 
138
- def tags
170
+ def tags # side-effect
171
+ raise "'post' was not called" unless @meta
139
172
  _debug "args = #{_args}"
140
173
  @meta.tags = _args.dup || []
174
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
141
175
  _optional_blank_line
142
176
  end
143
177
 
144
- def views
178
+ def views # side-effect
179
+ raise "'post' was not called" unless @meta
145
180
  _debug "data = #{_args}"
146
181
  @meta.views = _args.dup # + ["main"]
182
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
147
183
  _optional_blank_line
148
184
  end
149
185
 
150
- def pin
186
+ def pin # side-effect
187
+ raise "'post' was not called" unless @meta
151
188
  _debug "data = #{_args}"
152
189
  # verify only already-specified views?
153
190
  @meta.pinned = _args.dup
191
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
154
192
  _optional_blank_line
155
193
  end
156
194
 
@@ -166,7 +204,7 @@ end
166
204
 
167
205
  def list!
168
206
  _out "<ul>"
169
- lines = _body.each # {|line| @body << "<li>#{line}</li>" }
207
+ lines = _body.each
170
208
  loop do
171
209
  line = lines.next
172
210
  line = _format(line)
@@ -181,7 +219,9 @@ def list!
181
219
  end
182
220
 
183
221
  def asset
222
+ raise "'post' was not called" unless @meta
184
223
  @meta.assets ||= {}
224
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
185
225
  list = _args
186
226
  # For now: copies, doesn't keep record
187
227
  # Later: Add to file and uniq; use in publishing
@@ -190,27 +230,35 @@ def asset
190
230
  end
191
231
 
192
232
  def assets
233
+ raise "'post' was not called" unless @meta
193
234
  @meta.assets ||= []
194
235
  @meta.assets += _body
236
+ # STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
195
237
  _optional_blank_line
196
238
  end
197
239
 
198
- def write_post(meta)
240
+ def write_post # side-effect
241
+ raise "'post' was not called" unless @meta
242
+ # return
199
243
  save = Dir.pwd
200
244
  @postdir.gsub!(/\/\//, "/") # FIXME unneeded?
201
245
  Dir.mkdir(@postdir) unless Dir.exist?(@postdir) # FIXME remember assets!
202
246
  Dir.chdir(@postdir)
203
- meta.views = meta.views.join(" ")
204
- meta.tags = meta.tags.join(" ") rescue ""
205
- File.write("body.txt", @live.body) # Actually HTML...
206
- File.write("teaser.txt", meta.teaser)
247
+ STDERR.puts "------ cd into #@postdir"
248
+ @meta.views = @meta.views.join(" ")
249
+ @meta.tags = @meta.tags.join(" ") rescue ""
250
+ # STDERR.puts ">>>> #{__method__}: writing #{@live.body.size} bytes to #{Dir.pwd}/body.txt"
251
+ # File.write("body.txt", @live.body) # Actually HTML...
252
+ # p Dir.pwd
253
+ File.write("teaser.txt", @meta.teaser)
207
254
 
208
255
  fields = [:num, :title, :date, :pubdate, :views, :tags]
209
256
 
210
257
  fname2 = "metadata.txt"
211
258
  f2 = File.open(fname2, "w") do |f2|
212
- fields.each {|fld| f2.puts "#{fld}: #{meta.send(fld)}" }
259
+ fields.each {|fld| f2.puts "#{fld}: #{@meta.send(fld)}" }
213
260
  end
261
+ STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
214
262
  Dir.chdir(save)
215
263
  rescue => err
216
264
  puts "err = #{err}"
@@ -218,20 +266,29 @@ rescue => err
218
266
  end
219
267
 
220
268
  def teaser
221
- # STDERR.puts "--- teaser: pwd = #{Dir.pwd}"
269
+ raise "'post' was not called" unless @meta
222
270
  @meta.teaser = _body_text
271
+ STDERR.puts ">>> #{__method__}: @meta = #{@meta.to_h.inspect}"
223
272
  _out @meta.teaser + "\n"
273
+ STDERR.puts "TEASER cwd = #{Dir.pwd}"
274
+ # file = @vdir + "/teaser.txt"
275
+ # File.write(file, @meta.teaser)
224
276
  # FIXME
225
277
  end
226
278
 
227
279
  def finalize
280
+ unless @meta
281
+ puts @live.body
282
+ return
283
+ end
228
284
  if @blog.nil?
229
- puts @live.body if @emit
230
- return @live.body
285
+ return @meta # @live.body
231
286
  end
287
+
232
288
  @slug = @blog.make_slug(@meta)
233
- @postdir = @blog.view.dir + "/#@slug"
234
- write_post(@meta) # FIXME
289
+ slug_dir = @slug
290
+ @postdir = @blog.view.dir + "/posts/#{slug_dir}"
291
+ write_post
235
292
  @meta
236
293
  end
237
294
 
@@ -264,3 +321,364 @@ class Livetext::Functions
264
321
  end
265
322
 
266
323
  end
324
+
325
+ ###### experimental...
326
+
327
+ class Livetext::Functions
328
+
329
+ def _var(name)
330
+ ::Livetext::Vars[name] || "[:#{name} is undefined]"
331
+ end
332
+
333
+
334
+ def link
335
+ file, cdata = self.class.param.split("||", 2)
336
+ %[<link type="application/atom+xml" rel="alternate" href="#{_var(:host)}#{file}" title="#{_var(:title)}">]
337
+ end
338
+
339
+ end
340
+
341
+ ###############
342
+
343
+
344
+ def _var(name) # FIXME later
345
+ ::Livetext::Vars[name] || "[:#{name} is undefined]"
346
+ end
347
+
348
+ def head
349
+ # Depends on vars: title, desc, host
350
+ defaults = {}
351
+ defaults = { "charset" => %[<meta charset="utf-8">],
352
+ "http-equiv" => %[<meta http-equiv="X-UA-Compatible" content="IE=edge">],
353
+ "title" => %[<title>\n #{_var(:title)} | #{_var(:desc)}\n </title>],
354
+ "generator" => %[<meta name="generator" content="Runeblog v #@version">],
355
+ "og:title" => %[<meta property="og:title" content="#{_var(:title)}">],
356
+ "og:locale" => %[<meta property="og:locale" content="en_US">],
357
+ "description" => %[<meta name="description" content="#{_var(:desc)}">],
358
+ "og:description" => %[<meta property="og:description" content="#{_var(:desc)}">],
359
+ "linkc" => %[<link rel="canonical" href="#{_var(:host)}">],
360
+ "og:url" => %[<meta property="og:url" content="#{_var(:host)}">],
361
+ "og:site_name" => %[<meta property="og:site_name" content="#{_var(:title)}">],
362
+ "style" => %[<link rel="stylesheet" href="../assets/application.css">],
363
+ "feed" => %[<link type="application/atom+xml" rel="alternate" href="#{_var(:host)}/feed.xml" title="#{_var(:title)}">],
364
+ "favicon" => %[<link rel="shortcut icon" type="image/x-icon" href="../assets/favicon.ico">\n <link rel="apple-touch-icon" href="../assets/favicon.ico">]
365
+ }
366
+ result = {}
367
+ lines = _body
368
+ lines.each do |line|
369
+ line.chomp
370
+ word, remain = line.split(" ", 2)
371
+ case word
372
+ when "viewport"
373
+ result["viewport"] = %[<meta name="viewport" content="#{remain}">]
374
+ when "script"
375
+ file = remain
376
+ text = File.read(file)
377
+ result["script"] = Livetext.new.transform(text)
378
+ when "style"
379
+ result["style"] = %[<link rel="stylesheet" href="('/assets/#{remain}')">]
380
+ # Later: allow other overrides
381
+ when ""; break
382
+ else
383
+ puts "Unknown tag '#{word}'"
384
+ end
385
+ end
386
+ hash = defaults.dup.update(result) # FIXME collisions?
387
+ _out "<html lang=en_US>"
388
+ _out "<head>"
389
+ hash.each_value {|x| _out " " + x }
390
+ _out "</head>"
391
+ _out "<body>"
392
+ end
393
+
394
+ ########## newer stuff...
395
+
396
+ def meta
397
+ args = _args
398
+ enum = args.each
399
+ str = "<meta"
400
+ arg = enum.next
401
+ loop do
402
+ if arg.end_with?(":")
403
+ str << " " << arg[0..-2] << "="
404
+ a2 = enum.next
405
+ str << %["#{a2}"]
406
+ else
407
+ STDERR.puts "=== meta error?"
408
+ end
409
+ arg = enum.next
410
+ end
411
+ str << ">"
412
+ _out str
413
+ end
414
+
415
+ def main # side-effect
416
+ _out %[<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12">]
417
+ which = _args[0]
418
+ STDERR.puts "--- inside #main: which = #{which.inspect}"
419
+ case which
420
+ when "recent_posts"
421
+ all_teasers # FIXME does nothing yet
422
+ when "post"
423
+ self.data = "post-index.lt3"
424
+ _include
425
+ end
426
+ _out %[</div>]
427
+ end
428
+
429
+ def sidebar
430
+ _out %[<div class="col-lg-3 col-md-3 col-sm-3 col-xs-12">]
431
+ _body do |line|
432
+ tag = line.chomp.strip
433
+ self.data = "sidebar/#{tag.downcase}.lt3"
434
+ _include
435
+ end
436
+ _out %[</div>]
437
+ end
438
+
439
+ def sidebar2
440
+ _out %[<div class="col-lg-3 col-md-3 col-sm-3 col-xs-12">]
441
+ _args do |line|
442
+ tag = line.chomp.strip
443
+ self.data = "sidebar/#{tag.downcase}.lt3"
444
+ _include
445
+ end
446
+ _out %[</div>]
447
+ end
448
+
449
+ def stylesheet
450
+ lines = _body
451
+ url = lines[0]
452
+ integ = lines[1]
453
+ cross = lines[2] || "anonymous"
454
+ _out %[<link rel="stylesheet" href="#{url}" integrity="#{integ}" crossorigin="#{cross}"></link>]
455
+ end
456
+
457
+ def script
458
+ lines = _body
459
+ url = lines[0]
460
+ integ = lines[1]
461
+ cross = lines[2] || "anonymous"
462
+ _out %[<script src="#{url}" integrity="#{integ}" crossorigin="#{cross}"></script>]
463
+ end
464
+
465
+ ### How this next bit works:
466
+ ###
467
+ ### all_teasers will call _find_recent_posts
468
+ ###
469
+ ### _find_recent_posts will search higher in the directory structure
470
+ ### for where the posts are (0001, 0002, ...) NOTE: This implies you
471
+ ### must be in some specific place when this code is run.
472
+ ### It returns the 20 most recent posts.
473
+ ###
474
+ ### all_teasers will then pick a small number of posts and call _teaser
475
+
476
+ ### on each one. (The code in _teaser really belongs in a small template
477
+ ### somewhere.)
478
+ ###
479
+
480
+ def _find_recent_posts
481
+ STDERR.puts "--- frp: $FileDir = #{_var(:FileDir)}"
482
+ @vdir = _var(:FileDir).match(%r[(^.*/views/.*?)/])[1]
483
+ posts = nil
484
+ dir_posts = @vdir + "/posts"
485
+ entries = Dir.entries(dir_posts)
486
+ STDERR.puts "--- frp: dir_posts = #{dir_posts} ent = #{entries.inspect}"
487
+ posts = entries.grep(/^\d\d\d\d/).map {|x| dir_posts + "/" + x }
488
+ posts.select! {|x| File.directory?(x) }
489
+ # directories that start with four digits
490
+ posts = posts.sort {|a, b| b.to_i <=> a.to_i } # sort descending
491
+ posts[0..19] # return 20 at most
492
+ end
493
+
494
+ def all_teasers
495
+ STDERR.puts "-- inside #all_teasers..."
496
+ open = <<-HTML
497
+ <section class="posts">
498
+ HTML
499
+ close = <<-HTML
500
+ </section>
501
+ HTML
502
+ STDERR.puts "=== at01"
503
+ _out open
504
+ # FIXME: Now do the magic...
505
+ STDERR.puts "=== at02"
506
+ posts = _find_recent_posts
507
+ STDERR.puts "=== at03"
508
+ wanted = [5, posts.size].min # estimate how many we want?
509
+ STDERR.puts "=== at04"
510
+ enum = posts.each
511
+ STDERR.puts "=== at05"
512
+ wanted.times do
513
+ STDERR.puts "=== at06 (loop)"
514
+ postid = File.basename(enum.next)
515
+ STDERR.puts "=== at07"
516
+ postid = postid.to_i
517
+ STDERR.puts "=== at08"
518
+ _teaser(postid)
519
+ STDERR.puts "=== at09"
520
+ end
521
+ _out close
522
+ end
523
+
524
+ def _post_lookup(postid) # side-effect
525
+ # .. = templates, ../.. = views/thisview
526
+ slug = title = date = teaser_text = nil
527
+
528
+ dir_posts = @vdir + "/posts"
529
+ posts = Dir.entries(dir_posts).grep(/^\d\d\d\d/).map {|x| dir_posts + "/" + x }
530
+ posts.select! {|x| File.directory?(x) }
531
+
532
+ post = posts.select {|x| File.basename(x).to_i == postid }
533
+ raise "Error: More than one post #{postid}" if post.size > 1
534
+ postdir = post.first
535
+ fname = "#{postdir}/teaser.txt"
536
+ teaser_text = File.read(fname).chomp
537
+ # FIXME dumb hacks...
538
+ mdfile = "#{postdir}/metadata.txt"
539
+ lines = File.readlines(mdfile)
540
+ title = lines.grep(/title:/).first[7..-1].chomp
541
+ date = lines.grep(/pubdate:/).first[9..-1].chomp
542
+ slug = postdir
543
+ [slug, title, date, teaser_text]
544
+ end
545
+
546
+ def _interpolate(str, context) # FIXME move this later
547
+ wrapped = "%[" + str.dup + "]" # could fail...
548
+ eval(wrapped, context)
549
+ end
550
+
551
+ def _teaser(slug)
552
+ id = slug.to_i
553
+ text = nil
554
+ post_entry_name = @theme + "blog-_postentry.lt3"
555
+ @_post_entry ||= File.read(post_entry_name)
556
+ slug = title = date = teaser_text = nil
557
+ slug, title, date, teaser_text = _post_lookup(id)
558
+ # vdir = File.expand_path("../../..")
559
+ url = "#@vdir/#{slug}.html"
560
+ date = Date.parse(date)
561
+ date = date.strftime("%B %e<br>%Y")
562
+ text = _interpolate(@_post_entry, binding)
563
+ # File.write("../../../generated/#{slug}.html", text)
564
+ _out text
565
+ end
566
+
567
+ def card_iframe
568
+ title = _data
569
+ lines = _body
570
+ url = lines[0].chomp
571
+ stuff = lines[1..-1].join(" ") # FIXME later
572
+ text = <<-HTML
573
+ <div class="card mb-3">
574
+ <div class="card-body">
575
+ <h5 class="card-title">#{title}</h5>
576
+ <iframe src="#{url}" #{stuff}
577
+ style="border: 0" height="350"
578
+ frameborder="0" scrolling="no">
579
+ </iframe>
580
+ </div>
581
+ </div>
582
+ HTML
583
+ _out text
584
+ rescue
585
+ puts @live.body
586
+ end
587
+
588
+ def card1
589
+ card_title = _data
590
+ lines = _body
591
+ lines.map!(&:chomp)
592
+ card_text = lines[0]
593
+ url, target, classname, cdata = lines[1].split(",", 4)
594
+ text = <<-HTML
595
+ <div class="card bg-dark text-white mb-3">
596
+ <div class="card-body">
597
+ <h5 class="card-title">#{card_title}</h5>
598
+ <p class="card-text">#{card_text}</p>
599
+ <a href="#{url}" target="#{target}" class="#{classname}">#{cdata}</a>
600
+ </div>
601
+ </div>
602
+ HTML
603
+ _out text + "\n "
604
+ rescue
605
+ puts @live.body
606
+ end
607
+
608
+ def card2
609
+ card_title = _data
610
+ open = <<-HTML
611
+ <div class="card mb-3">
612
+ <div class="card-body">
613
+ <h5 class="card-title">#{card_title}</h5>
614
+ </div>
615
+ <ul class="list-group list-group-flush">
616
+ HTML
617
+ _out open
618
+ _body do |line|
619
+ url, target, cdata = line.chomp.split(",", 3)
620
+ _out %[<li class="list-group-item"><a href="#{url}" target="#{target}">#{cdata}</a> </li>]
621
+ end
622
+ close = %[ </ul>\n </div>]
623
+ _out close
624
+ rescue
625
+ puts @live.body
626
+ end
627
+
628
+ def tag_cloud
629
+ title = _data
630
+ title = "Tag Cloud" if title.empty?
631
+ open = <<-HTML
632
+ <div class="card mb-3">
633
+ <div class="card-body">
634
+ <h5 class="card-title">#{title}</h5>
635
+ HTML
636
+ _out open
637
+ _body do |line|
638
+ line.chomp!
639
+ url, target, classname, cdata = line.split(",", 4)
640
+ _out %[<a href="#{url}" target="#{target}" class="#{classname}">#{cdata}</a>]
641
+ end
642
+ rescue
643
+ puts @live.body
644
+ end
645
+
646
+ def navbar
647
+ title = _var(:title)
648
+ open = <<-HTML
649
+ <nav class="navbar navbar-expand-lg navbar-light bg-light">
650
+ <a class="navbar-brand" href="index.html">#{title}</a>
651
+ <button class="navbar-toggler"
652
+ type="button"
653
+ data-toggle="collapse"
654
+ data-target="#navbarSupportedContent"
655
+ aria-controls="navbarSupportedContent"
656
+ aria-expanded="false"
657
+ aria-label="Toggle navigation">
658
+ <span class="navbar-toggler-icon"></span>
659
+ </button>
660
+
661
+ <div class="collapse navbar-collapse pull-right"
662
+ id="navbarSupportedContent">
663
+ <ul class="navbar-nav mr-auto">
664
+ HTML
665
+ close = <<-HTML
666
+ </ul>
667
+ </div>
668
+ </nav>
669
+ HTML
670
+
671
+ first = true
672
+ _out open
673
+ _body do |line|
674
+ href, cdata = line.chomp.strip.split(" ", 2)
675
+ if first
676
+ first = false
677
+ _out %[<li class="nav-item active"> <a class="nav-link" href="#{href}">#{cdata}<span class="sr-only">(current)</span></a> </li>]
678
+ else
679
+ _out %[<li class="nav-item"> <a class="nav-link" href="#{href}">#{cdata}</a> </li>]
680
+ end
681
+ end
682
+ _out close
683
+ end
684
+