summaryrefslogtreecommitdiff
path: root/html.lua
diff options
context:
space:
mode:
Diffstat (limited to 'html.lua')
-rw-r--r--html.lua99
1 files changed, 99 insertions, 0 deletions
diff --git a/html.lua b/html.lua
new file mode 100644
index 0000000..3b34cc3
--- /dev/null
+++ b/html.lua
@@ -0,0 +1,99 @@
+local M = {}
+
+local void = {
+ area = true, base = true, br = true, col = true, embed = true, hr = true,
+ img = true, input = true, link = true, meta = true, param = true,
+ source = true, track = true, wbr = true,
+}
+
+local char_refs = {
+ ["<"] = "&lt;",
+ [">"] = "&gt;",
+ ['"'] = "&quot;",
+ ["&"] = "&amp;",
+}
+local function escape(x)
+ return (tostring(x):gsub('.', char_refs))
+end
+
+local function ele(_, name)
+ assert(name:match '^%w+$')
+ return function(...)
+ local params, inner
+ if select('#', ...) == 2 or type((...)) == 'table' then
+ params, inner = ...
+ else inner = ... end
+
+ coroutine.yield("<", name)
+ if params then
+ coroutine.yield(" ")
+ for k, v in pairs(params) do
+ assert(not k:find '[%s"\'>/=]')
+ coroutine.yield(k, "=\"", escape(v), "\" ")
+ end
+ end
+ coroutine.yield(">")
+
+ if not void[name] then
+ if type(inner) == 'function' then
+ inner()
+ elseif inner then
+ coroutine.yield(escape(inner))
+ end
+ coroutine.yield("</", name, ">")
+ else
+ assert(not inner)
+ end
+ end
+end
+
+setmetatable(M, {__index = ele})
+
+local function document(fn)
+ return function()
+ coroutine.yield("<!doctype html>")
+ M.html(fn)
+ coroutine.yield("\n")
+ end
+end
+
+function M.render(fn, ...)
+ local output = {}
+
+ local co = coroutine.create(fn, ...)
+ local retvals
+ while true do
+ retvals = {assert(coroutine.resume(co))}
+ if coroutine.status(co) == 'dead' then break end
+ if retvals[2] == abort then
+ coroutine.yield(unpack(retvals))
+ end
+ for i = 2, #retvals do
+ table.insert(output, retvals[i])
+ end
+ end
+
+ return table.concat(output), table.unpack(retvals, 2)
+end
+
+function M.render_doc(fn, ...)
+ return M.render(document(fn), ...)
+end
+
+function M.text(str)
+ coroutine.yield(escape(str))
+end
+
+function M.url_encode(str)
+ return tostring(str):gsub('([^A-Za-z0-9%_%.%-%~])', function(v)
+ return ('%%%02x'):format(v:byte()):upper()
+ end)
+end
+
+function M.url_decode(str)
+ return tostring(str):gsub('%%(%x%x)', function(c)
+ return string.char(tonumber(c, 16))
+ end)
+end
+
+return M