runeblog 0.3.02
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.lt3 +279 -0
- data/README.md +312 -0
- data/bin/blog +200 -0
- data/bin/mkwidget +164 -0
- data/data/EDITOR +1 -0
- data/data/ROOT +1 -0
- data/data/VIEW +1 -0
- data/data/features.txt +18 -0
- data/data/global.lt3 +20 -0
- data/data/universal.lt3 +18 -0
- data/empty_view/assets/austin-pano.jpg +0 -0
- data/empty_view/assets/sky2.jpg +0 -0
- data/empty_view/config/exper/2svg.lt3 +38 -0
- data/empty_view/config/exper/gen_svg.rb +60 -0
- data/empty_view/config/exper/meta.html +10 -0
- data/empty_view/config/exper/s2.html +25 -0
- data/empty_view/config/exper/varmint.rb +50 -0
- data/empty_view/config/facebook/credentials.txt +7 -0
- data/empty_view/config/facebook/facebook.rb +42 -0
- data/empty_view/config/facebook/fb.html +10 -0
- data/empty_view/config/facebook/fb.js.lt3 +15 -0
- data/empty_view/config/reddit/credentials.txt +6 -0
- data/empty_view/config/reddit/notes.txt +4 -0
- data/empty_view/config/reddit/reddit_post_url.py +34 -0
- data/empty_view/config/reddit/redpost.rb +43 -0
- data/empty_view/config/reddit/the-graffiti-wall.html +91 -0
- data/empty_view/config/twitter/credentials.txt +3 -0
- data/empty_view/config/twitter/tw.html +12 -0
- data/empty_view/config/twitter/tw.js +5 -0
- data/empty_view/config/twitter/twitter.rb +35 -0
- data/empty_view/posts/GIT_IS_DUMB +1 -0
- data/empty_view/remote/assets/GIT_IS_DUMB +1 -0
- data/empty_view/remote/banner/navbar/GIT_IS_DUMB +0 -0
- data/empty_view/remote/etc/GIT_IS_DUMB +1 -0
- data/empty_view/remote/permalink/GIT_IS_DUMB +1 -0
- data/empty_view/remote/widgets/ad/GIT_IS_DUMB +2 -0
- data/empty_view/remote/widgets/links/GIT_IS_DUMB +2 -0
- data/empty_view/remote/widgets/news/GIT_IS_DUMB +2 -0
- data/empty_view/remote/widgets/pages/GIT_IS_DUMB +2 -0
- data/empty_view/remote/widgets/pinned/GIT_IS_DUMB +2 -0
- data/empty_view/settings/features.txt +18 -0
- data/empty_view/settings/publish.txt +5 -0
- data/empty_view/settings/recent.txt +6 -0
- data/empty_view/settings/view.txt +4 -0
- data/empty_view/themes/standard/README +59 -0
- data/empty_view/themes/standard/banner/banner.lt3 +5 -0
- data/empty_view/themes/standard/banner/navbar/about.lt3 +18 -0
- data/empty_view/themes/standard/banner/navbar/contact.lt3 +18 -0
- data/empty_view/themes/standard/banner/navbar/faq.lt3 +1 -0
- data/empty_view/themes/standard/banner/navbar/list.data +3 -0
- data/empty_view/themes/standard/banner/top.lt3 +20 -0
- data/empty_view/themes/standard/blog/generate.lt3 +21 -0
- data/empty_view/themes/standard/blog/head.lt3 +16 -0
- data/empty_view/themes/standard/blog/index.lt3 +17 -0
- data/empty_view/themes/standard/blog/post_entry.lt3 +21 -0
- data/empty_view/themes/standard/etc/blog.css.lt3 +62 -0
- data/empty_view/themes/standard/etc/externals.lt3 +24 -0
- data/empty_view/themes/standard/etc/favicon.ico +0 -0
- data/empty_view/themes/standard/etc/misc.js +20 -0
- data/empty_view/themes/standard/post/generate.lt3 +38 -0
- data/empty_view/themes/standard/post/head.lt3 +7 -0
- data/empty_view/themes/standard/post/index.lt3 +24 -0
- data/empty_view/themes/standard/post/permalink.lt3 +32 -0
- data/empty_view/themes/standard/widgets/README +4 -0
- data/empty_view/themes/standard/widgets/ad/ad.lt3 +22 -0
- data/empty_view/themes/standard/widgets/ad/ad1.png +0 -0
- data/empty_view/themes/standard/widgets/ad/ad2.png +0 -0
- data/empty_view/themes/standard/widgets/ad/ad3.png +0 -0
- data/empty_view/themes/standard/widgets/ad/ad4.png +0 -0
- data/empty_view/themes/standard/widgets/bydates/README +2 -0
- data/empty_view/themes/standard/widgets/bydates/bydates.rb +18 -0
- data/empty_view/themes/standard/widgets/bydates/card.css +1 -0
- data/empty_view/themes/standard/widgets/bydates/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/bydates/main.css +2 -0
- data/empty_view/themes/standard/widgets/links/README +2 -0
- data/empty_view/themes/standard/widgets/links/card.css +1 -0
- data/empty_view/themes/standard/widgets/links/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/links/links.rb +90 -0
- data/empty_view/themes/standard/widgets/links/list.data +3 -0
- data/empty_view/themes/standard/widgets/links/main.css +2 -0
- data/empty_view/themes/standard/widgets/news/README +2 -0
- data/empty_view/themes/standard/widgets/news/card.css +1 -0
- data/empty_view/themes/standard/widgets/news/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/news/list.data +4 -0
- data/empty_view/themes/standard/widgets/news/main.css +2 -0
- data/empty_view/themes/standard/widgets/news/news.rb +88 -0
- data/empty_view/themes/standard/widgets/pages/README +2 -0
- data/empty_view/themes/standard/widgets/pages/card.css +1 -0
- data/empty_view/themes/standard/widgets/pages/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/pages/disclaim.lt3 +10 -0
- data/empty_view/themes/standard/widgets/pages/faq.lt3 +40 -0
- data/empty_view/themes/standard/widgets/pages/like-dislike.lt3 +11 -0
- data/empty_view/themes/standard/widgets/pages/list.data +4 -0
- data/empty_view/themes/standard/widgets/pages/local.rb +0 -0
- data/empty_view/themes/standard/widgets/pages/main.css +2 -0
- data/empty_view/themes/standard/widgets/pages/other-stuff.lt3 +10 -0
- data/empty_view/themes/standard/widgets/pages/pages.rb +95 -0
- data/empty_view/themes/standard/widgets/pinned/README +2 -0
- data/empty_view/themes/standard/widgets/pinned/card.css +1 -0
- data/empty_view/themes/standard/widgets/pinned/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/pinned/main.css +2 -0
- data/empty_view/themes/standard/widgets/pinned/pinned.rb +99 -0
- data/empty_view/themes/standard/widgets/search/README +2 -0
- data/empty_view/themes/standard/widgets/search/card.css +1 -0
- data/empty_view/themes/standard/widgets/search/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/search/main.css +2 -0
- data/empty_view/themes/standard/widgets/search/search.rb +18 -0
- data/empty_view/themes/standard/widgets/sitemap/README +2 -0
- data/empty_view/themes/standard/widgets/sitemap/card.css +1 -0
- data/empty_view/themes/standard/widgets/sitemap/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/sitemap/main.css +2 -0
- data/empty_view/themes/standard/widgets/sitemap/sitemap.rb +18 -0
- data/empty_view/themes/standard/widgets/tag-cloud/OLD-example.lt3 +12 -0
- data/empty_view/themes/standard/widgets/tag-cloud/README +2 -0
- data/empty_view/themes/standard/widgets/tag-cloud/card.css +1 -0
- data/empty_view/themes/standard/widgets/tag-cloud/custom.rb +1 -0
- data/empty_view/themes/standard/widgets/tag-cloud/main.css +2 -0
- data/empty_view/themes/standard/widgets/tag-cloud/tag-cloud.lt3 +3 -0
- data/empty_view/themes/standard/widgets/tag-cloud/tag-cloud.rb +18 -0
- data/lib/Javascript.stuff +69 -0
- data/lib/helpers-blog.rb +155 -0
- data/lib/helpers-repl.rb +201 -0
- data/lib/liveblog.rb +825 -0
- data/lib/logging.rb +44 -0
- data/lib/lowlevel.rb +73 -0
- data/lib/pathmagic.rb +14 -0
- data/lib/post.rb +211 -0
- data/lib/processing.rb +60 -0
- data/lib/publish.rb +73 -0
- data/lib/repl.rb +597 -0
- data/lib/runeblog.rb +773 -0
- data/lib/runeblog_version.rb +50 -0
- data/lib/view.rb +69 -0
- data/runeblog.gemspec +42 -0
- data/test/austin.rb +158 -0
- data/test/fakeimage.jpg +0 -0
- data/test/general_test.rb +304 -0
- data/test/make_blog.rb +196 -0
- data/test/test +3 -0
- metadata +242 -0
data/lib/helpers-repl.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
|
2
|
+
# Reopening...
|
3
|
+
|
4
|
+
make_exception(:CantOpen, "Can't open '$1'")
|
5
|
+
make_exception(:CantDelete, "Can't open '$1'")
|
6
|
+
make_exception(:InternalError, "Glitch: $1 got arg '$2'")
|
7
|
+
make_exception(:CantCopy, "Can't copy $1 to $2")
|
8
|
+
|
9
|
+
|
10
|
+
module RuneBlog::REPL
|
11
|
+
Patterns =
|
12
|
+
{"help" => :cmd_help,
|
13
|
+
"h" => :cmd_help,
|
14
|
+
"version" => :cmd_version,
|
15
|
+
"v" => :cmd_version,
|
16
|
+
"list views" => :cmd_list_views,
|
17
|
+
"lsv" => :cmd_list_views,
|
18
|
+
"clear" => :cmd_clear,
|
19
|
+
|
20
|
+
"new view $name" => :cmd_new_view,
|
21
|
+
|
22
|
+
"tags" => :cmd_tags,
|
23
|
+
"import" => :cmd_import,
|
24
|
+
|
25
|
+
"new post" => :cmd_new_post,
|
26
|
+
"p" => :cmd_new_post,
|
27
|
+
"post" => :cmd_new_post,
|
28
|
+
|
29
|
+
"change view $name" => :cmd_change_view,
|
30
|
+
"cv $name" => :cmd_change_view,
|
31
|
+
"cv" => :cmd_change_view, # 0-arity must come second
|
32
|
+
|
33
|
+
"config" => :cmd_config,
|
34
|
+
"manage $widget" => :cmd_manage,
|
35
|
+
|
36
|
+
"legacy" => :cmd_legacy,
|
37
|
+
|
38
|
+
"list posts" => :cmd_list_posts,
|
39
|
+
"lsp" => :cmd_list_posts,
|
40
|
+
|
41
|
+
"list drafts" => :cmd_list_drafts,
|
42
|
+
"lsd" => :cmd_list_drafts,
|
43
|
+
|
44
|
+
"list assets" => :cmd_list_assets,
|
45
|
+
"lsa" => :cmd_list_assets,
|
46
|
+
|
47
|
+
"pages" => :cmd_pages,
|
48
|
+
|
49
|
+
"delete >postid" => :cmd_remove_post,
|
50
|
+
"undel $postid" => :cmd_undelete_post,
|
51
|
+
|
52
|
+
"edit $postid" => :cmd_edit_post,
|
53
|
+
"ed $postid" => :cmd_edit_post,
|
54
|
+
"e $postid" => :cmd_edit_post,
|
55
|
+
|
56
|
+
"preview" => :cmd_preview,
|
57
|
+
|
58
|
+
"browse" => :cmd_browse,
|
59
|
+
|
60
|
+
"rebuild" => :cmd_rebuild,
|
61
|
+
|
62
|
+
"publish" => :cmd_publish,
|
63
|
+
|
64
|
+
"ssh" => :cmd_ssh,
|
65
|
+
|
66
|
+
"q" => :cmd_quit,
|
67
|
+
"quit" => :cmd_quit
|
68
|
+
}
|
69
|
+
|
70
|
+
Abbr = {
|
71
|
+
"h" => :cmd_help,
|
72
|
+
"v" => :cmd_version,
|
73
|
+
"lsv" => :cmd_list_views,
|
74
|
+
"p" => :cmd_new_post,
|
75
|
+
"cv $name" => :cmd_change_view,
|
76
|
+
"cv" => :cmd_change_view, # 0-arity must come second
|
77
|
+
"lsp" => :cmd_list_posts,
|
78
|
+
"lsd" => :cmd_list_drafts,
|
79
|
+
"list assets" => :cmd_list_assets,
|
80
|
+
"lsa" => :cmd_list_assets,
|
81
|
+
"rm $postid" => :cmd_remove_post,
|
82
|
+
"ed $postid" => :cmd_edit_post,
|
83
|
+
"e $postid" => :cmd_edit_post,
|
84
|
+
"q" => :cmd_quit
|
85
|
+
}
|
86
|
+
|
87
|
+
Regexes = {}
|
88
|
+
Patterns.each_pair do |pat, meth|
|
89
|
+
rx = "^" + pat
|
90
|
+
rx.gsub!(/ /, " +")
|
91
|
+
rx.gsub!(/\$(\w+) */) { " *(?<#{$1}>\\w+)" }
|
92
|
+
# How to handle multiple optional args?
|
93
|
+
rx.sub!(/>(\w+)$/) { "(.+)" }
|
94
|
+
rx << "$"
|
95
|
+
rx = Regexp.new(rx)
|
96
|
+
Regexes[rx] = meth
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.choose_method(cmd)
|
100
|
+
cmd = cmd.strip
|
101
|
+
found = nil
|
102
|
+
params = nil
|
103
|
+
Regexes.each_pair do |rx, meth|
|
104
|
+
m = cmd.match(rx)
|
105
|
+
result = m ? m.to_a : nil
|
106
|
+
next unless result
|
107
|
+
found = meth
|
108
|
+
params = m[1]
|
109
|
+
end
|
110
|
+
meth = found || :cmd_INVALID
|
111
|
+
params = cmd if meth == :cmd_INVALID
|
112
|
+
[meth, params]
|
113
|
+
end
|
114
|
+
|
115
|
+
def ask(prompt, meth = :to_s)
|
116
|
+
print prompt
|
117
|
+
gets.chomp.send(meth)
|
118
|
+
end
|
119
|
+
|
120
|
+
def ask!(prompt, meth = :to_s)
|
121
|
+
ask(fx(prompt, :bold), meth)
|
122
|
+
end
|
123
|
+
|
124
|
+
def reset_output(initial = "")
|
125
|
+
@out ||= ""
|
126
|
+
@out.replace initial
|
127
|
+
end
|
128
|
+
|
129
|
+
def flush_output(initial = "")
|
130
|
+
CantOpen
|
131
|
+
@out ||= ""
|
132
|
+
puts @out
|
133
|
+
reset_output
|
134
|
+
end
|
135
|
+
|
136
|
+
def output(str) # \n and indent
|
137
|
+
@out ||= ""
|
138
|
+
@out << " " + str.to_s
|
139
|
+
end
|
140
|
+
|
141
|
+
def outstr(str) # indent
|
142
|
+
@out ||= ""
|
143
|
+
@out << str
|
144
|
+
end
|
145
|
+
|
146
|
+
def output!(str) # \n and indent
|
147
|
+
@out ||= ""
|
148
|
+
@out << " " + str
|
149
|
+
end
|
150
|
+
|
151
|
+
def output_newline(n = 1)
|
152
|
+
@out ||= ""
|
153
|
+
n.times { @out << "\n" }
|
154
|
+
end
|
155
|
+
|
156
|
+
def check_empty(arg)
|
157
|
+
raise InternalError(caller[0], arg.inspect) unless arg.nil?
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_integer(arg)
|
161
|
+
Integer(arg)
|
162
|
+
rescue
|
163
|
+
raise ArgumentError, "'#{arg}' is not an integer"
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_file_exists(file)
|
167
|
+
raise FileNotFound(file) unless File.exist?(file)
|
168
|
+
end
|
169
|
+
|
170
|
+
def error_cant_delete(files)
|
171
|
+
case files
|
172
|
+
when String
|
173
|
+
raise CantDelete(files)
|
174
|
+
when Array
|
175
|
+
raise CantDelete(files.join("\n"))
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def colored_slug(slug)
|
180
|
+
slug[0..3] + slug[4..-1]
|
181
|
+
end
|
182
|
+
|
183
|
+
def tags_for_view(vname = @blog.view)
|
184
|
+
Dir.chdir(vname) do
|
185
|
+
fname = "tagpool"
|
186
|
+
if File.exist?(fname)
|
187
|
+
tags = File.readlines(fname).map(&:chomp)
|
188
|
+
else
|
189
|
+
tags = []
|
190
|
+
end
|
191
|
+
end
|
192
|
+
tags.sort
|
193
|
+
end
|
194
|
+
|
195
|
+
def all_tags
|
196
|
+
all = []
|
197
|
+
@blog.views.each {|view| all.append(*tags_for_view(view)) }
|
198
|
+
all.sort + ["NEW TAG"]
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
data/lib/liveblog.rb
ADDED
@@ -0,0 +1,825 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'pp'
|
3
|
+
require 'date'
|
4
|
+
require 'find'
|
5
|
+
|
6
|
+
require 'runeblog'
|
7
|
+
require 'pathmagic'
|
8
|
+
require 'processing'
|
9
|
+
|
10
|
+
# top = Livetext::Path + "/../plugin/liveblog/"
|
11
|
+
# eval(File.read("#{top}/testing.rb"))
|
12
|
+
|
13
|
+
|
14
|
+
def init_liveblog # FIXME - a lot of this logic sucks
|
15
|
+
dir = Dir.pwd.sub(/\.blogs.*/, "")
|
16
|
+
@blog = nil
|
17
|
+
Dir.chdir(dir) { @blog = RuneBlog.new }
|
18
|
+
@root = @blog.root
|
19
|
+
@view = @blog.view
|
20
|
+
@view_name = @blog.view.name unless @view.nil?
|
21
|
+
@vdir = @blog.view.dir rescue "NONAME"
|
22
|
+
@version = RuneBlog::VERSION
|
23
|
+
@theme = @vdir/:themes/:standard
|
24
|
+
rescue
|
25
|
+
raise "Only works inside a blog repo"
|
26
|
+
end
|
27
|
+
|
28
|
+
##################
|
29
|
+
# "dot" commands
|
30
|
+
##################
|
31
|
+
|
32
|
+
def dropcap
|
33
|
+
# Bad form: adds another HEAD
|
34
|
+
text = _data
|
35
|
+
_out " "
|
36
|
+
letter = text[0]
|
37
|
+
remain = text[1..-1]
|
38
|
+
_out %[<div class='mydrop'>#{letter}</div>]
|
39
|
+
_out %[<div style="padding-top: 1px">#{remain}]
|
40
|
+
end
|
41
|
+
|
42
|
+
def post
|
43
|
+
@meta = OpenStruct.new
|
44
|
+
@meta.num = _args[0]
|
45
|
+
_out " <!-- Post number #{@meta.num} -->\n "
|
46
|
+
end
|
47
|
+
|
48
|
+
def _got_python?
|
49
|
+
# Dumb - fix later - check up front as needed
|
50
|
+
# Should also check for praw lib
|
51
|
+
str = `which python3`
|
52
|
+
str.length > 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def _reddit_post_url(vdir, title, url)
|
56
|
+
_got_python?
|
57
|
+
tmpfile = "/tmp/reddit-post-url.txt"
|
58
|
+
File.open(tmpfile, "w") do |tmp|
|
59
|
+
tmp.puts "[Post] " + title
|
60
|
+
tmp.puts url
|
61
|
+
end
|
62
|
+
rid = nil
|
63
|
+
Dir.chdir(vdir/:config) { rid = `python3 reddit/reddit_post_url.py` }
|
64
|
+
system("rm #{tmpfile}")
|
65
|
+
rid # returns reddit id
|
66
|
+
end
|
67
|
+
|
68
|
+
def post_trailer
|
69
|
+
perma = _var("publish.proto") + "://" + _var("publish.server") +
|
70
|
+
"/" + _var("publish.path") + "/" + _var("post.aslug") +
|
71
|
+
".html"
|
72
|
+
tags = _var("post.tags")
|
73
|
+
taglist = tags.empty? ? "" : "Tags: #{tags}"
|
74
|
+
|
75
|
+
reddit_enabled = @blog.features["reddit"] rescue nil
|
76
|
+
reddit_txt = ""
|
77
|
+
if reddit_enabled
|
78
|
+
vdir = @blog.root/:views/@blog.view
|
79
|
+
nslug = "#{_var("post.num")}-#{_var("post.aslug")}"
|
80
|
+
rid_file = vdir/:posts/nslug/"reddit.id"
|
81
|
+
rid = File.exist?(rid_file) ? File.read(rid_file).chomp : nil
|
82
|
+
if rid.nil?
|
83
|
+
title = _var("title")
|
84
|
+
rid = _reddit_post_url(vdir, title, perma)
|
85
|
+
dump(rid, rid_file)
|
86
|
+
end
|
87
|
+
reddit_txt = <<~HTML
|
88
|
+
<hr>
|
89
|
+
<script src='https://redditjs.com/post.js'
|
90
|
+
data-url="#{rid}" data-width=800 ></script>
|
91
|
+
HTML
|
92
|
+
# damned syntax highlighting </>
|
93
|
+
end
|
94
|
+
_out <<~HTML
|
95
|
+
<table width=100%><tr>
|
96
|
+
<td width=10%><a style="text-decoration: none" href="javascript:history.go(-1)">[Back]</a></td>
|
97
|
+
<td width=10%><a style="text-decoration: none" href="#{perma}"> [permalink] </a></td>
|
98
|
+
<td width=80% align=right><font size=-3>#{taglist}</font></td></tr></table>
|
99
|
+
#{reddit_txt}
|
100
|
+
HTML
|
101
|
+
end
|
102
|
+
|
103
|
+
def faq
|
104
|
+
@faq_count ||= 0
|
105
|
+
_out "<br>" if @faq_count == 0
|
106
|
+
@faq_count += 1
|
107
|
+
ques = _data.chomp
|
108
|
+
ans = _body_text
|
109
|
+
id = "faq#@faq_count"
|
110
|
+
_out %[ <a data-toggle="collapse" href="##{id}" role="button" aria-expanded="false" aria-controls="collapseExample"><font size=+3>⌄</font></a>]
|
111
|
+
_out %[ <b>#{ques}</b>]
|
112
|
+
_out %[<div class="collapse" id="#{id}"><br><font size=+1> #{ans}</font></div>\n]
|
113
|
+
_out "<br>" unless @faq_count == 1
|
114
|
+
_optional_blank_line
|
115
|
+
end
|
116
|
+
|
117
|
+
def backlink
|
118
|
+
_out %[<br><a href="javascript:history.go(-1)">[Back]</a>]
|
119
|
+
end
|
120
|
+
|
121
|
+
def code
|
122
|
+
lines = _body_text
|
123
|
+
_out "<font size=+1><pre>\n#{lines}\n</pre></font>"
|
124
|
+
end
|
125
|
+
|
126
|
+
def _read_navbar_data
|
127
|
+
vdir = @blog.root/:views/@blog.view
|
128
|
+
dir = vdir/"themes/standard/banner/navbar/"
|
129
|
+
datafile = dir/"list.data"
|
130
|
+
_get_data(datafile)
|
131
|
+
end
|
132
|
+
|
133
|
+
def banner
|
134
|
+
count = 0
|
135
|
+
bg = "white" # outside loop
|
136
|
+
wide = nil
|
137
|
+
high = 250
|
138
|
+
str2 = ""
|
139
|
+
navbar = nil
|
140
|
+
vdir = @blog.root/:views/@blog.view
|
141
|
+
lines = _body.to_a
|
142
|
+
|
143
|
+
lines.each do |line|
|
144
|
+
count += 1
|
145
|
+
tag, *data = line.split
|
146
|
+
data ||= []
|
147
|
+
case tag
|
148
|
+
when "width"; wide = data[0]
|
149
|
+
when "height"; high = data[0]
|
150
|
+
when "bgcolor"; bg = data[0] || "white"
|
151
|
+
when "image"
|
152
|
+
image = data[0] || "banner.jpg"
|
153
|
+
image = "banner"/image
|
154
|
+
wide = data[0]
|
155
|
+
width = wide ? "width=#{wide}" : ""
|
156
|
+
str2 << " <td><img src=#{image} #{width} height=#{high}></img></td>" + "\n"
|
157
|
+
when "svg_title"
|
158
|
+
stuff, hash = _svg_title(*data)
|
159
|
+
wide = hash["width"]
|
160
|
+
str2 << " <td width=#{wide}>#{stuff}</td>" + "\n"
|
161
|
+
when "text"
|
162
|
+
data[0] ||= "top.html"
|
163
|
+
file = "banner"/data[0]
|
164
|
+
if ! File.exist?(file)
|
165
|
+
src = file.sub(/html$/, "lt3")
|
166
|
+
if File.exist?(src)
|
167
|
+
preprocess src: src, dst: file, call: ".nopara" # , vars: @blog.view.globals
|
168
|
+
else
|
169
|
+
raise "Neither #{file} nor #{src} found"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
str2 << "<td>" + File.read(file) + "</td>" + "\n"
|
173
|
+
when "navbar"
|
174
|
+
navbar = _make_navbar # horiz is default
|
175
|
+
when "vnavbar"
|
176
|
+
navbar = _make_navbar(:vert)
|
177
|
+
when "break"
|
178
|
+
str2 << " </tr>\n <tr>" + "\n"
|
179
|
+
else
|
180
|
+
str2 << " '#{tag}' isn't known" + "\n"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
_out <<~HTML
|
184
|
+
<table width=100% bgcolor=#{bg}>
|
185
|
+
<tr>
|
186
|
+
#{str2}
|
187
|
+
</tr>
|
188
|
+
</table>
|
189
|
+
HTML
|
190
|
+
_out navbar if navbar
|
191
|
+
rescue => err
|
192
|
+
STDERR.puts "err = #{err}"
|
193
|
+
STDERR.puts err.backtrace.join("\n")
|
194
|
+
gets
|
195
|
+
end
|
196
|
+
|
197
|
+
def _svg_title(*args)
|
198
|
+
width = "95%"
|
199
|
+
height = 90
|
200
|
+
bgcolor = "black"
|
201
|
+
style = nil
|
202
|
+
size = ""
|
203
|
+
font = "sans-serif"
|
204
|
+
color = "white"
|
205
|
+
xy = "5,5"
|
206
|
+
align = "center"
|
207
|
+
style2 = nil
|
208
|
+
size2 = ""
|
209
|
+
font2 = "sans-serif"
|
210
|
+
color2 = "white"
|
211
|
+
xy2 = "5,5"
|
212
|
+
align2 = "center"
|
213
|
+
|
214
|
+
e = args.each
|
215
|
+
hash = {} # TODO get rid of hash??
|
216
|
+
|
217
|
+
valid = %w[width height bgcolor style size font color xy
|
218
|
+
align style2 size2 font2 color2 xy2 align2]
|
219
|
+
os = OpenStruct.new
|
220
|
+
loop do
|
221
|
+
arg = e.next
|
222
|
+
arg = arg.chop
|
223
|
+
raise "Don't know '#{arg}'" unless valid.include?(arg)
|
224
|
+
os.send(arg+"=", e.next)
|
225
|
+
end
|
226
|
+
x, y = xy.split(",")
|
227
|
+
x2, y2 = xy2.split(",")
|
228
|
+
names = %w[x y x2 y2] + valid
|
229
|
+
names.each {|name| hash[name] = os.send(name) }
|
230
|
+
result = <<~HTML
|
231
|
+
<svg width="#{width}" height="#{height}"
|
232
|
+
viewBox="0 0 #{width} #{height}">
|
233
|
+
<defs>
|
234
|
+
<linearGradient id="grad1" x1="100%" y1="100%" x2="0%" y2="100%">
|
235
|
+
<stop offset="0%" style="stop-color:rgb(198,198,228);stop-opacity:1" />
|
236
|
+
<stop offset="100%" style="stop-color:rgb(30,30,50);stop-opacity:1" />
|
237
|
+
</linearGradient>
|
238
|
+
</defs>
|
239
|
+
<style>
|
240
|
+
.title { font: #{style} #{size} #{font}; fill: #{color} }
|
241
|
+
.subtitle { font: #{style2} #{size2} #{font2}; fill: #{color2} }
|
242
|
+
</style>
|
243
|
+
<rect x="10" y="10" rx="10" ry="10" width="#{width}" height="#{height}" fill="url(#grad1)"/>
|
244
|
+
<text text-anchor="#{align}" x="#{x}" y="#{y}" class="title">#{Livetext::Vars["view.title"]} </text>
|
245
|
+
<text text-anchor="#{align2}" x="#{x2}" y="#{y2}" class="subtitle">#{Livetext::Vars["view.subtitle"]} </text>
|
246
|
+
</svg>
|
247
|
+
<!-- ^ how does syntax highlighting get messed up? </svg> -->
|
248
|
+
HTML
|
249
|
+
[result, hash]
|
250
|
+
end
|
251
|
+
|
252
|
+
def quote
|
253
|
+
_passthru "<blockquote>"
|
254
|
+
_passthru _body.join(" ")
|
255
|
+
_passthru "</blockquote>"
|
256
|
+
_optional_blank_line
|
257
|
+
end
|
258
|
+
|
259
|
+
def categories # does nothing right now
|
260
|
+
end
|
261
|
+
|
262
|
+
def style
|
263
|
+
fname = _args[0]
|
264
|
+
_passthru %[<link rel="stylesheet" href="???/etc/#{fname}')">]
|
265
|
+
end
|
266
|
+
|
267
|
+
# Move elsewhere later!
|
268
|
+
|
269
|
+
def h1; _passthru "<h1>#{@_data}</h1>"; end
|
270
|
+
def h2; _passthru "<h2>#{@_data}</h2>"; end
|
271
|
+
def h3; _passthru "<h3>#{@_data}</h3>"; end
|
272
|
+
def h4; _passthru "<h4>#{@_data}</h4>"; end
|
273
|
+
def h5; _passthru "<h5>#{@_data}</h5>"; end
|
274
|
+
def h6; _passthru "<h6>#{@_data}</h6>"; end
|
275
|
+
|
276
|
+
def hr; _passthru "<hr>"; end
|
277
|
+
|
278
|
+
def nlist
|
279
|
+
_out "<ol>"
|
280
|
+
_body {|line| _out "<li>#{line}</li>" }
|
281
|
+
_out "</ol>"
|
282
|
+
_optional_blank_line
|
283
|
+
end
|
284
|
+
|
285
|
+
def list
|
286
|
+
_out "<ul>"
|
287
|
+
_body {|line| _out "<li>#{line}</li>" }
|
288
|
+
_out "</ul>"
|
289
|
+
_optional_blank_line
|
290
|
+
end
|
291
|
+
|
292
|
+
def list!
|
293
|
+
_out "<ul>"
|
294
|
+
lines = _body.each
|
295
|
+
loop do
|
296
|
+
line = lines.next
|
297
|
+
line = _format(line)
|
298
|
+
if line[0] == " "
|
299
|
+
_out line
|
300
|
+
else
|
301
|
+
_out "<li>#{line}</li>"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
_out "</ul>"
|
305
|
+
_optional_blank_line
|
306
|
+
end
|
307
|
+
|
308
|
+
### inset
|
309
|
+
|
310
|
+
def inset
|
311
|
+
lines = _body
|
312
|
+
box = ""
|
313
|
+
output = []
|
314
|
+
lines.each do |line|
|
315
|
+
line = line
|
316
|
+
case line[0]
|
317
|
+
when "/" # Only into inset
|
318
|
+
line[0] = ' '
|
319
|
+
box << line
|
320
|
+
line.replace(" ")
|
321
|
+
when "|" # Into inset and body
|
322
|
+
line[0] = ' '
|
323
|
+
box << line
|
324
|
+
output << line
|
325
|
+
else # Only into body
|
326
|
+
output << line
|
327
|
+
end
|
328
|
+
end
|
329
|
+
lr = _args.first
|
330
|
+
wide = _args[1] || "25"
|
331
|
+
stuff = "<div style='float:#{lr}; width: #{wide}%; padding:8px; padding-right:12px'>"
|
332
|
+
stuff << '<b><i>' + box + '</i></b></div>'
|
333
|
+
_out "</p>" # kludge!! nopara
|
334
|
+
0.upto(2) {|i| _passthru output[i] }
|
335
|
+
_passthru stuff
|
336
|
+
3.upto(output.length-1) {|i| _passthru output[i] }
|
337
|
+
_out "<p>" # kludge!! para
|
338
|
+
_optional_blank_line
|
339
|
+
end
|
340
|
+
|
341
|
+
def title
|
342
|
+
raise "'post' was not called" unless @meta
|
343
|
+
title = @_data.chomp
|
344
|
+
@meta.title = title
|
345
|
+
setvar :title, title
|
346
|
+
# FIXME refactor -- just output variables for a template
|
347
|
+
_optional_blank_line
|
348
|
+
end
|
349
|
+
|
350
|
+
def pubdate
|
351
|
+
raise "'post' was not called" unless @meta
|
352
|
+
_debug "data = #@_data"
|
353
|
+
# Check for discrepancy?
|
354
|
+
match = /(\d{4}).(\d{2}).(\d{2})/.match @_data
|
355
|
+
junk, y, m, d = match.to_a
|
356
|
+
y, m, d = y.to_i, m.to_i, d.to_i
|
357
|
+
@meta.date = ::Date.new(y, m, d)
|
358
|
+
@meta.pubdate = "%04d-%02d-%02d" % [y, m, d]
|
359
|
+
_optional_blank_line
|
360
|
+
end
|
361
|
+
|
362
|
+
def image # primitive so far
|
363
|
+
_debug "img: huh? <img src=#{_args.first}></img>"
|
364
|
+
fname = _args.first
|
365
|
+
path = :assets/fname
|
366
|
+
_out "<img src=#{path}></img>"
|
367
|
+
_optional_blank_line
|
368
|
+
end
|
369
|
+
|
370
|
+
def tags
|
371
|
+
raise "'post' was not called" unless @meta
|
372
|
+
_debug "args = #{_args}"
|
373
|
+
@meta.tags = _args.dup || []
|
374
|
+
_optional_blank_line
|
375
|
+
end
|
376
|
+
|
377
|
+
def views
|
378
|
+
raise "'post' was not called" unless @meta
|
379
|
+
_debug "data = #{_args}"
|
380
|
+
@meta.views = _args.dup
|
381
|
+
_optional_blank_line
|
382
|
+
end
|
383
|
+
|
384
|
+
def pin
|
385
|
+
raise "'post' was not called" unless @meta
|
386
|
+
_debug "data = #{_args}" # verify only valid views?
|
387
|
+
pinned = @_args
|
388
|
+
@meta.pinned = pinned
|
389
|
+
pinned.each do |pinview|
|
390
|
+
dir = @blog.root/:views/pinview/"themes/standard/widgets/pinned/"
|
391
|
+
datafile = dir/"list.data"
|
392
|
+
pins = _get_data?(datafile)
|
393
|
+
pins << "#{@meta.num} #{@meta.title}\n"
|
394
|
+
pins.uniq!
|
395
|
+
File.open(datafile, "w") {|out| pins.each {|pin| out.puts pin } }
|
396
|
+
end
|
397
|
+
_optional_blank_line
|
398
|
+
rescue => err
|
399
|
+
STDERR.puts "err = #{err}"
|
400
|
+
STDERR.puts err.backtrace.join("\n")
|
401
|
+
gets
|
402
|
+
end
|
403
|
+
|
404
|
+
def write_post
|
405
|
+
raise "'post' was not called" unless @meta
|
406
|
+
@meta.views = @meta.views.join(" ") if @meta.views.is_a? Array
|
407
|
+
@meta.tags = @meta.tags.join(" ") if @meta.tags.is_a? Array
|
408
|
+
_write_metadata
|
409
|
+
rescue => err
|
410
|
+
puts "err = #{err}"
|
411
|
+
puts err.backtrace.join("\n")
|
412
|
+
end
|
413
|
+
|
414
|
+
def teaser
|
415
|
+
raise "'post' was not called" unless @meta
|
416
|
+
text = _body_text
|
417
|
+
@meta.teaser = text
|
418
|
+
setvar :teaser, @meta.teaser
|
419
|
+
if _args[0] == "dropcap" # FIXME doesn't work yet!
|
420
|
+
letter, remain = text[0], text[1..-1]
|
421
|
+
_out %[<div class='mydrop'>#{letter}</div>]
|
422
|
+
_out %[<div style="padding-top: 1px">#{remain}] + "\n"
|
423
|
+
else
|
424
|
+
_out @meta.teaser + "\n"
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
def finalize
|
429
|
+
return unless @meta
|
430
|
+
return @meta if @blog.nil?
|
431
|
+
|
432
|
+
@slug = @blog.make_slug(@meta)
|
433
|
+
slug_dir = @slug
|
434
|
+
@postdir = @blog.view.dir/:posts/slug_dir
|
435
|
+
write_post
|
436
|
+
@meta
|
437
|
+
end
|
438
|
+
|
439
|
+
def head # Does NOT output <head> tags
|
440
|
+
args = _args
|
441
|
+
args.each do |inc|
|
442
|
+
self.data = inc
|
443
|
+
_include
|
444
|
+
end
|
445
|
+
# Depends on vars: title, desc, host
|
446
|
+
defaults = {}
|
447
|
+
defaults = { "charset" => %[<meta charset="utf-8">],
|
448
|
+
"http-equiv" => %[<meta http-equiv="X-UA-Compatible" content="IE=edge">],
|
449
|
+
"title" => %[<title>\n #{_var("view.title")} | #{_var("view.subtitle")}\n </title>],
|
450
|
+
"generator" => %[<meta name="generator" content="Runeblog v #@version">],
|
451
|
+
"og:title" => %[<meta property="og:title" content="#{_var("view.title")}">],
|
452
|
+
"og:locale" => %[<meta property="og:locale" content="#{_var(:locale)}">],
|
453
|
+
"description" => %[<meta name="description" content="#{_var("view.subtitle")}">],
|
454
|
+
"og:description" => %[<meta property="og:description" content="#{_var("view.subtitle")}">],
|
455
|
+
"linkc" => %[<link rel="canonical" href="#{_var(:host)}">],
|
456
|
+
"og:url" => %[<meta property="og:url" content="#{_var(:host)}">],
|
457
|
+
"og:site_name" => %[<meta property="og:site_name" content="#{_var("view.title")}">],
|
458
|
+
# "style" => %[<link rel="stylesheet" href="etc/blog.css">],
|
459
|
+
# ^ FIXME
|
460
|
+
"feed" => %[<link type="application/atom+xml" rel="alternate" href="#{_var(:host)}/feed.xml" title="#{_var("view.title")}">],
|
461
|
+
"favicon" => %[<link rel="shortcut icon" type="image/x-icon" href="etc/favicon.ico">\n <link rel="apple-touch-icon" href="etc/favicon.ico">]
|
462
|
+
}
|
463
|
+
result = {}
|
464
|
+
lines = _body
|
465
|
+
lines.each do |line|
|
466
|
+
line.chomp
|
467
|
+
word, remain = line.split(" ", 2)
|
468
|
+
case word
|
469
|
+
when "viewport"
|
470
|
+
result["viewport"] = %[<meta name="viewport" content="#{remain}">]
|
471
|
+
when "script" # FIXME this is broken
|
472
|
+
file = remain
|
473
|
+
text = File.read(file)
|
474
|
+
result["script"] = Livetext.new.transform(text)
|
475
|
+
when "style"
|
476
|
+
result["style"] = %[<link rel="stylesheet" href="etc/#{remain}">]
|
477
|
+
# Later: allow other overrides
|
478
|
+
when ""; break
|
479
|
+
else
|
480
|
+
if defaults[word]
|
481
|
+
result[word] = %[<meta property="#{word}" content="#{remain}">]
|
482
|
+
else
|
483
|
+
puts "Unknown tag '#{word}'"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
hash = defaults.dup.update(result) # FIXME collisions?
|
488
|
+
|
489
|
+
hash.each_value {|x| _out " " + x }
|
490
|
+
end
|
491
|
+
|
492
|
+
########## newer stuff...
|
493
|
+
|
494
|
+
def meta
|
495
|
+
args = _args
|
496
|
+
enum = args.each
|
497
|
+
str = "<meta"
|
498
|
+
arg = enum.next
|
499
|
+
loop do
|
500
|
+
if arg.end_with?(":")
|
501
|
+
str << " " << arg[0..-2] << "="
|
502
|
+
a2 = enum.next
|
503
|
+
str << %["#{a2}"]
|
504
|
+
else
|
505
|
+
STDERR.puts "=== meta error?"
|
506
|
+
end
|
507
|
+
arg = enum.next
|
508
|
+
end
|
509
|
+
str << ">"
|
510
|
+
_out str
|
511
|
+
end
|
512
|
+
|
513
|
+
def recent_posts # side-effect
|
514
|
+
_out <<-HTML
|
515
|
+
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12">
|
516
|
+
<iframe id="main" style="width: 70vw; height: 100vh; position: relative;"
|
517
|
+
src='recent.html' width=100% frameborder="0" allowfullscreen>
|
518
|
+
</iframe>
|
519
|
+
</div>
|
520
|
+
HTML
|
521
|
+
end
|
522
|
+
|
523
|
+
def _make_class_name(app)
|
524
|
+
if app =~ /[-_]/
|
525
|
+
words = app.split(/[-_]/)
|
526
|
+
name = words.map(&:capitalize).join
|
527
|
+
else
|
528
|
+
name = app.capitalize
|
529
|
+
end
|
530
|
+
return name
|
531
|
+
end
|
532
|
+
|
533
|
+
def _load_local(widget)
|
534
|
+
Dir.chdir("widgets/#{widget}") do
|
535
|
+
rclass = _make_class_name(widget)
|
536
|
+
found = (require("./#{widget}") if File.exist?("#{widget}.rb"))
|
537
|
+
code = found ? ::RuneBlog::Widget.class_eval(rclass) : nil
|
538
|
+
code
|
539
|
+
end
|
540
|
+
rescue => err
|
541
|
+
STDERR.puts err.to_s
|
542
|
+
STDERR.puts err.backtrace.join("\n")
|
543
|
+
sleep 6; RubyText.stop
|
544
|
+
exit
|
545
|
+
end
|
546
|
+
|
547
|
+
def _handle_standard_widget(tag)
|
548
|
+
wtag = :widgets/tag
|
549
|
+
code = _load_local(tag)
|
550
|
+
if code
|
551
|
+
Dir.chdir(wtag) do
|
552
|
+
widget = code.new(@blog)
|
553
|
+
widget.build
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def sidebar
|
559
|
+
_debug "--- handling sidebar\r"
|
560
|
+
if _args.include? "off"
|
561
|
+
_body { } # iterate, do nothing
|
562
|
+
return
|
563
|
+
end
|
564
|
+
|
565
|
+
_out %[<div class="col-lg-3 col-md-3 col-sm-3 col-xs-12">]
|
566
|
+
|
567
|
+
standard = %w[pinned pages links news]
|
568
|
+
|
569
|
+
_body do |token|
|
570
|
+
tag = token.chomp.strip.downcase
|
571
|
+
wtag = :widgets/tag
|
572
|
+
raise "Can't find #{wtag}" unless Dir.exist?(wtag)
|
573
|
+
tcard = "#{tag}-card.html"
|
574
|
+
|
575
|
+
case
|
576
|
+
when standard.include?(tag)
|
577
|
+
_handle_standard_widget(tag)
|
578
|
+
when tag == "ad"
|
579
|
+
num = rand(1..4)
|
580
|
+
img = "widgets/ad/ad#{num}.png"
|
581
|
+
src, dst = img, @root/:views/@view_name/"remote/widgets/ad/"
|
582
|
+
system!("cp #{src} #{dst}")
|
583
|
+
File.open(wtag/"vars.lt3", "w") {|f| f.puts ".set ad.image = #{img}" }
|
584
|
+
preprocess cwd: wtag, src: tag, dst: tcard, call: ".nopara",
|
585
|
+
force: true # , debug: true # , deps: depend
|
586
|
+
end
|
587
|
+
|
588
|
+
_include_file wtag/tcard
|
589
|
+
end
|
590
|
+
_out %[</div>]
|
591
|
+
rescue => err
|
592
|
+
puts "err = #{err}"
|
593
|
+
puts err.backtrace.join("\n")
|
594
|
+
sleep 6; RubyText.stop
|
595
|
+
exit
|
596
|
+
end
|
597
|
+
|
598
|
+
def stylesheet
|
599
|
+
lines = _body
|
600
|
+
url = lines[0]
|
601
|
+
integ = lines[1]
|
602
|
+
cross = lines[2] || "anonymous"
|
603
|
+
_out %[<link rel="stylesheet" href="#{url}" integrity="#{integ}" crossorigin="#{cross}"></link>]
|
604
|
+
end
|
605
|
+
|
606
|
+
def script
|
607
|
+
lines = _body
|
608
|
+
url = lines[0]
|
609
|
+
integ = lines[1]
|
610
|
+
cross = lines[2] || "anonymous"
|
611
|
+
_out %[<script src="#{url}" integrity="#{integ}" crossorigin="#{cross}"></script>]
|
612
|
+
end
|
613
|
+
|
614
|
+
$Dot = self # Clunky! for dot commands called from Functions class
|
615
|
+
|
616
|
+
# Find a better way to do this?
|
617
|
+
|
618
|
+
class Livetext::Functions
|
619
|
+
|
620
|
+
def br(n="1")
|
621
|
+
# Thought: Maybe make a way for functions to "simply" call the
|
622
|
+
# dot command of the same name?? Is this trivial??
|
623
|
+
n = n.empty? ? 1 : n.to_i
|
624
|
+
"<br>"*n
|
625
|
+
end
|
626
|
+
|
627
|
+
def h1(param); "<h1>#{param}</h1>"; end
|
628
|
+
def h2(param); "<h2>#{param}</h2>"; end
|
629
|
+
def h3(param); "<h3>#{param}</h3>"; end
|
630
|
+
def h4(param); "<h4>#{param}</h4>"; end
|
631
|
+
def h5(param); "<h5>#{param}</h5>"; end
|
632
|
+
def h6(param); "<h6>#{param}</h6>"; end
|
633
|
+
|
634
|
+
def hr(param=nil)
|
635
|
+
$Dot.hr
|
636
|
+
end
|
637
|
+
|
638
|
+
def image(param)
|
639
|
+
"<img src='#{param}'></img>"
|
640
|
+
end
|
641
|
+
|
642
|
+
end
|
643
|
+
|
644
|
+
###### experimental...
|
645
|
+
|
646
|
+
class Livetext::Functions
|
647
|
+
def _var(name)
|
648
|
+
::Livetext::Vars[name] || "[:#{name} is undefined]"
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
###
|
653
|
+
|
654
|
+
|
655
|
+
def tag_cloud
|
656
|
+
title = _data
|
657
|
+
title = "Tag Cloud" if title.empty?
|
658
|
+
open = <<-HTML
|
659
|
+
<div class="card mb-3">
|
660
|
+
<div class="card-body">
|
661
|
+
<h5 class="card-title">
|
662
|
+
<button type="button" class="btn btn-primary" data-toggle="collapse" data-target="#tag-cloud">+</button>
|
663
|
+
#{title}
|
664
|
+
</h5>
|
665
|
+
<div class="collapse" id="tag-cloud">
|
666
|
+
HTML
|
667
|
+
_out open
|
668
|
+
_body do |line|
|
669
|
+
line.chomp!
|
670
|
+
url, classname, cdata = line.split(",", 3)
|
671
|
+
main = _main(url)
|
672
|
+
_out %[<a #{main} class="#{classname}">#{cdata}</a>]
|
673
|
+
end
|
674
|
+
close = %[ </div>\n </div>\n </div>]
|
675
|
+
_out close
|
676
|
+
end
|
677
|
+
|
678
|
+
def vnavbar
|
679
|
+
str = _make_navbar(:vert)
|
680
|
+
end
|
681
|
+
|
682
|
+
def hnavbar
|
683
|
+
str = _make_navbar # horiz is default
|
684
|
+
end
|
685
|
+
|
686
|
+
def navbar
|
687
|
+
str = _make_navbar # horiz is default
|
688
|
+
end
|
689
|
+
|
690
|
+
def _make_navbar(orient = :horiz)
|
691
|
+
vdir = @root/:views/@blog.view
|
692
|
+
title = _var("view.title")
|
693
|
+
|
694
|
+
if orient == :horiz
|
695
|
+
name = "navbar.html"
|
696
|
+
li1, li2 = "", ""
|
697
|
+
extra = "navbar-expand-lg"
|
698
|
+
list1 = list2 = ""
|
699
|
+
else
|
700
|
+
name = "vnavbar.html"
|
701
|
+
li1, li2 = '<li class="nav-item">', "</li>"
|
702
|
+
extra = ""
|
703
|
+
list1, list2 = '<l class="navbar-nav mr-auto">', "</ul>"
|
704
|
+
end
|
705
|
+
|
706
|
+
start = <<-HTML
|
707
|
+
<table><tr><td>
|
708
|
+
<nav class="navbar #{extra} navbar-light bg-light">
|
709
|
+
#{list1}
|
710
|
+
HTML
|
711
|
+
finish = <<-HTML
|
712
|
+
#{list2}
|
713
|
+
</nav>
|
714
|
+
</td></tr></table>
|
715
|
+
HTML
|
716
|
+
|
717
|
+
html_file = @blog.root/:views/@blog.view/"themes/standard/banner/navbar"/name
|
718
|
+
output = File.new(html_file, "w")
|
719
|
+
output.puts start
|
720
|
+
lines = _read_navbar_data
|
721
|
+
lines = ["index Home"] + lines unless _args.include?("nohome")
|
722
|
+
lines.each do |line|
|
723
|
+
basename, cdata = line.chomp.strip.split(" ", 2)
|
724
|
+
full = :banner/:navbar/basename+".html"
|
725
|
+
href_main = _main(full)
|
726
|
+
if basename == "index" # special case
|
727
|
+
output.puts %[#{li1} <a class="nav-link" href="index.html">#{cdata}<span class="sr-only">(current)</span></a> #{li2}]
|
728
|
+
else
|
729
|
+
dir = @blog.root/:views/@blog.view/"themes/standard/banner/navbar"
|
730
|
+
dest = vdir/"remote/banner/navbar"/basename+".html"
|
731
|
+
preprocess cwd: dir, src: basename, dst: dest, call: ".nopara" # , debug: true
|
732
|
+
output.puts %[#{li1} <a class="nav-link" #{href_main}>#{cdata}</a> #{li2}]
|
733
|
+
end
|
734
|
+
end
|
735
|
+
output.puts finish
|
736
|
+
output.close
|
737
|
+
return File.read(html_file)
|
738
|
+
end
|
739
|
+
|
740
|
+
|
741
|
+
##################
|
742
|
+
# helper methods
|
743
|
+
##################
|
744
|
+
|
745
|
+
def _html_body(file, css = nil)
|
746
|
+
file.puts "<html>"
|
747
|
+
if css
|
748
|
+
file.puts " <head>"
|
749
|
+
file.puts " <style>\n#{css}\n </style>"
|
750
|
+
file.puts " </head>"
|
751
|
+
end
|
752
|
+
file.puts " <body>"
|
753
|
+
yield
|
754
|
+
file.puts " </body>\n</html>"
|
755
|
+
end
|
756
|
+
|
757
|
+
def _errout(*args)
|
758
|
+
::STDERR.puts *args
|
759
|
+
end
|
760
|
+
|
761
|
+
def _passthru(line)
|
762
|
+
return if line.nil?
|
763
|
+
line = _format(line)
|
764
|
+
_out line + "\n"
|
765
|
+
_out "<p>" if line.empty? && ! @_nopara
|
766
|
+
end
|
767
|
+
|
768
|
+
def _passthru_noline(line)
|
769
|
+
return if line.nil?
|
770
|
+
line = _format(line)
|
771
|
+
_out line
|
772
|
+
_out "<p>" if line.empty? && ! @_nopara
|
773
|
+
end
|
774
|
+
|
775
|
+
def _write_metadata
|
776
|
+
File.write("teaser.txt", @meta.teaser)
|
777
|
+
fields = [:num, :title, :date, :pubdate, :views, :tags, :pinned]
|
778
|
+
fname2 = "metadata.txt"
|
779
|
+
f2 = File.open(fname2, "w") do |f2|
|
780
|
+
fields.each {|fld| f2.puts "#{fld}: #{@meta.send(fld)}" }
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
def _post_lookup(postid) # side-effect
|
785
|
+
# .. = templates, ../.. = views/thisview
|
786
|
+
slug = title = date = teaser_text = nil
|
787
|
+
|
788
|
+
dir_posts = @vdir/:posts
|
789
|
+
posts = Dir.entries(dir_posts).grep(/^\d\d\d\d/).map {|x| dir_posts/x }
|
790
|
+
posts.select! {|x| File.directory?(x) }
|
791
|
+
|
792
|
+
post = posts.select {|x| File.basename(x).to_i == postid }
|
793
|
+
raise "Error: More than one post #{postid}" if post.size > 1
|
794
|
+
postdir = post.first
|
795
|
+
vp = RuneBlog::ViewPost.new(@blog.view, postdir)
|
796
|
+
vp
|
797
|
+
end
|
798
|
+
|
799
|
+
def _card_generic(card_title:, middle:, extra: "")
|
800
|
+
front = <<-HTML
|
801
|
+
<div class="card #{extra} mb-3">
|
802
|
+
<div class="card-body">
|
803
|
+
<h5 class="card-title">#{card_title}</h5>
|
804
|
+
HTML
|
805
|
+
|
806
|
+
tail = <<-HTML
|
807
|
+
</div>
|
808
|
+
</div>
|
809
|
+
HTML
|
810
|
+
text = front + middle + tail
|
811
|
+
_out text + "\n "
|
812
|
+
end
|
813
|
+
|
814
|
+
def _var(name) # FIXME scope issue!
|
815
|
+
::Livetext::Vars[name] || "[:#{name} is undefined]"
|
816
|
+
end
|
817
|
+
|
818
|
+
def _main(url)
|
819
|
+
%[href="javascript: void(0)" onclick="javascript:open_main('#{url}')"]
|
820
|
+
end
|
821
|
+
|
822
|
+
def _blank(url)
|
823
|
+
%[href='#{url}' target='blank']
|
824
|
+
end
|
825
|
+
|