1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
local function fmt_attrs(attrs)
local function fmt_attr(k,v) k = k:gsub("_","-")
if v == true then return k
else if type(v) == "table" then v = table.concat(v," ") end
return ('%s="%s"'):format(k,tostring(v):gsub('"','"')) end end
local o = ""
for k,v in pairs(attrs) do o=o.." "..fmt_attr(k,v) end
return o end
local html
local function fmt_tag(tag)
local attrs = tag.attrs and fmt_attrs(tag.attrs) or ""
local selfclosing = (tag.body == "")
if selfclosing then return ("<%s%s/>"):format(tag.tname,attrs)
else return ("<%s%s>%s</%s>"):format(tag.tname,attrs,html(tag.body),tag.tname) end end
-- tag(tname,body) or tag(tname,attrs,body)
local function tag(tname, a1, a2)
local body, attrs if a2 then attrs=a1 body=a2 else attrs=nil body=a1 end
return setmetatable({tag=true,tname=tname,attrs=attrs,body=body},{__tostring=fmt_tag}) end
-- T.tname(body) or T.tname(attrs, body)
local T = setmetatable({}, {__index=function(_,tname)
return function (...) return tag(tname, ...) end end})
-- marks s as safe, doesn't escape it
local function safe(s) assert(type(s) == "string","can only mark string as safe")
return setmetatable({safe=true,s=s},{__tostring=function(x)return x.s end}) end
local function escape(s)
s=s:gsub("&","&")
s=s:gsub("<","<")
s=s:gsub(">",">")
s=s:gsub('"',""")
return safe(s) end
html = function (x)
if type(x) == "string" then return escape(x)
elseif type(x) == 'number' then return html(tostring(x))
elseif type(x) == "table" then
if x.safe then return x -- safestr. already escaped
elseif x.tag then return safe( tostring(x) ) -- it's a tag
else -- just a regular list
local o = ""
for _,item in ipairs(x) do o=o..tostring(html(item)) end
return safe(o) end end end
return {
html = html,
tag = tag,
safe = safe,
T = T,
}
|