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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
-- HTML generator DSL for lua 5.3
-- copyright © 2021 citrons <citrons@mondecitronne.com>
-- arbitrary copying, distribution, modification, and use is permitted as long
-- as this copyright notice is retained, and you get in the hole immediately.
-- go on, get in.
local h = {}
local esc_sequences = {
["<"] = "<",
[">"] = ">",
['"'] = """
}
local function escape(x)
local escaped = tostring(x)
escaped = escaped:gsub("&", "&")
for char,esc in pairs(esc_sequences) do
escaped = string.gsub(escaped, char, esc)
end
return escaped
end
local function render(x)
local mt = getmetatable(x)
if mt.render then return mt.render(x)
else return escape(x) end
end
local function verify_tag(x)
end
local meta = {}
function meta.render(self)
local attr_strings = {}
local i = 1
for k,v in pairs(self.attrs) do
verify_tag(k)
attr_strings[i] = string.format(' %s="%s"', k, escape(v))
i = i + 1
end
local attrs = table.concat(attr_strings)
local str
if self.children then
local child_strings = {}
for i,c in ipairs(self.children) do
child_strings[i] = render(c)
end
local children = table.concat(child_strings)
str = string.format('<%s%s>%s</%s>',
self.tag, attrs, children, self.tag)
else
str = string.format('<%s%s />', self.tag, attrs)
end
if self.tag == "html" then
str = "<!DOCTYPE html>\n" .. str
end
return str
end
meta.__tostring = meta.render
setmetatable(h, {
__index = function(self, tag)
verify_tag(tag)
return function(...)
local attrs, children
local args = {...}
if #args == 2 then
attrs = args[1]
if type(args[2]) ~= "table" then
children = {args[2]}
else
children = args[2]
end
elseif #args == 1 then
if type(args[1]) ~= "table" then
attrs = {} children = {args[1]}
elseif args[1][1] ~= nil then
attrs = {} children = args[1]
else
children = {}
attrs = {}
-- if args[1] is key/value pairs, use it as attributes
for _,_ in pairs(args[1]) do
children = nil attrs = args[1] break
end
end
else attrs = {} end
if children then
for i,v in ipairs(children) do
local mt = getmetatable(v)
if mt.tohtml then
children[i] = mt.tohtml(v)
end
end
end
local element = {tag=tag, attrs=attrs, children=children}
setmetatable(element, meta)
return element
end
end
})
local raw_meta = {
render = function(self) return self.str end,
__tostring = function(self) return self.str end
}
function h.raw(str)
local raw = {str=str}
setmetatable(raw, raw_meta)
return raw
end
return h
|