summaryrefslogtreecommitdiffstats
path: root/scripts/metadata.lua
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/metadata.lua')
-rw-r--r--scripts/metadata.lua342
1 files changed, 342 insertions, 0 deletions
diff --git a/scripts/metadata.lua b/scripts/metadata.lua
new file mode 100644
index 0000000..8f32bd4
--- /dev/null
+++ b/scripts/metadata.lua
@@ -0,0 +1,342 @@
1local path = require 'pandoc.path'
2
3function pandoc.List:flatten()
4 local result = pandoc.List()
5
6 for i = 1, #self do result:extend(self[i]) end
7
8 return result
9end
10
11function pandoc.List:flatMap(fn)
12 local mapped = self:map(fn)
13 local result = pandoc.List()
14
15 for i = 1, #mapped do result:extend(mapped[i]) end
16
17 return result
18end
19
20function pandoc.List:take(n)
21 if n >= #self then return self end
22
23 local result = pandoc.List()
24
25 for i = 1, n do result:insert(self[i]) end
26
27 return result
28end
29
30function dump(o)
31 if type(o) == 'table' then
32 local s = '{ '
33 for k, v in pairs(o) do
34 if type(k) ~= 'number' then k = '"' .. k .. '"' end
35 s = s .. '[' .. k .. '] = ' .. dump(v) .. ','
36 end
37 return s .. '} '
38 else
39 return tostring(o)
40 end
41end
42
43function page_sort(order)
44 return function(p1, p2)
45 if p1.position then
46 if p2.position then
47 return tonumber(p1.position) < tonumber(p2.position)
48 else
49 return true
50 end
51 elseif p2.position then
52 return false
53 elseif order == "date_desc" then
54 if p1.last_update then
55 if p2.last_update then
56 return p1.last_update.yyyy_mm_dd > p2.last_update.yyyy_mm_dd
57 else
58 return true
59 end
60 else
61 return false
62 end
63 else
64 return p1.title:upper() < p2.title:upper()
65 end
66 end
67end
68
69function slug(str)
70 return str:lower():gsub("[^ a-z]", ""):gsub("[ ]+", "-")
71end
72
73function format_date(date)
74 if not date then return date end
75
76 date = pandoc.utils.normalize_date(pandoc.utils.stringify(date))
77 local year, month, day = date:match("(%d%d%d%d)-(%d%d)-(%d%d)")
78 if not year then return nil end
79
80 local time = os.time({ year = tonumber(year), month = tonumber(month), day = tonumber(day) })
81 return {
82 yyyy_mm_dd = os.date("%F", time),
83 yyyy = os.date("%Y", time),
84 mm = os.date("%m", time),
85 dd = os.date("%d", time),
86 rfc3339 = os.date("%FT%T+00:00", time),
87 long = os.date("%B %d, %Y", time),
88 short = os.date("%b %d, %Y", time),
89 }
90end
91
92function make_absolute(rel, base)
93 return path.is_absolute(rel) and rel or path.join({ path.directory(base), rel })
94end
95
96function resolve_url(site_url, ref_file, target_file)
97 target_file = target_file:gsub("/index%.html$", "/")
98
99 local ref_base_dir = path.directory(ref_file)
100 local abs = target_file
101 local rel = path.make_relative(abs, ref_base_dir, true)
102 local full = (abs:sub(1, 1) == "/" and (site_url .. abs)) or abs
103
104 return { abs = abs, rel = rel, full = full }
105end
106
107function resolve_layout(depth)
108 local layout = "categorized_list"
109
110 if depth == 0 then
111 layout = "page"
112 elseif depth == 1 then
113 layout = "list"
114 end
115
116 return layout
117end
118
119function prep_layout(layout)
120 layout = pandoc.utils.stringify(layout)
121 return { id = layout, ["is_" .. layout] = true }
122end
123
124function resolve_namespace(namespace)
125 namespace = pandoc.utils.stringify(namespace)
126
127 local root = "index"
128 if namespace ~= "" then root = namespace:gsub("^/([^/]*).*$", "%1") end
129
130 return { root = { id = root, ["is_" .. root] = true }, full = namespace }
131end
132
133function prep_menu(active_id, main_menu)
134 local active_item = nil
135 local items = pandoc.List()
136
137 for i = 1, #main_menu do
138 local item = main_menu[i]
139 local active = pandoc.utils.stringify(item.id) == active_id
140 item.active = active
141 if active then active_item = item end
142 if not item.hidden or item.active then items:insert(item) end
143 end
144
145 return { items = items, active = active_item }
146end
147
148function process_pages(global, order, pages_by_id)
149 if not pages_by_id then return nil end
150
151 local pages_all = pandoc.List()
152 local pages_date_desc = pandoc.List()
153
154 for _, page in pairs(pages_by_id) do
155 local p = process(global, page)
156 if not p.unlisted then
157 pages_all:insert(p)
158 if p.last_update then pages_date_desc:insert(p) end
159 end
160 end
161
162 pages_all:sort(page_sort(order))
163 pages_date_desc:sort(page_sort("date_desc"))
164
165 local pages_data = { all = pages_all, date_desc = pages_date_desc, by_id = pages_by_id }
166
167 return pages_data
168end
169
170function find_depth(pages)
171 local depth = 0
172
173 if pages then
174 for i = 1, #pages.all do
175 local p = pages.all[i]
176 local d = tonumber(p.depth)
177 if d > depth then depth = d end
178 end
179
180 depth = depth + 1
181 end
182
183 return depth
184end
185
186function d1_page_to_list_item(meta, p)
187 return {
188 title = p.title,
189 subtitle = p.subtitle,
190 date = p.date,
191 last_update = p.last_update,
192 schema_type = p.schema_type,
193 position = p.position,
194 url = p.url,
195 rel = p.rel,
196 slug = p.slug,
197 thumbnail = p.thumbnail,
198 icon = p.icon or meta.icon,
199 post_icon = p.post_icon or meta.list_post_icon,
200 indicator = meta.list_read_indicators,
201 }
202end
203
204function d2_page_to_list_item(meta, cat, p, set_cat_title)
205 return {
206 title = p.title,
207 subtitle = p.subtitle,
208 category = set_cat_title and cat.title,
209 date = p.date,
210 last_update = p.last_update,
211 schema_type = p.schema_type,
212 position = p.position,
213 url = p.url,
214 rel = p.rel,
215 slug = p.slug,
216 thumbnail = p.thumbnail,
217 icon = p.icon or cat.icon,
218 post_icon = p.post_icon or cat.list_post_icon or meta.list_post_icon,
219 indicator = cat.list_read_indicators,
220 }
221end
222
223function cat_to_list_cat(cat, allItems)
224 local limit = cat.list_limit or 9999
225 local items = allItems:take(limit)
226 local omitted = #allItems - #items
227
228 return {
229 title = cat.title,
230 description = (cat.description and pandoc.MetaBlocks(pandoc.Para(cat.description))) or
231 (not cat.no_description and cat.content),
232 last_update = cat.last_update,
233 schema_type = cat.schema_type,
234 url = cat.url,
235 slug = cat.slug,
236 layout = cat.list_layout,
237 items = items,
238 total = tostring(#allItems),
239 omitted = omitted ~= 0 and tostring(omitted),
240 }
241end
242
243function generate_list(meta)
244 if meta.depth < 1 then return nil end
245
246 if meta.depth == 1 then
247 return meta.pages.all:map(function(p) return d1_page_to_list_item(meta, p) end)
248 end
249
250 if meta.depth == 2 then
251 return meta.pages.all
252 :map(function(cat)
253 local allItems = ((cat.pages and cat.pages.all) or pandoc.List())
254 :map(function(p) return d2_page_to_list_item(meta, cat, p, false) end)
255
256 return cat_to_list_cat(cat, allItems)
257 end)
258 :filter(function(cat) return #cat.items ~= 0 end)
259 end
260
261 if meta.depth == 3 then
262 return meta.pages.all
263 :map(function(cat)
264 local allItems = (cat.pages and cat.pages.all or pandoc.List()):flatMap(function(c)
265 if not c.pages or not cat.list_flatten then
266 return pandoc.List({ d1_page_to_list_item(cat, c) })
267 else
268 return c.pages.all:map(function(p) return d2_page_to_list_item(cat, c, p, true) end)
269 end
270 end)
271 allItems:sort(page_sort(cat.list_order))
272
273 return cat_to_list_cat(cat, allItems)
274 end)
275 :filter(function(cat) return #cat.items ~= 0 end)
276 end
277end
278
279function process(global, meta)
280 meta.namespace = resolve_namespace(meta.namespace)
281 meta.file_out = pandoc.utils.stringify(meta.file_out):gsub("^out", "")
282 meta.redirect = meta.url and true
283 meta.url = meta.url and pandoc.utils.stringify(meta.url)
284 meta.url = resolve_url(global.site.url, global.file_out, meta.url or meta.file_out)
285 meta.title = (meta.title and pandoc.utils.stringify(meta.title)) or ""
286 meta.slug = slug(meta.title)
287 meta.schema_type = (meta.schema_type and pandoc.utils.stringify(meta.schema_type)) or "CreativeWork"
288 if meta.list_order then meta.list_order = pandoc.utils.stringify(meta.list_order) end
289 meta.list_layout = meta.list_layout and prep_layout(meta.list_layout)
290 if meta.list_limit then meta.list_limit = tonumber(pandoc.utils.stringify(meta.list_limit)) end
291 if meta.position then meta.position = pandoc.utils.stringify(meta.position) end
292
293 if meta.feed then
294 if meta.file_out:match(".html$") then
295 meta.feed = {
296 url = resolve_url(global.site.url, global.file_out, meta.file_out:gsub(".html$", ".xml")),
297 }
298 else
299 meta.page = {
300 url = resolve_url(global.site.url, global.file_out, meta.file_out:gsub(".xml$", ".html")),
301 }
302 end
303 end
304
305 if meta.thumbnail then
306 meta.thumbnail = make_absolute("thumbnail." .. pandoc.utils.stringify(meta.thumbnail),
307 meta.file_out)
308 meta.thumbnail = resolve_url(global.site.url, global.file_out, meta.thumbnail)
309 end
310
311 if meta.menus and meta.menus.main then
312 meta.menus.main = prep_menu(meta.namespace.root.id, meta.menus.main)
313 end
314
315 meta.pages = process_pages(global, meta.list_order, meta.pages)
316 meta.depth = find_depth(meta.pages)
317 meta.layout = prep_layout(meta.layout or (meta.redirect and "redirect") or resolve_layout(meta.depth))
318
319 if meta.date then
320 meta.date = format_date(meta.date)
321 elseif meta.pages and #meta.pages.date_desc ~= 0 then
322 meta.date = meta.pages.date_desc[1].date
323 end
324
325 if meta.last_update then
326 meta.last_update = format_date(meta.last_update)
327 elseif meta.pages and #meta.pages.date_desc ~= 0 then
328 meta.last_update = meta.pages.date_desc[1].last_update
329 elseif meta.date then
330 meta.last_update = meta.date
331 end
332
333 meta.list = generate_list(meta)
334
335 return meta
336end
337
338function Meta(meta)
339 meta.site.url = pandoc.utils.stringify(meta.site.url):gsub("/$", "")
340
341 return process(meta, meta)
342end