local h = require 'html' local M = {} local site_sections = { default = "account", } local function navbar(links) h.nav({class = "site-nav"}, function() for _, link in ipairs(links) do h.a({ href = link[2], class = link[2] == links.selected and "selected" or nil }, link[1]) h.text " " end end) end function M.page(param) return h.render_doc(function() h.meta {charset = "utf-8"} h.title(param.title and param.title .. " — auth.citrons.xyz" or "auth.citrons.xyz") h.meta { name = "viewport", content = "width=device-width, initial-scale=0.75" } h.link {rel = "stylesheet", href = "/static/citrine.css"} h.div({id = "page-grid"}, function() h.header({class = "site-header"}, function() param.section = param.section or "default" h.img({ src = "/static/headers/"..param.section..".png", alt = site_sections[param.section] }) end) h.div({class = "content"}, function() h.main(param[1]) h.footer({class = "site-footer"}, function() h.p(function() h.a({href = "https://citrons.xyz/git/auth.git/" }, "© 2023 citrons") end) end) end) h.div({class = "header-icons"}, function() h.a({class = "help", href = "/account"}, function() h.img { src = "/static/gear.png", alt = "account settings", class = "help" } end) h.a({class = "help", href = "/"}, function() h.img { src = "/static/qmark.png", alt = "about", class = "help" } end) end) end) end) end local function class_article(e) return function(...) h[e]({class = "article"}, ...) end end M.h1 = class_article "h1" M.h2 = class_article "h2" M.h3 = class_article "h3" M.h4 = class_article "h4" M.h5 = class_article "h5" M.h6 = class_article "h6" function M.quote(quote, attrib) h.figure({class = attrib and "quote attrib" or "quote"}, function() h.blockquote(function() h.p({}, quote) end) if attrib then h.figcaption({}, attrib) end end) end function M.dyk(content) h.aside({class = "dyk"}, function() h.h2 "did you know?" if type(content) == "string" then html.p(content) else content() end end) end function M.code_block(content) h.pre({class = "code-block"}, content) end function M.apiobutton() h.a({href = "https://citrons.xyz/a/memetic-apioform-page", class="apiopage"}, function() h.img {src = "https://citrons.xyz/a/static/apiopage.png", alt = "memetic apiopage: click to view"} end) end function M.redact(s) h.del({class = "redaction", title = "redacted text"}, function() h.span({}, s:gsub(utf8.charpattern, function(c) if c ~= " " then return "X" end end)) end) end function M.errmsg(e) h.div({class = "errmsg"}, function() h.p({}, e) end) end local statuses = { [400] = "400 Bad Request", [401] = "401 Unauthorized", [403] = "403 Forbidden", [404] = "404 Not Found", [405] = "405 Method Not Allowed", [410] = "410 Gone", [411] = "411 Length Required", [413] = "413 Payload Too Large", [429] = "429 Too Many Requests", [500] = "500 Internal Server Error", [501] = "501 Not Implemented", [502] = "502 Bad Gateway", [503] = "503 Service Unavailable", [504] = "504 Gateway Timeout" } local messages = { [400] = "your software has sent an invalid request to the server.", [401] = "permission denied.", [403] = "permission denied.", [404] = "the page you attempted to access was not found.", [405] = "your software has sent a request with an invalid method.", [410] = "this content is no longer available.", [411] = "your software failed to provide a length for the request body.", [413] = "your software sent too much data to the server.", [429] = "you are doing this too often. try again later.", [500] = "a temporary internal error has occurred. this is my fault.", [501] = "the requested feature is not implemented.", } function M.error(code, message) return { content_type = 'text/html', status = statuses[code] or code }, M.page { section = "error", function() M.h1((statuses[code] or ("error "..code)):lower()) h.p(message or messages[code] or messages[code // 100 * 100] or "an error has occurred") end } end return M