function format_date(date) if date == nil then return date end date = pandoc.utils.normalize_date(pandoc.utils.stringify(date)) local year, month, day = date:match("(%d%d%d%d)-(%d%d)-(%d%d)") if year == nil then return nil end local time = os.time({ year = tonumber(year), month = tonumber(month), day = tonumber(day) }) return pandoc.MetaMap({ yyyy_mm_dd = pandoc.MetaString(os.date("%F", time)), yyyy = pandoc.MetaString(os.date("%Y", time)), mm = pandoc.MetaString(os.date("%m", time)), dd = pandoc.MetaString(os.date("%d", time)), rfc3339 = pandoc.MetaString(os.date("%FT%T+00:00", time)) }) end function table_to_list(t, cmp) local l = pandoc.List() for key, value in pairs(t) do l:insert(pandoc.MetaMap({key = key, value = value})) end l:sort(cmp or function(i1, i2) return i1.key < i2.key end) return l end function group_by(l, field, insert) insert = insert or function(group, _, item) if not group then group = l:new({item}) return group end group:insert(item) end local groups = {} for i = 1, #l do local item = l[i] local f = field(item) if f ~= nil then local out = insert(groups[f], f, item) if out ~= nil then groups[f] = out end end end return groups end function splitstr(input, sep) sep = sep or "%s" local t = {} for str in input:gmatch("([^" .. sep .. "]+)") do table.insert(t, str) end return t end function relative_to(dir, target) dir = splitstr(dir, "/") target = splitstr(target, "/") local prefix = true local path = "" for i = 1, math.min(#dir, #target) do local t = target[i] if prefix then if dir[i] ~= t then prefix = false path = "../" .. t end else path = "../" .. path .. "/" .. t end end if #dir < #target then for i = #dir + 1, #target do path = path .. (path == "" and "" or "/") .. target[i] end elseif #dir > #target then for _ = #target + 1, #dir do path = "../" .. path end end return path end function apply_path_rewrites(rewrites, str) for i = 1, #rewrites.path do local r = rewrites.path[i] str = str:gsub(pandoc.utils.stringify(r.from), pandoc.utils.stringify(r.to)) end return str end function get_file_out(rewrites, content_dir, file_in) local file_out = file_in:gsub("^" .. content_dir, ""):gsub("%.md$", ".html") if file_out:match(".html$") and not file_out:match("/index%.html$") then file_out = file_out:gsub("/(.*)%.html$", "/%1/index.html") end file_out = apply_path_rewrites(rewrites, file_out) return pandoc.MetaString(file_out) end function make_absolute(rel, base) return rel:find("^/") ~= nil and rel or base:gsub("^(.*)/.-$", "%1") .. "/" .. rel end function resolve_url(site_url, ref_file, target_file) target_file = target_file:gsub("/index%.html$", "/") local ref_base_dir = ref_file:gsub("^(.*)/.-$", "%1") local abs = target_file local rel = relative_to(ref_base_dir, abs):gsub("/index%.html$", "/") return pandoc.MetaMap({ abs = pandoc.MetaString(abs), rel = pandoc.MetaString(rel), full = pandoc.MetaString(site_url .. abs) }) end function resolve_layout(layout) if layout then layout = pandoc.utils.stringify(layout) return pandoc.MetaMap({ id = pandoc.MetaString(layout), ["is_" .. layout] = pandoc.MetaBool(true) }) end end function resolve_section(content_dir, file_in) local section = file_in:gsub("^" .. content_dir, ""):match("^/(.-)[/.]") or "index" return pandoc.MetaMap({ id = pandoc.MetaString(section), ["is_" .. section] = pandoc.MetaBool(true) }) end function resolve_category(categories, category) if categories and category then category = pandoc.utils.stringify(category) data = pandoc.MetaMap(categories[category]) data.id = pandoc.MetaString(category) return data end end function prep_main_menu(rewrites, section, main_menu) local active_item = nil for i = 1, #main_menu do local item = main_menu[i] local active = pandoc.utils.stringify(item.id) == section.id item.active = pandoc.MetaBool(active) item.url = apply_path_rewrites(rewrites, pandoc.utils.stringify(item.url)) if active then active_item = item end end return pandoc.MetaMap({ items = main_menu:filter(function(item) return not item.hidden or item.active end), active = active_item }) end function organize_subpages(site_url, ref_file, pages) for i = 1, #pages do local page = pages[i] page.url = resolve_url(site_url, ref_file, pandoc.utils.stringify(page.file_out)) end local pages_grouped_date = group_by(pages, function(p) return not p.date end) local pages_undated = pages_grouped_date[true] or pandoc.MetaList({}) pages_undated:sort(function(p1, p2) return pandoc.utils.stringify(p1.title) < pandoc.utils.stringify(p2.title) end) local pages_dated = pages_grouped_date[false] or pandoc.MetaList({}) pages_dated:sort(function(p1, p2) return pandoc.utils.stringify(p1.date.yyyy_mm_dd) > pandoc.utils.stringify(p2.date.yyyy_mm_dd) end) local pages_categorized = pages:filter( function(p) return p.category ~= nil end) pages_categorized:sort(function(p1, p2) return pandoc.utils.stringify(p1.title) < pandoc.utils.stringify(p2.title) end) local pages_by_year = group_by(pages_dated, function(p) return pandoc.utils.stringify(p.date.yyyy) end) pages_by_year = pandoc.MetaList(table_to_list(pages_by_year, function(i1, i2) return i1.key > i2.key end)) local pages_by_category = group_by(pages_categorized, function(p) return pandoc.utils.stringify(p.category.id) end) pages_by_category = pandoc.MetaList(table_to_list(pages_by_category, function(i1, i2) return i1.key < i2.key end)) local pages_data = pandoc.MetaMap({ all_dated = pages_dated, all_undated = pages_undated, by_year = pages_by_year, by_category = pages_by_category, last_update = #pages_dated ~= 0 and pages_dated[1].last_update }) local categories_data = group_by(pages_categorized, function(p) return p.category and pandoc.utils.stringify(p.category.id) end, function(stats, _, p) if not stats then return {name = pandoc.MetaString(p.category.name), count = 1} else stats.count = stats.count + 1 end end) categories_data = pandoc.MetaList(table_to_list(categories_data)) for i = 1, #categories_data do categories_data[i].value.count = pandoc.MetaString(("%d"):format(categories_data[i].value.count)) end return pages_data, categories_data end function Meta(meta) meta.content_dir = meta.content_dir:gsub("/$", "") meta.site.url = pandoc.utils.stringify(meta.site.url):gsub("/$", "") meta.rewrites = meta.rewrites or pandoc.MetaMap({path = pandoc.MetaList({})}) meta.page_type = meta.page_type or "page" meta.layout = resolve_layout(meta.layout) meta.section = resolve_section(meta.content_dir, meta.file_in) meta.file_out = get_file_out(meta.rewrites, meta.content_dir, meta.file_in) if meta.relative_to == nil then meta.relative_to = meta.file_in end meta.relative_to_out = get_file_out(meta.rewrites, meta.content_dir, meta.relative_to) meta.url = resolve_url(meta.site.url, meta.relative_to_out, meta.file_out) if meta.preview ~= nil then meta.preview = resolve_url(meta.site.url, meta.relative_to_out, make_absolute( pandoc.utils.stringify(meta.preview), meta.file_out)) end meta.date = format_date(meta.date) if meta.last_update ~= nil then meta.last_update = format_date(meta.last_update) else meta.last_update = meta.date end meta.category = resolve_category(meta.categories[meta.section.id], meta.category) meta.categories = nil if meta.page_type == "feed" then meta.page = pandoc.MetaMap({ url = resolve_url(meta.site.url, meta.relative_to_out, meta.file_out:gsub("%.xml$", ".html")) }) end if meta.create_feed then meta.feed = pandoc.MetaMap({ url = resolve_url(meta.site.url, meta.relative_to_out, meta.file_out:gsub("%.html$", ".xml")) }) end if meta.menus and meta.menus.main then meta.menus.main = prep_main_menu(meta.rewrites, meta.section, meta.menus.main) end if meta.pages then local pages, categories = organize_subpages(meta.site.url, meta.relative_to_out, meta.pages) meta.pages = pages meta.categories = categories end return meta end