aboutsummaryrefslogtreecommitdiff
path: root/html.lua
blob: 454eae8f36a7dd2f8787057cf8bec8fd34a4dc2e (plain)
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 = {
	["<"] = "&lt;",
	[">"] = "&gt;",
	['"'] = "&quot;"
}
local function escape(x)
	local escaped = tostring(x)
	escaped = escaped:gsub("&", "&amp;")

	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