diff options
Diffstat (limited to 'html.lua')
-rw-r--r-- | html.lua | 99 |
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 = { + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["&"] = "&", +} +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 |