-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhtml.lua
More file actions
169 lines (148 loc) · 4.03 KB
/
html.lua
File metadata and controls
169 lines (148 loc) · 4.03 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
local p = {}
--- Escapes a string for use in HTML attributes/text.
local function escapeHtml(str)
str = tostring(str or "")
return str
:gsub("&", "&")
:gsub("<", "<")
:gsub(">", ">")
:gsub('"', """)
end
--- Converts a style table to a CSS string.
local function cssToString(css)
local parts = {}
for _, k in ipairs(css._keys) do
local v = css[k]
table.insert(parts, k .. ":" .. v)
end
return table.concat(parts, ";")
end
--- For .attrs etc
local function orderedTableInit(t)
t._keys = {}
return t
end
local function orderedTableAdd(t, key, value)
assert(type(key) == "string" and key ~= "", "Key must be a non-empty string.")
assert(key ~= "_keys", "Key must not be named: _keys.")
if t[key] == nil then
table.insert(t._keys, key)
end
t[key] = value
end
--- HTML element builder object.
local HtmlElement = {}
HtmlElement.__index = HtmlElement
--- Adds an attribute to the element.
function HtmlElement:attr(name, value)
-- nil = NOP
if value == nil then
return self
end
-- self.attrs[name] = tostring(value)
orderedTableAdd(self.attrs, name, tostring(value))
return self
end
--- Adds a CSS property.
function HtmlElement:css(name, value)
-- nil = NOP
if value == nil then
return self
end
-- self.styles[name] = tostring(value)
orderedTableAdd(self.styles, name, tostring(value))
return self
end
--- Appends raw wikitext content (i.e., innerHTML).
function HtmlElement:wikitext(...)
for i = 1, select("#", ...) do
local content = select(i, ...)
if content ~= nil then
table.insert(self.children, { type = "wikitext", value = tostring(content) })
end
end
return self
end
--- Appends a new HTML tag as a child.
function HtmlElement:tag(name)
local child = p.create(name)
child._parent = self
table.insert(self.children, child)
return child
end
--- Adds a CSS class (appends to the "class" attribute).
function HtmlElement:addClass(className)
-- nil etc = NOP
if type(className) ~= "string" then
return self
end
local existing = self.attrs["class"] or ""
local value
if existing ~= "" then
value = existing .. " " .. className
else
value = className
end
orderedTableAdd(self.attrs, "class", value)
return self
end
--- Appends a newline (for better readability in output).
function HtmlElement:newline()
table.insert(self.children, { type = "wikitext", value = "\n" })
return self
end
--- Returns the parent element (useful for chaining after :tag()).
function HtmlElement:done()
local res = self._parent or self
return res
end
--- Converts the element and its children to HTML.
function HtmlElement:__tostring()
local buf = {}
if self.nodeName ~= "-" then
table.insert(buf, "<" .. self.nodeName)
for _, k in ipairs(self.attrs._keys) do
local v = self.attrs[k]
table.insert(buf, " " .. k .. '="' .. escapeHtml(v) .. '"')
end
if next(self.styles._keys) then
table.insert(buf, ' style="' .. escapeHtml(cssToString(self.styles)) .. '"')
end
end
if #self.children == 0 then
if self.nodeName ~= "-" then
table.insert(buf, " />")
end
else
if self.nodeName ~= "-" then
table.insert(buf, ">")
end
for _, child in ipairs(self.children) do
if type(child) == "table" and getmetatable(child) == HtmlElement then
table.insert(buf, tostring(child))
elseif child.type == "wikitext" then
table.insert(buf, child.value)
end
end
if self.nodeName ~= "-" then
table.insert(buf, "</" .. self.nodeName .. ">")
end
end
return table.concat(buf)
end
--- Creates a new HTML element object.
-- @param tag Tag name (e.g. "div", "span", etc.)
-- @return A fluent HTML builder object.
function p.create(tag)
-- assert(type(tag) == "string" and tag ~= "", "Tag must be a non-empty string.")
if type(tag) ~= "string" or tag == "" then
tag = "-"
end
return setmetatable({
nodeName = tag,
attrs = orderedTableInit({}),
styles = orderedTableInit({}),
children = {}
}, HtmlElement)
end
return p