function format_date(date) if not date 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 not year then return nil end local time = os.time({ year = tonumber(year), month = tonumber(month), day = tonumber(day) }) return { yyyy_mm_dd = os.date("%F", time), yyyy = os.date("%Y", time), mm = os.date("%m", time), dd = os.date("%d", time), rfc3339 = os.date("%FT%T+00:00", time), long = os.date("%B %d, %Y", time), short = os.date("%b %d, %y", time), } end function table_to_list(t, kv, cmp) local l = pandoc.List() if kv then for key, value in pairs(t) do l:insert({ key = key, value = value }) end else for _, value in pairs(t) do l:insert(value) end 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 then local out = insert(groups[f], f, item) if out 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 make_absolute(rel, base) return rel:find("^/") 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 { abs = abs, rel = rel, full = (site_url .. abs) } end function resolve_layout(layout) if layout then layout = pandoc.utils.stringify(layout) return { id = layout, ["is_" .. layout] = true } end end function resolve_namespace(namespace) namespace = pandoc.utils.stringify(namespace) local root = "index" if namespace ~= "" then root = namespace:gsub("^/([^/]*).*$", "%1") end return { root = { id = root, ["is_" .. root] = true }, full = namespace } end function prep_menu(active_id, main_menu) local active_item = nil for i = 1, #main_menu do local item = main_menu[i] local active = pandoc.utils.stringify(item.id) == active_id item.active = active if active then active_item = item end end return { items = main_menu:filter(function(item) return not item.hidden or item.active end), active = active_item, } end function process_pages(global, pages_by_id) if not pages_by_id then return nil end local pages_list = pandoc.List() for _, page in pairs(pages_by_id) do pages_list:insert(process(global, page)) end pages_list:sort(function(p1, p2) if p1.position then if p2.position then return tonumber(p1.position) < tonumber(p2.position) else return true end elseif p2.position then return false elseif p1.date then if p2.date then return p1.date.yyyy_mm_dd > p2.date.yyyy_mm_dd else return true end elseif p2.date then return false else return p1.title < p2.title end end) local pages_by_category = pages_list:filter(function(p) return p.category ~= nil end) pages_by_category = group_by(pages_by_category, function(p) return p.category.id end, function(data, _, p) if not data then local l = pandoc.List() l:insert(p) return { name = pandoc.utils.stringify(p.category.name), pages = l } else data.pages:insert(p) end end) pages_by_category = table_to_list(pages_by_category, false, function(i1, i2) return i1.name < i2.name end) local pages_data = { all = pages_list, by_id = pages_by_id, by_category = pages_by_category } return pages_data end function pages_last_update(all_pages) local last_update = format_date("1990-01-01") for i = 1, #all_pages do local page = all_pages[i] if page.last_update and page.last_update.yyyy_mm_dd > last_update.yyyy_mm_dd then last_update = page.last_update end end return last_update end local titlecase_exceptions = pandoc.List({ "a", "an", "and", "as", "at", "but", "by", "en", "for", "if", "in", "nor", "of", "on", "or", "per", "the", "to", "v%.?", "vs%.?", "via", }) function titlecase(str) return str:gsub("(%S)(%S*)(%s*)", function(a, b, space) local word = a .. b local is_exception = word:match("%u") or titlecase_exceptions:find_if( function(exception) return word:match("^" .. exception .. "$") end) if is_exception then return word .. space end return string.upper(a) .. b .. space end) end function process(global, meta) meta.namespace = resolve_namespace(meta.namespace) meta.file_out = pandoc.utils.stringify(meta.file_out):gsub("^out", "") meta.layout = resolve_layout(meta.layout) meta.url = resolve_url(global.site.url, global.file_out, meta.file_out) meta.title = (meta.title and titlecase(pandoc.utils.stringify(meta.title))) or "" if meta.position then meta.position = pandoc.utils.stringify(meta.position) end if meta.create_feed then if meta.file_out:match(".html$") then meta.feed = { url = resolve_url(global.site.url, global.file_out, meta.file_out:gsub(".html$", ".xml")), } else meta.page = { url = resolve_url(global.site.url, global.file_out, meta.file_out:gsub(".xml$", ".html")), } end end if meta.preview then meta.preview = make_absolute(pandoc.utils.stringify(meta.preview), meta.file_out) meta.preview = resolve_url(global.site.url, global.file_out, meta.preview) end if meta.date then meta.date = format_date(meta.date) end if meta.menus and meta.menus.main then meta.menus.main = prep_menu(meta.namespace.root.id, meta.menus.main) end meta.pages = process_pages(global, meta.pages) if meta.last_update then meta.last_update = format_date(meta.last_update) elseif meta.date then meta.last_update = meta.date elseif meta.pages then meta.last_update = pages_last_update(meta.pages.all) end return meta end function Meta(meta) meta.site.url = pandoc.utils.stringify(meta.site.url):gsub("/$", "") return process(meta, meta) end