runeblog 0.3.02
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 +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
|
+
|